Skip to content

Commit 7eae25a

Browse files
authored
Merge branch 'master' into phg/nested-objects
2 parents 3c3c770 + a82573d commit 7eae25a

File tree

7 files changed

+127
-9
lines changed

7 files changed

+127
-9
lines changed

client-java/controller/src/main/java/org/evomaster/client/java/controller/problem/rpc/schema/params/NamedTypedValue.java

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package org.evomaster.client.java.controller.problem.rpc.schema.params;
22

33
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.DeserializationFeature;
45
import com.fasterxml.jackson.databind.ObjectMapper;
56
import org.evomaster.client.java.controller.api.dto.problem.rpc.ParamDto;
67
import org.evomaster.client.java.controller.problem.rpc.schema.types.AccessibleSchema;
78
import org.evomaster.client.java.controller.problem.rpc.schema.types.PrimitiveOrWrapperType;
89
import org.evomaster.client.java.controller.problem.rpc.schema.types.TypeSchema;
10+
import org.evomaster.client.java.utils.SimpleLogger;
911

1012
import java.util.ArrayList;
1113
import java.util.List;
@@ -21,6 +23,9 @@ public abstract class NamedTypedValue<T extends TypeSchema, V> {
2123

2224
protected final static ObjectMapper objectMaper = new ObjectMapper();
2325

26+
protected final static ObjectMapper mapperAllowUnkownFields = new ObjectMapper()
27+
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
28+
2429
/**
2530
* name of the instance, eg param name
2631
*/
@@ -225,7 +230,27 @@ else if (PrimitiveOrWrapperType.isPrimitiveOrTypes(json.getClass())){
225230
}
226231

227232
public Object parseValueWithJson(String json) throws JsonProcessingException {
228-
return objectMaper.readValue(json, getType().getClazz());
233+
try{
234+
return objectMaper.readValue(json, getType().getClazz());
235+
}catch (JsonProcessingException e) {
236+
/*
237+
In the seeded tests of the industrial case study, there are cases where
238+
unrecognized fields appear in the JSON input, such as:
239+
{
240+
"setType": true // Note: there is no corresponding 'setType' field in the target class
241+
}
242+
243+
To handle such cases while still capturing the error message,
244+
attempt to parse the JSON again with a configuration that allows unknown fields.
245+
*/
246+
if (e.getMessage().contains("Unrecognized field")) {
247+
SimpleLogger.recordErrorMessage(
248+
String.format("Try once to fix issues in json: %s", e.getMessage()));
249+
return mapperAllowUnkownFields.readValue(json, getType().getClazz());
250+
}
251+
throw e;
252+
}
253+
229254
}
230255

231256
/**

core/src/main/kotlin/org/evomaster/core/output/naming/rest/PathAmbiguitySolver.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,22 @@ class PathAmbiguitySolver : AmbiguitySolver {
1919
val lastPath = restAction.path
2020
val lastPathQualifier = getPath(lastPath.nameQualifier)
2121

22+
val candidateTokens = mutableListOf<String>()
23+
24+
if (!lastPath.isRoot()) {
25+
candidateTokens.add(getParentPath(lastPath))
26+
}
27+
candidateTokens.add(lastPathQualifier)
28+
29+
return if (canAddNameTokens(candidateTokens, remainingNameChars)) candidateTokens else listOf(lastPathQualifier)
30+
}
31+
32+
private fun getParentPath(lastPath: RestPath): String {
2233
var parentPath = lastPath.parentPath()
2334
if (lastPath.isLastElementAParameter()) {
2435
parentPath = parentPath.parentPath()
2536
}
26-
val candidateTokens = listOf(getParentPathQualifier(parentPath), lastPathQualifier)
27-
28-
return if (canAddNameTokens(candidateTokens, remainingNameChars)) candidateTokens else listOf(lastPathQualifier)
37+
return getParentPathQualifier(parentPath)
2938
}
3039

3140
/*

core/src/main/kotlin/org/evomaster/core/problem/rest/builder/CreateResourceUtils.kt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,22 @@ object CreateResourceUtils {
5050
POST /x
5151
POST /x/{id}/y
5252
GET /x/{id}/y
53-
not going to save the position of last POST, as same as target
53+
not need to save the position of last POST, as same as target
5454
5555
however, might also be in the case of:
5656
PUT /x/{id}
5757
GET /x/{id}
5858
*/
59-
before.saveCreatedResourceLocation = false
59+
/*
60+
removing the flag here was a mistake.
61+
even if after is not using the resource path, between "before" and "after"
62+
there could be other calls that need it, eg:
63+
64+
PUT /x/{a}
65+
PUT /x/{a}/y/{b}
66+
DELETE /x/{a}
67+
*/
68+
//before.saveCreatedResourceLocation = false
6069

6170
// the target (eg GET) needs to use the location of first POST, or more correctly
6271
// the same location used for the last POST (in case there is a deeper chain)

core/src/main/kotlin/org/evomaster/core/problem/rest/data/RestCallAction.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.evomaster.core.problem.rest.data
22

3+
import org.evomaster.core.Lazy
34
import org.evomaster.core.problem.httpws.HttpWsAction
45
import org.evomaster.core.problem.httpws.auth.HttpWsAuthenticationInfo
56
import org.evomaster.core.problem.httpws.auth.HttpWsNoAuth
@@ -71,7 +72,7 @@ class RestCallAction(
7172
*
7273
* TODO check if it could be used to handle issue in BackwardLinkReference
7374
*/
74-
private var weakReference: RestCallAction? = null
75+
private var weakReference: RestCallAction? = null
7576
) : HttpWsAction(auth, isCleanUp, parameters) {
7677

7778
companion object{
@@ -367,6 +368,8 @@ class RestCallAction(
367368
return true // nothing to do
368369
}
369370
if(weakReference!!.isMounted()) {
371+
//make sure both actions are in the same individual
372+
Lazy.assert { this.getRoot() == weakReference!!.getRoot() }
370373
usePreviousLocationId = weakReference!!.creationLocationId()
371374
weakReference = null
372375
return true
@@ -409,4 +412,22 @@ class RestCallAction(
409412

410413
return true
411414
}
415+
416+
/**
417+
* a copy() on an unresolved action with a weakref will crash.
418+
* this is as intended, as weakref would be meaningless when action mounted in a new individual.
419+
* however, there are cases in which we need to "duplicate" an action in an individual.
420+
* in those cases, we must keep the weakref
421+
*/
422+
fun copyKeepingSameWeakRef() : RestCallAction{
423+
if(weakReference == null){
424+
return copy() as RestCallAction
425+
}
426+
val wr = weakReference
427+
weakReference = null
428+
val copy = this.copy() as RestCallAction
429+
copy.weakReference = wr
430+
this.weakReference = wr
431+
return copy
432+
}
412433
}

core/src/main/kotlin/org/evomaster/core/problem/rest/service/RestIndividualBuilder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ class RestIndividualBuilder {
145145
"${template.path} vs ${target.path}")
146146
}
147147

148-
val res = template.copy() as RestCallAction
148+
val res = template.copyKeepingSameWeakRef()
149149

150150
res.resetLocalIdRecursively()
151151

@@ -180,7 +180,7 @@ class RestIndividualBuilder {
180180
"${previous.path} vs ${template.path}")
181181
}
182182

183-
val res = template.copy() as RestCallAction
183+
val res = template.copyKeepingSameWeakRef()
184184

185185
res.resetLocalIdRecursively()
186186

core/src/test/kotlin/org/evomaster/core/output/naming/TestCaseDisambiguationTest.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,35 @@ class TestCaseDisambiguationTest {
288288
assertEquals("test_1_getOnLanguagesWithQueryParamEmptyNameReturnsEmpty", testCases[1].name)
289289
}
290290

291+
@Test
292+
fun noDisambiguationWhenMoreThanOneIndividualAtRootLevel() {
293+
val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/"))
294+
val emptyStringIndividual = getEvaluatedIndividualWith(getRestCallAction("/"))
295+
296+
val solution = Solution(mutableListOf(simpleIndividual, emptyStringIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList())
297+
298+
val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME, MAX_NAME_LENGTH)
299+
val testCases = namingStrategy.getTestCases()
300+
301+
assertEquals(2, testCases.size)
302+
assertEquals("test_0_getOnRootReturnsEmpty", testCases[0].name)
303+
assertEquals("test_1_getOnRootReturnsEmpty", testCases[1].name)
304+
}
305+
306+
@Test
307+
fun onlyQueryParamsDisambiguationWhenBothInRootPath() {
308+
val simpleIndividual = getEvaluatedIndividualWith(getRestCallAction("/"))
309+
val emptyQPIndividual = getEvaluatedIndividualWith(getRestCallAction("/", parameters = mutableListOf(getStringQueryParam("name", false))))
310+
ensureGeneValue(emptyQPIndividual, "name", "")
311+
312+
val solution = Solution(mutableListOf(simpleIndividual, emptyQPIndividual), "suitePrefix", "suiteSuffix", Termination.NONE, emptyList(), emptyList())
313+
314+
val namingStrategy = RestActionTestCaseNamingStrategy(solution, javaFormatter, QUERY_PARAMS_IN_NAME, MAX_NAME_LENGTH)
315+
val testCases = namingStrategy.getTestCases()
316+
317+
assertEquals(2, testCases.size)
318+
assertEquals("test_0_getOnRootReturnsEmpty", testCases[0].name)
319+
assertEquals("test_1_getOnRootWithQueryParamEmptyNameReturnsEmpty", testCases[1].name)
320+
}
321+
291322
}

e2e-tests/spring-rpc/spring-rpc-thrift/src/test/java/com/foo/rpc/examples/spring/fakemockobject/FakeMockObjectController.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,30 @@ public List<SeededRPCTestDto> seedRPCTests() {
181181
descriptiveInfo = "a scheduled task for invoking executeFlag";
182182
}}
183183
);
184+
}},
185+
new SeededRPCTestDto() {{
186+
testName = "test_7";
187+
rpcFunctions = Arrays.asList(
188+
new SeededRPCActionDto() {{
189+
interfaceName = FakeMockObjectService.Iface.class.getName();
190+
functionName = "getFooFromExternalService";
191+
inputParams = Arrays.asList("0");
192+
inputParamTypes = Arrays.asList(int.class.getName());
193+
mockRPCExternalServiceDtos = Arrays.asList(
194+
new MockRPCExternalServiceDto() {{
195+
appKey = "fake.app";
196+
interfaceFullName = "com.foo.rpc.examples.spring.fakemockobject.external.fake.api.GetApiData";
197+
functionName = "one";
198+
responses = Arrays.asList("{\"exName\":\"abc\",\"exId\":0,\"exInfo\":[\"2025-05-28\"],\"unknownField\":\"unknown\"}");
199+
responseTypes = Arrays.asList(
200+
"com.foo.rpc.examples.spring.fakemockobject.external.fake.api.ExApiDto"
201+
);
202+
}}
203+
);
204+
}}
205+
);
184206
}}
207+
185208
);
186209
}
187210

0 commit comments

Comments
 (0)