HTF不测试TH生成的道具

art*_*tem 5 haskell quickcheck template-haskell htf

我想在我的库中对各种类型进行一些类似的测试.

为简化起见,假设我有许多实现Num类的向量类型,并且我想生成相同的QuickCheck属性检查prop_absNorm x y = abs x + abs y >= abs (x+y),该检查可以对库中的所有类型起作用.

我使用TH生成这样的属性:

$(writeTests
    (\t ->
        [d| prop_absNorm :: $(t) -> $(t) -> Bool
            prop_absNorm x y = abs x + abs y >= abs (x+y)
        |])
 )
Run Code Online (Sandbox Code Playgroud)

我生成测试的函数具有以下签名:

writeTests :: (TypeQ -> Q [Dec]) -> Q [Dec]
Run Code Online (Sandbox Code Playgroud)

此函数查找我的矢量类的所有实例VectorMath (n::Nat) t(以及同时的实例Num)reify ''VectorMath并相应地生成所有prop函数. -ddump-splices显示这样的事情:

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)
Run Code Online (Sandbox Code Playgroud)

问题是所有手动编写的属性都被检查,但生成的属性不是.

注1:我在同一个文件中生成了非生成属性(即TH表达式$(..)与其他道具在同一个文件中).

注2:用于创建prop函数的类型列表是可变的 - 我想VectorMath稍后添加其他实例,因此它们会自动添加到测试列表中.

我相信问题是HTF(可能也使用TH)解析原始文件,而不是生成代码的文件 - 但我不知道为什么会发生这种情况.

所以我的问题是:如何解决这个问题?如果不可能使用TH生成的道具,那么可以对各种类型进行QuickCheck测试(即它可以替代它们prop_absNorm :: Vector 4 a -> Vector 4 a -> Bool)吗?

另一个替代方案可能是使用TH进一步手动将测试条目添加到htf_Main,但我还没想出如何做到这一点; 它看起来不是一个很好的清洁解决方案.

art*_*tem 1

好的,我设法解决了这个问题。这个想法是使用 TH 来聚合测试并将它们插入到htfMain. 除了我在问题中提到的内容之外,还包括以下步骤:

  1. 将所有可测试的属性转换为IO运行 QuickCheck 测试的操作;
  2. 将所有测试聚合到TestSuite
  3. 将所有测试套件聚合到一个列表中并将其放入htfMain.

为了使用步骤 1,我必须使用 HTF 的半内部函数qcAssertion :: (QCAssertion t) => t -> Assertion. 该功能可用,但不建议外部使用;它允许很好地运行 QuickCheck 测试,并将它们集成到报告中。

为了继续步骤 2,我使用 HTF 中的两个函数:makeTestSuitemakeQuickCheckTest。我还使用locationTH 的函数来提供文件名和插入测试模板的拼接位置的行(以获得更好的测试日志)。

第 3 步是一个棘手的步骤:为此我们需要找到所有生成的测试套件。问题是 TH 不允许浏览模块中的所有函数(包括生成的函数)。为了克服这个问题,我添加了以下类型类:

class MultitypeTestSuite name where
    multitypeTestSuite :: name -> TestSuite
Run Code Online (Sandbox Code Playgroud)

所以我的函数writeTests生成一个新的数据类型和该数据类型data MTS[prop_name]的实例。MultitypeTestSuite这允许我稍后在 htfMain 中使用另一个 splice 函数,该函数将从该类的实例中生成测试套件列表,使用reify

aggregateTests :: ExpQ
aggregateTests = do
    ClassI _ instances <- reify ''MultitypeTestSuite
    liftM ListE . forM instances
          $ \... -> [e| multitypeTestSuite $(...) |]
Run Code Online (Sandbox Code Playgroud)

最后,将所有生成的测试与手动编写的测试一起包含起来看起来非常简单:

main :: IO ()
main = htfMain $ htf_importedTests ++ $(aggregateTests)
Run Code Online (Sandbox Code Playgroud)

因此,通过调整函数,$(writeTests)我现在可以生成和测试参数类型不同的属性 - 对于同一类型范围内可用的所有类型。测试结果和日志的包含方式与原始测试相同。

至此问题就完全解决了。