Skip to content

Commit cdf1849

Browse files
authored
Merge pull request #230 from tegonal/improve/rename-combine
rename combine to cartesian for ordered and zip for arb
2 parents ddba5a7 + ec55445 commit cdf1849

37 files changed

+1653
-1415
lines changed

README.md

Lines changed: 121 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ version: [README of v2.0.0-RC-1](https://github.com/tegonal/minimalist/tree/v2.0
4040
- [Your first parameterized Test](#your-first-parameterized-test)
4141
- [Ordered and arbitrary arguments generators](#ordered-and-arbitrary-arguments-generators)
4242
- [Combinators](#combinators)
43-
- [combine](#combine)
43+
- [generic combine](#generic-combine)
44+
- [ordered.cartesian](#ordered-cartesian)
45+
- [arb.zip](#arb-zip)
4446
- [combineDependent](#combinedependent)
4547
- [transform](#transform)
4648
- [map](#map)
@@ -148,11 +150,11 @@ Raw values are turned into a `List` and then passed to `ordered.fromList`. The n
148150
149151
Minimalist provides two entry points to create an `ArgsGenenerator`: `ordered` and `arb`.
150152
151-
`ordered` can be used to define an ordered (not to be confused with sorted) list of finite values where the
152-
corresponding
153-
`OrderedArgsGenerator` generates a sequence which repeats them indefinitely.
154-
For instance, if you use `ordered.of('a', 'b')` as provider, then it results in two runs where in the first run you
155-
will get `'a'` and in the second `'b'` or you will get `'b'` in the first run and `'a'` in the second.
153+
`ordered` can be used to define an ordered (not to be confused with sorted) list of finite (`OrderedArgsGenerator.size`)
154+
values where the corresponding `OrderedArgsGenerator` generates a sequence which repeats them indefinitely.
155+
For instance, if you use `ordered.of('a', 'b')` as provider, then `OrderedArgsGenerator.size = 2` and correspondingly
156+
it results in two runs. You either will get `'a'` in the first run and in the second `'b'` or
157+
you will get `'b'` in the first run and `'a'` in the second.
156158
That is because the resulting sequence repeats indefinitely `'a', 'b', 'a', 'b', ... `
157159
and it depends on a randomly chosen seed what offset is taken.
158160
@@ -184,8 +186,14 @@ ordered.longFromTo(1, 5)
184186

185187
The "counterpart" of `ordered` is `arb` that allows to create `ArbArgsGenerator`s which per definition generate
186188
an infinite sequence of values where it is basically not known if they follow some order or not.
187-
The default implementations almost all are based on `Random`.
188-
Following a few examples as well (import and enum definition omitted, as it is the same as above):
189+
The default implementations are almost all based on `Random`.
190+
The number of runs of such a provider is in theory infinite as well (`ArbArgsGenerator.size` doesn't exist) but gets
191+
limited by the [profile](#profiles) the test falls into, the environment where the test runs and what configuration
192+
was set up for this combination. Also `OrderedArgsGenerator` are limited by profile/env but introduce an own limit in
193+
addition.
194+
195+
Following a few examples for `ArbArgsGenerator` as well (import and enum definition omitted, as it is the same as
196+
above):
189197

190198
<code-arb-1>
191199

@@ -250,17 +258,15 @@ contain predefined `ArgsSource` providers and one ArgsSourceProvider which exten
250258
from Minimalist's PredefinedArgsSourceProviders as well.
251259

252260
Back to the example, in contrast to the [first parameterized test example](#your-first-parameterized-test) where we used
253-
raw values (which are
254-
turned into a `List` and then passed to `ordered.fromList`), we now want to be sure the test
261+
raw values (which are turned into a `List` and then passed to `ordered.fromList`), we now want to be sure the test
255262
works for all positive integers and not only `1..20`. Since there are many positive integers, we use `arb` and no
256263
longer `ordered`.
257264

258265
Note, that even if we defined a provider with `arb.intFromTo(1, 20)` the runtime behaviour differs from `ordered`.
259266
Where `OrderedArgsGenerator`s generate a window of all possible values (i.e. still ordered),
260267
an `ArbArgsGenerator` generates arbitrary/random values (possibly the same value multiple times).
261268
To illustrate it better, following an example where `maxArgs=5` (more on `maxArgs` in
262-
the [Configuration](#configuration)
263-
section):
269+
the [Configuration](#configuration) section):
264270

265271
```kotlin
266272
ordered.of(1, 2, 3) // results in 3 runs: 1, 2, 3 or 2, 3, 1 or 3, 1, 2
@@ -274,12 +280,12 @@ suited.
274280
## Combinators
275281

276282
Minimalist provides different combinators to produce new `ArgsGenerator`.
277-
The most common combinator is to combine multiple `ArgsGenerator`s, a reason why it got some extra care.
278283

279-
### Combine
284+
### Generic Combine
280285

281-
Although Minimalist provides a `combine` function (more on that later on), the cleanest way to define that two
282-
`ArgsGenerator`s shall be combined, is to use `Tuple` (from ch.tutteli.kbox) which exists up to `Tuple9`:
286+
The most frequently used combinator is probably a way to combine multiple `ArgsGenerator`s in some way. A reason why
287+
we added a bit of magic to Minimalist. The idiomatic way to define that we want to combine multiple generators is
288+
to use `Tuple` (from ch.tutteli.kbox) which exists up to `Tuple9`:
283289

284290
<code-combine-tuple>
285291

@@ -309,28 +315,27 @@ class CombineTupleTest : PredefinedArgsProviders {
309315

310316
</code-combine-tuple>
311317

312-
As you can see in the example, you can also combine `ordered` and `arb` (resulting in a `SemiOrderedArgsGenerator`).
313-
You only need to make sure that your first `ArgsGenerator` in the tuple is a `SemiOrderedArgsGenerator`
314-
(`OrderedArgsGenerator` is a subtype of `SemiOrderedArgsGenerator`). If your first `ArgsGenerator` in the tuple is
315-
an `ArbArgsGenerator` then all generators which follow need to be an `ArbArgsGenerator` as well (otherwise it will
316-
fail at runtime).
317-
318-
Maybe you are asking yourself how many runs result out of the above definition. `SemiOrderedArgsGenerator`s have a
319-
property `size` and as long as no `maxArgs` definition restricts it, it will result in that many runs.
318+
Combining two `OrderedArgsGenerator`s A and B (or `SemiOrderedArgsGenerator`s) results in an `OrderedArgsGenerator`
319+
representing their cartesian product and the size correspondingly `A.size * B.size`. I.e. such combinations can
320+
grow quickly, but Minimalist has you covered in therms that this is just a definition (nothing generated yet) and
321+
you still execute only a window of those values in a fast and efficient way.
322+
On the other hand, combining two `ArbArgsGenerator` means zipping them and results in another `ArbArgsGenerator`.
323+
324+
Combining an `OrderedArgsGenerators` and an `ArbArgsGenerator` works as well (as shown in the example) and uses
325+
again zip behaviour, where the result is no longer an `OrderedArgsGenerators` but a `SemiOrderedArgsGenerators` (which
326+
still has a `size` property). You only need to make sure that your first `ArgsGenerator` in the tuple is a
327+
`SemiOrderedArgsGenerator` (`OrderedArgsGenerator` is a subtype of `SemiOrderedArgsGenerator`).
328+
If your first `ArgsGenerator` in the tuple is an `ArbArgsGenerator` then all generators which follow need to be an
329+
`ArbArgsGenerator` as well (otherwise it will fail at runtime).
330+
331+
Maybe you are asking yourself how many runs result out of the above definition. As long as no `maxArgs` definition
332+
restricts it, it will result in `SemiOrderedArgsGenerator.size` runs.
320333
So for `ordered.intFromTo(15, 30)`, we will get 30 - 15 + 1 = 16 runs at max (+1 since bounds are inclusive for
321334
`intFromTo`). Which means we combine 16 arbitrary names with the defined ages.
322335

323-
If you combine two `OrderedArgsGenerator`s A and B (or `SemiOrderedArgsGenerator`s) the resulting `OrderedArgsGenerator`
324-
will be their cartesian product and the size correspondingly `A.size * B.size`. I.e. such combinations can grow quickly,
325-
but Minimalist has you covered in therms that this is just a definition (nothing generated yet) and you still execute
326-
only a window of those values in a fast and efficient way.
327-
328-
If you combine two `ArbArgsGenerator`s then you will get back an `ArbArgsGenerator` which semantically
329-
[`zip`s](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.collections/zip.html) the generates values.
330-
331336
What if you want to combine more than 9 `ArgsGenerators`? In such a case you have to combine them via
332337
`TupleX.combineAll()` to get an `ArgsGenerators<TupleX<...>>` which then again can be used in a tuple.
333-
Or use `ArgsGenerator.combine(otherArgsGenerator)` to create an `ArgsGenerators<Tuple2<...>>`.
338+
Or use `cartesian`, `zip` to create an `ArgsGenerators<Tuple2<...>>`.
334339
Following an example (using less than 9 `ArgsGenerators` for brevity -- imports omitted, same as in
335340
`TupleCombine1Test` above)
336341

@@ -349,7 +354,7 @@ class CombineManuallyTest : PredefinedArgsProviders {
349354
@JvmStatic
350355
fun numbersAndChar() = run { // use run to let the compiler infer the return type
351356
val numbers = Tuple(
352-
arb.int().combine(arb.long()), // combines them into an ArbArgsGenerators<Tuple2<Int, Long>>
357+
arb.int().zip(arb.long()), // combines them into an ArbArgsGenerators<Tuple2<Int, Long>>
353358
arb.double(),
354359
arb.bigIntFromUntil(BigInt.ZERO, BigInt.TEN)
355360
).combineAll() // combines all into an ArbArgsGenerators<Tuple3<...>>
@@ -365,19 +370,81 @@ class CombineManuallyTest : PredefinedArgsProviders {
365370

366371
</code-combine-manually>
367372

368-
Note two things. First, tuples are flattened in the process of transforming the definition into `Arguments`. Second,
369-
`Tuple2`/`Tuple3` are just type aliases for `Pair`/`Triple`. Which means, if you want that an argument is like a
373+
Note two things. First, tuples are flattened in the process of transforming the definition into JUnit's `Arguments`.
374+
Second, `Tuple2`/`Tuple3` are just type aliases for `Pair`/`Triple`. Which means, if you want that an argument is like a
370375
`Pair`/`Triple` without being flattened, then define e.g. a `data class`.
371376

372-
The advantage of using tuples instead of manual `combine` are:
377+
The advantage of using tuples instead of manual `cartesian`/`zip` are:
373378

374379
- a) readability (less consecutive `combine` method calls -- less cluttering) and
375380
- b) you only define that you would like to combine them without actually doing it. Which allows that you can for
376381
instance `append` another `ArgsGenerator` to the tuple, replace one at a specific position, `glue` tuples together and
377382
more (see the documentation of [kbox](https://github.com/robstoll/kbox) regarding tuples).
378383

379-
`combine` also provides an overload which takes a `transform` function so that you can turn it into something else than
380-
`Tuple2`.
384+
`cartesian` and `zip` provide an overload which takes a `transform` function so that you can turn the generates values
385+
pairwise into something else than `Tuple2`.
386+
387+
### ordered cartesian
388+
389+
As mentioned in [generic combine](#generic-combine), combining multiple `OrderArgsGenerator`s by using a `Tuple` uses
390+
`cartesian` behind the scenes and results in a new `OrderArgsGenerator` which represents the cartesian product of them,
391+
i.e. all possible combinations.
392+
393+
<code-cartesian-1>
394+
395+
```kotlin
396+
ordered.of(1, 2).cartesian(ordered.of('A', 'B'))
397+
```
398+
399+
</code-cartesian-1>
400+
401+
For the above example, the possible combinations are 1/A, 2/A, 1/B, 2/B
402+
You can pass a `transform` function as last argument and map the values pairwise to another type:
403+
404+
<code-cartesian-2>
405+
406+
```kotlin
407+
ordered.of(1, 2).cartesian(ordered.of(4, 5)) { i1, i2 ->
408+
i1 + i2
409+
}
410+
```
411+
412+
</code-cartesian-2>
413+
414+
The above example results in an `OrderedArgsGenerator<Int>` with the values 5 (1+4), 6 (2+4), 6 (1+5), 7 (2+5)
415+
(the order of the values is implementation specific). As you can see, an `OrderedArgsGenerator` can also generate the
416+
same value multiple times.
417+
418+
### arb zip
419+
420+
As mentioned in [generic combine](#generic-combine), combining multiple `ArbArgsGenerator`s by using a `Tuple` uses
421+
`zip` behind the scenes and results in a new `ArbArgsGenerator`:
422+
423+
<code-zip-1>
424+
425+
```kotlin
426+
arb.intFromUntil(1, 100).zip(arb.charFromTo('A', 'Z'))
427+
```
428+
429+
</code-zip-1>
430+
431+
For the above example, the possible combinations are 1/A, 2/A,... 99/A, 1/B, 2/B... 99/Z
432+
As mentioned in [ordered and arbitrary arguments generators](#ordered-and-arbitrary-arguments-generators), an
433+
`ArbArgsGenerator` order is undefined and it could occur that you see a combination more than once.
434+
You can pass a `transform` function as last argument and map the values pairwise another type:
435+
436+
<code-zip-2>
437+
438+
```kotlin
439+
arb.intFromUntil(1, 100).zip(arb.intFromUntil(1000, 2000)) { i1, i2 ->
440+
i1 + i2
441+
}
442+
```
443+
444+
</code-zip-2>
445+
446+
The above example results in an `ArbArgsGenerator<Int>` generating values from 1001 until 2098 where 1002 until 1099 are
447+
more likely to appear since they result twice (1+1001 and 2+1000 = 1002 etc.).
381448

382449
### combineDependent
383450

@@ -520,7 +587,7 @@ else.
520587
```kotlin
521588
arb.intFromTo(1, 100).chunked(3)
522589
arb.intFromTo(1, 100).chunked(3) { it.sorted() }
523-
arb.charFromTo('a', 't').combine(arb.intFromTo(1, 100)).chunked(3) { it.toMap() }
590+
arb.charFromTo('a', 't').zip(arb.intFromTo(1, 100)).chunked(3) { it.toMap() }
524591
```
525592

526593
</code-chunked>
@@ -584,13 +651,23 @@ method.
584651
Minimalist provides a configuration via `MinimalistConfig` which per default can be customised via
585652
`minimalist.properties`.
586653
This file needs to be available on your classpath. Typically, you put it in src/test/resources.
587-
Next to `minimalist.properties` which is intended to make project based adjustments, you can create a
588-
`minimalist.local.properties`
589-
which you should add on your git ignore list. This file overwrites settings in `minimalist.properties` and is intended
590-
for personal adjustments and debugging.
654+
Next to `minimalist.properties` which is intended to make project based adjustments
655+
(e.g. change `Miniamlist.defaultProfile` to `E2E`, see [profiles](#profiles)), you can create a
656+
`minimalist.local.properties` which you should add on your git ignore list.
657+
This file overwrites settings in `minimalist.properties` and is intended for personal adjustments and debugging.
591658

592659
More documentation about the configuration will follow, in the meantime, take a look at the KDoc of MinimalistConfig.
593660

661+
## Profiles
662+
663+
Minimalist steers how many runs will result at maximum (if not limited by other factors such as
664+
`OrderedArgsGenerator.size`) by the profile definition in use (`MinimalistConfig.defaultProfile` is `Integration`)
665+
and the environment the test runs in (defined via `MinimalistConfig.activeEnv`).
666+
The active environment is determined from environment variables (GitHub and GitLab env vars),
667+
defaulting to `Local` if it cannot be deduced.
668+
669+
See `MinimalistConfig.testProfiles` for what `maxArgs` are defined per default.
670+
594671
# Code Documentation
595672

596673
Code documentation can be found on github-pages: <https://tegonal.github.io/minimalist/2.0.0-RC-1/kdoc>.

0 commit comments

Comments
 (0)