rima.j
rima.j

Reputation: 31

jqwik pairs of sorted array with some element of it

Following code aims to generate random sorted array, and key as one element of that array. But I do not know the issue, the keys are not in the array?

@Provide
Arbitrary<Map<Integer, Integer[]>> llstPairs() {


  // sortedArrayGenerator is generattor that return Arbitrary<Integer[]> sorted values
  // and it works fine
  Arbitrary<Integer[]> vals = sortedArrayGenerator();

  Integer[] sample = vals.sample();

  Arbitrary<Integer> key = Arbitraries.samples(sample);


  return Arbitraries.maps(key,vals);

}

Why my keys are not in the array, I need the key to be one element of the Integer[] array.

Upvotes: 1

Views: 482

Answers (1)

johanneslink
johanneslink

Reputation: 5351

The one important thing you should be aware of is: "Never create your own sample. Let jqwik decide when to do that." In other words: Arbitrary.sample() is only for testing generators, e.g. in JShell, or using generators outside of jqwik properties.

What you need instead is Arbitrary.flatMap(). Flat mapping is needed whenever the result of one generator is needed to feed another generator. So your first try could be:

@Provide
Arbitrary<Map<Integer, Integer[]>> llstPairs() {
    Arbitrary<Integer[]> vals = sortedArrayGenerator().filter(array -> array.length > 0);

    return vals.flatMap(array -> {
        Arbitrary<Integer> keys = Arbitraries.of(array);
        return Arbitraries.maps(keys, vals);
    });

}

Two sidenotes:

However, it's not as easy because the generated array for the map is a different one than you used for the keys. You have to make sure that you use the exact same array when creating the map. So you might go with:

@Provide
Arbitrary<Map<Integer, Integer[]>> llstPairs() {
    Arbitrary<Integer[]> vals = sortedArrayGenerator().filter(array -> array.length > 0);

    return vals.flatMap(array -> {
        Arbitrary<Integer> keys = Arbitraries.of(array);
        Arbitrary<List<Integer>> listOfKeys = keys.list();
        return listOfKeys.map(lok -> {
            Map<Integer, Integer[]> map = new HashMap<>();
            for (Integer k : lok) {
                map.put(k, array);
            }
            return map;
        });
    });
}

which is quite involved given that - as I understand it - you don't really need the map but want a list of key-array-pairs. That's why I would go with this:

@Provide
Arbitrary<List<Tuple.Tuple2<Integer, Integer[]>>> listOfPairs() {
    Arbitrary<Integer[]> vals = sortedArrayGenerator().filter(array -> array.length > 0);
    return vals.flatMap(arrayOfInt -> {
        Arbitrary<Integer> key = Arbitraries.of(arrayOfInt);
        return key.map(k -> Tuple.of(k, arrayOfInt)).list();
    });
}

And here's a property to check that it does what it's supposed to do:

@Property(tries = 100)
void listOfPairs_keyIsInArray(@ForAll("listOfPairs") List<Tuple.Tuple2<Integer, Integer[]>> listOfPairs) {
    for (Tuple.Tuple2<Integer, Integer[]> pair : listOfPairs) {
        Integer key = pair.get1();
        Integer[] array = pair.get2();
        Assertions.assertThat(array).contains(key);
    }
}

Upvotes: 1

Related Questions