diff --git a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/TaintedArrayGene.kt b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/TaintedArrayGene.kt index 65dc2f4efb..e17de33d3d 100644 --- a/core/src/main/kotlin/org/evomaster/core/search/gene/collection/TaintedArrayGene.kt +++ b/core/src/main/kotlin/org/evomaster/core/search/gene/collection/TaintedArrayGene.kt @@ -129,8 +129,20 @@ class TaintedArrayGene( return false } - return this.arrayGene?.unsafeCopyValueFrom(other.arrayGene!!) - ?: true + val otherArray = other.arrayGene + if(otherArray == null){ + // other is unresolved: drop the array, if any + killAllChildren() + } else if(arrayGene == null){ + // other is resolved but this is not: resolve this with a copy of its array + addChild(otherArray.copy()) + } else if(!arrayGene!!.unsafeCopyValueFrom(otherArray)){ + return false + } + + taintedValue = other.taintedValue + isActive = other.isActive + return true } override fun getPossiblyTaintedValue(): String { diff --git a/core/src/test/kotlin/org/evomaster/core/search/gene/collection/TaintedArrayGeneTest.kt b/core/src/test/kotlin/org/evomaster/core/search/gene/collection/TaintedArrayGeneTest.kt new file mode 100644 index 0000000000..85cf15da64 --- /dev/null +++ b/core/src/test/kotlin/org/evomaster/core/search/gene/collection/TaintedArrayGeneTest.kt @@ -0,0 +1,87 @@ +package org.evomaster.core.search.gene.collection + +import org.evomaster.client.java.instrumentation.shared.TaintInputName +import org.evomaster.core.search.gene.Gene +import org.evomaster.core.search.gene.numeric.IntegerGene +import org.evomaster.core.search.gene.wrapper.ChoiceGene +import org.evomaster.core.search.service.Randomness +import org.junit.jupiter.api.Assertions.* +import org.junit.jupiter.api.Test + +class TaintedArrayGeneTest { + + private val randomness = Randomness() + + private fun resolved(taintId: Int = 1): TaintedArrayGene = + TaintedArrayGene("resolved", TaintInputName.getTaintName(taintId), true, ArrayGene("array", IntegerGene("element"))) + + private fun unresolved(taintId: Int = 2): TaintedArrayGene = + TaintedArrayGene("unresolved", TaintInputName.getTaintName(taintId)) + + @Test + fun testCopyBetweenResolvedGenes() { + val target = resolved().apply { doInitialize(randomness) } + val source = resolved().apply { doInitialize(randomness) } + source.randomize(randomness, true) + + assertTrue(target.copyValueFrom(source)) + assertTrue(target.containsSameValueAs(source)) + } + + /** + * This used to report a successful copy, even if the tainted value of the source was not copied. + */ + @Test + fun testCopyBetweenUnresolvedGenes() { + val target = unresolved(2).apply { doInitialize(randomness) } + val source = unresolved(3).apply { doInitialize(randomness) } + + assertTrue(target.copyValueFrom(source)) + assertEquals(source.taintedValue, target.taintedValue) + assertTrue(target.containsSameValueAs(source)) + } + + /** + * This used to throw a NullPointerException, as the missing array of the source was dereferenced. + */ + @Test + fun testCopyFromUnresolvedIntoResolved() { + val target = resolved().apply { doInitialize(randomness) } + val source = unresolved().apply { doInitialize(randomness) } + + assertTrue(target.copyValueFrom(source)) + assertFalse(target.isResolved()) + assertTrue(target.containsSameValueAs(source)) + } + + /** + * This used to report a successful copy, even if the target was left without the array of the source. + */ + @Test + fun testCopyFromResolvedIntoUnresolved() { + val target = unresolved().apply { doInitialize(randomness) } + val source = resolved().apply { doInitialize(randomness) } + + assertTrue(target.copyValueFrom(source)) + assertTrue(target.isResolved()) + assertTrue(target.containsSameValueAs(source)) + } + + /** + * A choice copies into its first alternative of the same class as the source, which here is in a + * different state than the source. This used to throw a NullPointerException. + */ + @Test + fun testChoiceCopiesIntoAnAlternativeInADifferentState() { + val choice = ChoiceGene("choice", listOf(resolved(), unresolved())) + choice.doInitialize(randomness) + + val target = choice.copy() as ChoiceGene<*> + target.selectActiveGene(0) + val source = choice.copy() as ChoiceGene<*> + source.selectActiveGene(1) + + assertTrue(target.copyValueFrom(source)) + assertTrue(target.containsSameValueAs(source)) + } +} \ No newline at end of file