Skip to content

Commit 6fca9df

Browse files
Fixes for resource schema
1 parent 9745e30 commit 6fca9df

2 files changed

Lines changed: 44 additions & 49 deletions

File tree

core/src/main/kotlin/org/evomaster/core/search/gene/jsonpatch/JsonPatchDocumentGene.kt

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,18 @@ import org.evomaster.core.search.service.mutator.genemutation.SubsetGeneMutation
1818
* a ChoiceGene that selects among the six standard operations:
1919
* remove, move, copy, add, replace, test.
2020
*
21-
* [resourceSchema] is stored for future schema-aware path extraction but is not yet analysed.
22-
* Until that step is wired in, paths come from [JsonPatchDocumentGeneBuilder.DEFAULT_PATHS].
21+
* [resourceSchema] is used only during construction: [JsonPatchDocumentGeneBuilder] extracts its
22+
* field types to build typed path-value pairs for each operation. It is not retained afterwards.
2323
*
2424
* The private constructor accepts a pre-built operations array (used by [copyContent]).
2525
*/
2626
class JsonPatchDocumentGene private constructor(
2727
name: String,
28-
val resourceSchema: Gene?,
2928
operationsArr: ArrayGene<ChoiceGene<JsonPatchOperationGene>>
3029
) : CompositeFixedGene(name, listOf(operationsArr)) {
3130

3231
constructor(name: String, resourceSchema: Gene? = null, randomness: Randomness? = null)
33-
: this(name, resourceSchema, JsonPatchDocumentGeneBuilder.buildOperationsArray(resourceSchema, randomness = randomness))
32+
: this(name, JsonPatchDocumentGeneBuilder.buildOperationsArray(resourceSchema, randomness = randomness))
3433

3534
companion object {
3635
val MIN_SIZE get() = JsonPatchDocumentGeneBuilder.MIN_SIZE
@@ -55,28 +54,17 @@ class JsonPatchDocumentGene private constructor(
5554
extraCheck: Boolean
5655
): String {
5756
val inner = operationsArray.getValueAsPrintableString(previousGenes, mode, targetFormat, extraCheck)
57+
// There is no RFC defining an XML representation of JSON Patch (RFC 6902 is JSON-only).
58+
// We use <patch> as the root tag name as a reasonable convention.
5859
return if (mode == GeneUtils.EscapeMode.XML) "<patch>$inner</patch>" else inner
5960
}
6061

6162
override fun copyContent(): Gene =
62-
JsonPatchDocumentGene(
63-
name,
64-
resourceSchema?.copy(),
65-
operationsArray.copy() as ArrayGene<ChoiceGene<JsonPatchOperationGene>>
66-
)
63+
JsonPatchDocumentGene(name, operationsArray.copy() as ArrayGene<ChoiceGene<JsonPatchOperationGene>>)
6764

6865
override fun containsSameValueAs(other: Gene): Boolean {
6966
if (other !is JsonPatchDocumentGene) throw IllegalArgumentException("Invalid gene type ${other.javaClass}")
70-
val schemaMatch = when {
71-
resourceSchema == null && other.resourceSchema == null -> true
72-
resourceSchema == null || other.resourceSchema == null -> false
73-
else -> try {
74-
resourceSchema.containsSameValueAs(other.resourceSchema)
75-
} catch (_: IllegalArgumentException) {
76-
false
77-
}
78-
}
79-
return schemaMatch && operationsArray.containsSameValueAs(other.operationsArray)
67+
return operationsArray.containsSameValueAs(other.operationsArray)
8068
}
8169

8270
override fun unsafeCopyValueFrom(other: Gene): Boolean {

core/src/test/kotlin/org/evomaster/core/search/gene/jsonpatch/JsonPatchDocumentGeneTest.kt

Lines changed: 37 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,16 @@ class JsonPatchDocumentGeneTest {
4343
}
4444

4545
@Test
46-
fun testCopyPreservesNonNullResourceSchema() {
47-
val original = JsonPatchDocumentGene("patch", StringGene("schema"))
48-
val copy = original.copy() as JsonPatchDocumentGene
49-
assertNotNull(copy.resourceSchema)
46+
fun testSchemaFieldsAreUsedAsPaths() {
47+
val schema = ObjectGene("body", listOf(StringGene("name"), IntegerGene("age")))
48+
val d = JsonPatchDocumentGene("patch", schema)
49+
50+
val array = d.getViewOfChildren()[0] as org.evomaster.core.search.gene.collection.ArrayGene<*>
51+
val removeOp = array.template.getViewOfChildren()[0] as JsonPatchPathOnlyGene
52+
val paths = removeOp.pathGene.values.map { it.toString() }
53+
54+
assertTrue(paths.contains("/name"), "Expected /name from schema, got: $paths")
55+
assertTrue(paths.contains("/age"), "Expected /age from schema, got: $paths")
5056
}
5157

5258
// --- operations property ---
@@ -155,13 +161,31 @@ class JsonPatchDocumentGeneTest {
155161

156162
// --- XML serialization ---
157163

164+
@Test
165+
fun testRemoveOperationJsonFormat() {
166+
val op = JsonPatchPathOnlyGene(JsonPatchOperationGene.OP_REMOVE, JsonPatchOperationGene.OP_REMOVE, EnumGene("path", listOf("/x")))
167+
val result = op.getValueAsPrintableString()
168+
assertEquals("{\"op\":\"remove\",\"path\":\"/x\"}", result)
169+
}
170+
158171
@Test
159172
fun testRemoveOperationXmlFormat() {
160173
val op = JsonPatchPathOnlyGene(JsonPatchOperationGene.OP_REMOVE, JsonPatchOperationGene.OP_REMOVE, EnumGene("path", listOf("/x")))
161174
val result = op.getValueAsPrintableString(mode = GeneUtils.EscapeMode.XML)
162175
assertEquals("<operation><op>remove</op><path>/x</path></operation>", result)
163176
}
164177

178+
@Test
179+
fun testMoveOperationJsonFormat() {
180+
val op = JsonPatchFromPathGene(
181+
JsonPatchOperationGene.OP_MOVE, JsonPatchOperationGene.OP_MOVE,
182+
fromGene = EnumGene("from", listOf("/a")),
183+
pathGene = EnumGene("path", listOf("/b"))
184+
)
185+
val result = op.getValueAsPrintableString()
186+
assertEquals("{\"op\":\"move\",\"from\":\"/a\",\"path\":\"/b\"}", result)
187+
}
188+
165189
@Test
166190
fun testMoveOperationXmlFormat() {
167191
val op = JsonPatchFromPathGene(
@@ -220,25 +244,7 @@ class JsonPatchDocumentGeneTest {
220244
}
221245

222246
@Test
223-
fun testContainsSameValueAsTrueWhenBothSchemasAreNull() {
224-
val d1 = doc()
225-
val d2 = d1.copy() as JsonPatchDocumentGene
226-
assertNull(d1.resourceSchema)
227-
assertNull(d2.resourceSchema)
228-
assertTrue(d1.containsSameValueAs(d2))
229-
}
230-
231-
@Test
232-
fun testContainsSameValueAsFalseWhenOneSchemaIsNull() {
233-
val withSchema = JsonPatchDocumentGene("patch", ObjectGene("body", listOf(StringGene("name"))))
234-
withSchema.doInitialize(Randomness().apply { updateSeed(42) })
235-
val withoutSchema = doc()
236-
assertFalse(withSchema.containsSameValueAs(withoutSchema))
237-
assertFalse(withoutSchema.containsSameValueAs(withSchema))
238-
}
239-
240-
@Test
241-
fun testContainsSameValueAsTrueForCopiesWithSchema() {
247+
fun testContainsSameValueAsTrueForCopiesBuiltWithSchema() {
242248
val schema = ObjectGene("body", listOf(StringGene("name"), IntegerGene("age")))
243249
val d1 = JsonPatchDocumentGene("patch", schema)
244250
d1.doInitialize(Randomness().apply { updateSeed(1) })
@@ -247,13 +253,14 @@ class JsonPatchDocumentGeneTest {
247253
}
248254

249255
@Test
250-
fun testContainsSameValueAsFalseForDifferentSchemaTypes() {
251-
val schemaA = ObjectGene("body", listOf(StringGene("name")))
252-
val schemaB = ObjectGene("body", listOf(IntegerGene("count")))
253-
val d1 = JsonPatchDocumentGene("patch", schemaA)
254-
d1.doInitialize(Randomness().apply { updateSeed(1) })
255-
val d2 = JsonPatchDocumentGene("patch", schemaB)
256-
d2.doInitialize(Randomness().apply { updateSeed(1) })
256+
fun testContainsSameValueAsFalseAfterRandomize() {
257+
val d1 = doc(seed = 1L)
258+
val d2 = doc(seed = 2L)
259+
val rand2 = Randomness().apply { updateSeed(99) }
260+
var attempts = 0
261+
while (d1.containsSameValueAs(d2) && attempts++ < 20) {
262+
d2.randomize(rand2, tryToForceNewValue = true)
263+
}
257264
assertFalse(d1.containsSameValueAs(d2))
258265
}
259266

0 commit comments

Comments
 (0)