in.specmatic.core.pattern.CombinationSpec.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of specmatic-core Show documentation
Show all versions of specmatic-core Show documentation
Turn your contracts into executable specifications. Contract Driven Development - Collaboratively Design & Independently Deploy MicroServices & MicroFrontends.
Deprecation Notice for group ID "in.specmatic"
******************************************************************************************************
Updates for "specmatic-core" will no longer be available under the deprecated group ID "in.specmatic".
Please update your dependencies to use the new group ID "io.specmatic".
******************************************************************************************************
package `in`.specmatic.core.pattern
/**
* Provides utility access to all combinations of multiple sets of candidate values. Represented as the cartesian
* product of all sets where each combination is represented as a numerical index from 0 to (MAX_COMBOS - 1).
*
* Supports sequential iteration over all combinations, which is used to test as many combinations as possible.
*
* Supports translating a combination of candidate values to an index, which is used to first test priority combinations
* (and to remember these when later iterating remaining combinations to not double-include these priority combinations)
*
* See this
* stackoverflow article accepted answer. A notable difference is that our representation is reversed such that
* a sequential iteration produces all combinations with the first candidate value of the first set before producing
* all combinations using the second candidate value of the first set, and so on for each subsequent value and set.
*
* Note: This code now returns a sequence instead of a list, and no longer calculates up-front the max size of any
* sequence. There are significant changes to how it works. However, it continues to prioritise combinations in the
* same way as before.
*/
class CombinationSpec(
keyToCandidatesOrig: Map>>,
private val maxCombinations: Int
) {
companion object {
fun from(keyToCandidates: Map>, maxCombinations: Int): CombinationSpec {
return CombinationSpec(keyToCandidates.mapValues { it.value.map { HasValue(it) } }, maxCombinations)
}
}
val keyToCandidates: Map>> = keyToCandidatesOrig
init {
if (maxCombinations < 1) throw IllegalArgumentException("maxCombinations must be > 0 and <= ${Int.MAX_VALUE}")
}
val selectedCombinations: Sequence>> = toSelectedCombinations(keyToCandidates, maxCombinations)
fun toSelectedCombinations(rawPatternCollection: Map>>, maxCombinations: Int): Sequence>> {
val patternCollection = rawPatternCollection.filterValues { it.any() }
if (patternCollection.isEmpty())
return emptySequence()
val cachedValues = patternCollection.mapValues { mutableListOf>() }
val prioritisedGenerations = mutableSetOf>>()
val ranOut = cachedValues.mapValues { false }.toMutableMap()
val iterators = patternCollection.mapValues {
it.value.iterator()
}.filter {
it.value.hasNext()
}
return sequence {
var ctr = 0
while (true) {
val nextValue: Map> = iterators.mapValues { (key, iterator) ->
val nextValueFromIterator = if (iterator.hasNext()) {
val value = iterator.next()
cachedValues.getValue(key).add(value)
value
} else {
ranOut[key] = true
val cachedValuesForKey = cachedValues.getValue(key)
val value = cachedValuesForKey.get(ctr % cachedValuesForKey.size)
value
}
nextValueFromIterator
}
val _nextValue: ReturnValue