Gabriel
Gabriel

Reputation: 1761

Better Arbitrary Number?

I need an Arbitrary java.lang.Number.

Here is what I came up with:

    @Provide
    Arbitrary<Number> numbers(){
        return
        Combinators.combine(
                Arbitraries.integers().between(0, 5),
                Arbitraries.integers(),
                Arbitraries.longs(),
                Arbitraries.bigIntegers(),
                Arbitraries.floats(),
                Arbitraries.doubles(),
                Arbitraries.bigDecimals()
        ).as((i, intA, longA, bigIntegerA, floatA, doubleA, bigDecimalA) ->
          new Number[]{intA, longA, bigIntegerA, floatA, doubleA, bigDecimalA}[i]
        );
    }

This kind of works but causes

STANDARD_ERROR
10:17:38      Aug 24, 2022 2:17:38 PM net.jqwik.engine.properties.RandomizedShrinkablesGenerator logEdgeCasesOutnumberTriesIfApplicable
10:17:38      INFO: Edge case generation exceeds number of tries. Stopped after 1000 generated cases.

Is there a better way to implement an Arbitrary that represents all the possible values of java.lang.Number?

Footnote:

For you set theorists out there:

Yes I know that I am forming a product and extracting a union, which is about the least efficient way to do this. Undoubtedly this confuses the shrinker to no end. This was the best I could do. If there is a way to do this as a simple union instead, please let me know.

Upvotes: 1

Views: 189

Answers (2)

johanneslink
johanneslink

Reputation: 5351

There's a simpler solution, assuming that any concrete subtype of Number should be generated:

@Property
void test(@ForAll Number aNumber) {
    System.out.println(aNumber.getClass() + " = " + aNumber);
}

@Property
void test2(@ForAll("anyNumber") Number aNumber) {
    System.out.println(aNumber.getClass() + " = " + aNumber);
}

@Provide
Arbitrary<Number> anyNumber() {
    return Arbitraries.defaultFor(Number.class);
}

As you can see, trying to generate a Number just works. Under the hood, jqwik checks for all registered arbitrary providers that are compatible with type Number and randomly chooses between them.

Upvotes: 1

Gabriel
Gabriel

Reputation: 1761

Use Arbitraries.oneOf

    @Provide
    Arbitrary<Number> numbers(){
        return Arbitraries.oneOf(
          Arbitraries.integers(),
          Arbitraries.longs(),
          Arbitraries.bigIntegers(),
          Arbitraries.floats(),
          Arbitraries.doubles(),
          Arbitraries.bigDecimals()
        );
    }

This will still cause the Edge case generation exceeds number of tries warning unfortunately.

Upvotes: 0

Related Questions