Even more rigorous testing with PropCheck
MilesCranmer opened this issue · 1 comments
100% test coverage is not enough!
I think we should add PropCheck.jl testing to probe more edge cases.
Here's a look at how this could work (https://seelengrab.github.io/PropCheck.jl/stable/Examples/structs.html). The following code builds a PropCheck generator of Quantity{T, R}
s:
using Test
using DynamicQuantities
using DynamicQuantities: DEFAULT_DIM_BASE_TYPE
using PropCheck: itype, isample, interleave, check, generate
DEFAULT_R = DEFAULT_DIM_BASE_TYPE
function ifixedrational(R; limit=100)
inumerator = isample(-limit:limit)
idenominator = isample(1:limit)
iargs = interleave(inumerator, idenominator)
return map(R, map(splat(Rational), iargs))
end
function idimensions(R; dimension_limit=100)
iargs = interleave(
ntuple(
_ -> ifixedrational(R, limit=dimension_limit),
fieldcount(Dimensions{R})
)...
)
return map(splat(Dimensions{R}), iargs)
end
function iquantity(T, R; dimension_limit=100)
iargs = interleave(itype(T), idimensions(R; dimension_limit))
return map(splat(Quantity), iargs)
end
with this, we can see that the combination of these iterators gives us random quantities:
generate(iquantity(Float32, DEFAULT_R))
# Tree(-2.802293e-10 m³³²³ᐟ¹²⁶⁰⁰ kg²⁰²⁶³ᐟ²⁵²⁰⁰ s⁸⁹⁰⁹ᐟ¹²⁶⁰⁰ A⁻⁷⁰⁶ᐟ¹⁵⁷⁵ K⁻⁹²ᐟ⁶³ cd¹ᐟ⁶ mol⁸⁰⁴⁷ᐟ³⁶⁰⁰)
with this iterator, we can test different properties, like commutativity of operators, via PropCheck's shrinkage approach:
@test check(interleave(iquantity(Float32, DEFAULT_R), iquantity(Float32, DEFAULT_R))) do (q1, q2)
q1 * q2 == q2 * q1
end
This is more powerful than writing out test cases by hand since every time it runs, it will randomly check a different part of the code. Manual test cases might keep missing a particular combination of code branches and we may never know about it.
Now to use Supposition.jl for Julia 1.11+.