Skip to content

Commit 1beceb0

Browse files
committed
add tests & update docs/options.md
1 parent 9db0972 commit 1beceb0

2 files changed

Lines changed: 118 additions & 1 deletion

File tree

core/src/test/kotlin/org/evomaster/core/search/algorithms/LIPSAlgorithmTest.kt

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import org.evomaster.core.search.algorithms.onemax.OneMaxIndividual
1212
import org.evomaster.core.search.algorithms.onemax.OneMaxModule
1313
import org.evomaster.core.search.algorithms.onemax.OneMaxSampler
1414
import org.evomaster.core.search.service.ExecutionPhaseController
15+
import org.evomaster.core.search.algorithms.observer.GARecorder
1516
import org.junit.jupiter.api.Assertions.assertEquals
1617
import org.junit.jupiter.api.Assertions.assertTrue
1718
import org.junit.jupiter.api.BeforeEach
@@ -53,6 +54,122 @@ class LIPSAlgorithmTest {
5354
)
5455
}
5556

57+
// Edge Case: CrossoverProbability=0 on LIPS
58+
@Test
59+
fun testNoCrossoverWhenProbabilityZero_LIPS() {
60+
TestUtils.handleFlaky {
61+
val ga = injector.getInstance(
62+
Key.get(object : TypeLiteral<LIPSAlgorithm<OneMaxIndividual>>() {})
63+
)
64+
65+
val rec = GARecorder<OneMaxIndividual>()
66+
ga.addObserver(rec)
67+
68+
val config = injector.getInstance(EMConfig::class.java)
69+
config.gaSolutionSource = EMConfig.GASolutionSource.POPULATION
70+
config.maxEvaluations = 100_000
71+
config.stoppingCriterion = EMConfig.StoppingCriterion.ACTION_EVALUATIONS
72+
config.populationSize = 5
73+
config.xoverProbability = 0.0 // no crossover
74+
config.fixedRateMutation = 1.0 // force mutation
75+
76+
ga.setupBeforeSearch()
77+
ga.searchOnce()
78+
79+
val nextPop = ga.getViewOfPopulation()
80+
assertEquals(config.populationSize, nextPop.size)
81+
assertEquals(0, rec.xoCalls.size)
82+
// at least the offspring should have been mutated
83+
assertTrue(rec.mutated.size >= 1)
84+
}
85+
}
86+
87+
// Edge Case: MutationProbability=0 on LIPS
88+
@Test
89+
fun testNoMutationWhenProbabilityZero_LIPS() {
90+
TestUtils.handleFlaky {
91+
val ga = injector.getInstance(
92+
Key.get(object : TypeLiteral<LIPSAlgorithm<OneMaxIndividual>>() {})
93+
)
94+
95+
val rec = GARecorder<OneMaxIndividual>()
96+
ga.addObserver(rec)
97+
98+
val config = injector.getInstance(EMConfig::class.java)
99+
config.gaSolutionSource = EMConfig.GASolutionSource.POPULATION
100+
config.maxEvaluations = 100_000
101+
config.stoppingCriterion = EMConfig.StoppingCriterion.ACTION_EVALUATIONS
102+
config.populationSize = 5
103+
config.xoverProbability = 1.0 // allow crossover
104+
config.fixedRateMutation = 0.0 // disable mutation
105+
106+
ga.setupBeforeSearch()
107+
ga.searchOnce()
108+
109+
val nextPop = ga.getViewOfPopulation()
110+
assertEquals(config.populationSize, nextPop.size)
111+
assertEquals(0, rec.mutated.size)
112+
}
113+
}
114+
115+
// Next generation is elites + best from offspring
116+
@Test
117+
fun testNextGenerationIsElitesPlusBestFromOffspring_LIPS() {
118+
TestUtils.handleFlaky {
119+
val ga = injector.getInstance(
120+
Key.get(object : TypeLiteral<LIPSAlgorithm<OneMaxIndividual>>() {})
121+
)
122+
123+
val rec = GARecorder<OneMaxIndividual>()
124+
ga.addObserver(rec)
125+
126+
val config = injector.getInstance(EMConfig::class.java)
127+
config.populationSize = 5
128+
config.elitesCount = 1
129+
config.xoverProbability = 0.0
130+
config.fixedRateMutation = 1.0
131+
config.gaSolutionSource = EMConfig.GASolutionSource.POPULATION
132+
config.maxEvaluations = 100_000
133+
config.stoppingCriterion = EMConfig.StoppingCriterion.ACTION_EVALUATIONS
134+
135+
ga.setupBeforeSearch()
136+
137+
val initPop = ga.getViewOfPopulation()
138+
val eliteCount = config.elitesCount
139+
val mu = config.populationSize
140+
141+
// Determine expected elites from current population
142+
val eliteScores = initPop
143+
.map { ga.score(it) }
144+
.sortedDescending()
145+
.take(eliteCount)
146+
147+
ga.searchOnce()
148+
149+
val finalPop = ga.getViewOfPopulation()
150+
151+
// population size remains the same
152+
assertEquals(mu, finalPop.size)
153+
154+
// offspring considered as those mutated
155+
val offspringScores = rec.mutated
156+
.map { ga.score(it) }
157+
.sortedDescending()
158+
159+
val neededFromOffspring = mu - eliteCount
160+
val expectedScores = (eliteScores + offspringScores.take(neededFromOffspring))
161+
.sortedDescending()
162+
163+
val finalScores = finalPop
164+
.map { ga.score(it) }
165+
.sortedDescending()
166+
167+
assertEquals(expectedScores, finalScores)
168+
// All non-elite positions were filled by mutated offspring
169+
assertEquals(neededFromOffspring, rec.mutated.size)
170+
}
171+
}
172+
56173
}
57174

58175

docs/options.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ There are 3 types of options:
6868
|`addPreDefinedTests`| __Boolean__. Add predefined tests at the end of the search. An example is a test to fetch the schema of RESTful APIs. *Default value*: `true`.|
6969
|`addTestComments`| __Boolean__. Add summary comments on each test. *Default value*: `true`.|
7070
|`advancedBlackBoxCoverage`| __Boolean__. Apply more advanced coverage criteria for black-box testing. This can result in larger generated test suites. *Default value*: `true`.|
71-
|`algorithm`| __Enum__. The algorithm used to generate test cases. The default depends on whether black-box or white-box testing is done. *Valid values*: `DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, StandardGA, MonotonicGA, SteadyStateGA`. *Default value*: `DEFAULT`.|
71+
|`algorithm`| __Enum__. The algorithm used to generate test cases. The default depends on whether black-box or white-box testing is done. *Valid values*: `DEFAULT, SMARTS, MIO, RANDOM, WTS, MOSA, RW, StandardGA, MonotonicGA, SteadyStateGA, LIPS`. *Default value*: `DEFAULT`.|
7272
|`allowInvalidData`| __Boolean__. When generating data, allow in some cases to use invalid values on purpose. *Default value*: `true`.|
7373
|`appendToStatisticsFile`| __Boolean__. Whether should add to an existing statistics file, instead of replacing it. *Default value*: `false`.|
7474
|`archiveAfterMutationFile`| __String__. Specify a path to save archive after each mutation during search, only useful for debugging. *DEBUG option*. *Default value*: `archive.csv`.|

0 commit comments

Comments
 (0)