hawnzug / polycheck

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Testing Polymorphic Functions

GitHub Workflow Status

This library can be used with QuickCheck to test polymorphic properties. It will automatically pick suitable types to instantiate the properties. Furthermore, it can avoid unnecessary test cases by prefilling special parts of the inputs. Users who manually pick types to test polymorphic functions or use polyQuickCheck or monomorphic in QuickCheck can switch to this library to get more efficient testing without extra effort.

Quick Start

Suppose we want to test the following two polymorphic properties:

prop_reverse :: Eq a => [a] -> Bool
prop_reverse l = reverse (reverse l) == l

prop_map :: Eq b => (a -> b) -> [a] -> Bool
prop_map f xs = reverse (map f xs) == map f (reverse xs)

First we need to enable several extensions:

{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE DeriveGeneric #-}

and import the monomorphic function:

import Text.Show.Functions
import Test.QuickCheck hiding (monomorphic)
import Test.Logarithm.TH (monomorphic)

Now invoke monomorphic to generate the monomorphised definitions:

$(monomorphic 'prop_reverse)
$(monomorphic 'prop_map)

Two new functions prop_reverse_mono and prop_map_mono will be generated, which are properties that can be directly called by quickCheck:

main :: IO ()
main = do
  quickCheck prop_reverse_mono
  quickCheck prop_map_mono

More examples

Here is a user defined list type with its Arbitrary instance

data MyList a = MyNil | MyCons a (MyList a) deriving (Eq, Show)

instance Arbitrary a => Arbitrary (MyList a) where
  arbitrary = foldr MyCons MyNil <$> listOf arbitrary

Then we can monomorphize functions involving this type

prop :: Eq a => MyList a -> MyList (a, Maybe a) -> Bool
$(monomorphic prop)

It also works for inductive types with functions

data State s a = State (s -> (a, s)) deriving (Show)
instance (CoArbitrary s, Arbitrary s, Arbitrary b) => Arbitrary (State s b) where
  arbitrary = State <$> arbitrary
prop :: s -> State s a -> Bool
$(monomorphic prop)

See tests/Main.hs for more examples.

Build the package

Building:

cabal build

Testing:

cabal test

To see the counterexamples generated for the wrong properties:

cabal run test

To see the generated definitions, one can add the -ddump-splices GHC option.

Install: At the time of writing, this package has not been published to the Hackage. To use it in your own testing, you have to manually include this package in your cabal.project.

Notes

Type of monomorphic

This module only exports one function

monomorphic :: Name -> Q [Dec]

Note that its type is different from the Test.QuickCheck.monomorphic, which has type Name -> Q Exp. This is because our monomorphic needs to generate the definitions of those types produced by the logarithm operation. It also generates a new function with the suffix _mono because it provides custom instances of Arbitrary to reduce the test cases, so it cannot reuse the original function by simply specializing its type. Therefore monomorphic must be invoked at the top level, unlike the one in QuickCheck.

Counterexamples

When QuickCheck finds a counterexample, sometimes it will be printed in a hardly readable form because it might use the new types generated by the library. Those types are unsimplified and their names are verbose. If you find the counterexample too confusing, it might be a good idea to instantiate the function by integers and increase the number of test cases to find a readable counter example.

Functions Types

To test a function with function arguments, we need Show (a -> b) to make QuickCheck happy. Currently we don't have good ways to show a normal function. And the Fun a b in QuickCheck is not treated specially. Thus to make it work, we can just import Text.Show.Functions.

Generic

DeriveGeneric is needed to automatically derive the CoArbitrary instances for the logarithm types. Because the logarithm types might contain Void, this module also implicitly exports an instance of CoArbitrary Void. It can possibly be removed in the future if the logarithm types were simplified by eliminating empty types.

About

License:BSD 3-Clause "New" or "Revised" License


Languages

Language:Haskell 100.0%