I want to do a number of similar tests on various types in my library.
To simplify things, assume I have a number of vector types implementing Num
class, and I want to generate the same QuickCheck property check prop_absNorm x y = abs x + abs y >= abs (x+y)
that would work on all of the types in library.
I generate such properties using TH:
$(writeTests
(\t ->
[d| prop_absNorm :: $(t) -> $(t) -> Bool
prop_absNorm x y = abs x + abs y >= abs (x+y)
|])
)
My function to generate tests has the following signature:
writeTests :: (TypeQ -> Q [Dec]) -> Q [Dec]
This function looks for all instances of my vector class VectorMath (n::Nat) t
(and, at the same time, instances of Num
) through reify ''VectorMath
and generates all prop functions accordingly.
-ddump-splices
shows something like this:
prop_absNormIntX4 :: Vector 4 Int -> Vector 4 Int -> Bool
prop_absNormIntX4 x y = abs x + abs y >= abs (x+y)
prop_absNormCIntX4 :: Vector 4 CInt -> Vector 4 CInt -> Bool
prop_absNormCIntX4 x y = abs x + abs y >= abs (x+y)
...
prop_absNormFloatX4 :: Vector 4 Float -> Vector 4 Float -> Bool
prop_absNormFloatX4 x y = abs x + abs y >= abs (x+y)
prop_absNormFloatX3 :: Vector 3 Float -> Vector 3 Float -> Bool
prop_absNormFloatX3 x y = abs x + abs y >= abs (x+y)
The problem is that all manually written properties are checked, but generated ones are not.
Note 1: I have generated and non-generated properties in the same file (i.e. TH expression $(..)
is in the same file as the other props).
Note 2: the list of types for creation of prop functions is variable - I want to add other instances of VectorMath
later, so they are automatically added into the test list.
I believe that the problem is that HTF (which presumably uses TH too) parses the original file, not the one with generated code - but I cannot get why this happens.
So my question is: how to solve this problem? If it is not possible to use TH-generated props, then is that possible to do QuickCheck tests on various types (i.e. that it substitutes them into prop_absNorm :: Vector 4 a -> Vector 4 a -> Bool
)?
Also another alternative may be to use TH further to add test entries manually to htf_Main, but I have not figured out how to do this yet; and it does not look like a nice clean solution.
Ok, I managed to solve this problem. The idea is to use TH to aggregate the tests and insert them into
htfMain
. On top of what I have in the question, this includes following steps:IO
actions running QuickCheck tests;TestSuite
;htfMain
.In order to use step 1 I had to use semi-internal function of HTF called
qcAssertion :: (QCAssertion t) => t -> Assertion
. This function is available, but not recommended for external use; it allows running QuickCheck tests nicely, integrating them into report.To proceed with step 2, I use two functions from HTF:
makeTestSuite
andmakeQuickCheckTest
. I also uselocation
function from TH to provide filename and line of the place where the splice with test template is inserted (for nicer test logs).Step 3 is a tricky one: for this we need to find all generated test suites. The problem is that TH does not allow to browse through all functions (including generated) in a module. To overcome this, I added following type class:
So my function
writeTests
generates a new data typedata MTS[prop_name]
and an instance ofMultitypeTestSuite
for that data type. This allows me later to use another splice function in htfMain that will generate a list of test suites out of instances of that class usingreify
:In the end, including all generated tests together with manually written ones looks pretty simple:
So, by adjusting function
$(writeTests)
I am able now to generate and test properties that vary in argument type - for all types available in scope at the same type. Test results and logs are included the same way as original tests.On that the problem is fully solved.