{-# LANGUAGE Rank2Types, ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Codec.Winery.Test
(
TestGen(..)
, printTests
, Tested(..)
, testCase
, allTests
, mergeTests
) where
import Test.HUnit
import qualified Data.Map as M
import qualified Data.HashMap.Strict as HM
import qualified Data.ByteString as B
import Data.Functor.Identity
import Data.Hashable
import Data.Proxy
import qualified Data.Sequence as S
import Data.Typeable
import Codec.Winery
import Data.Word
import Data.Void
import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as UV
import GHC.Generics
import Text.Show
import qualified Data.Text as T
testCase :: (Show a, Eq a, Serialise a)
=> Schema
-> B.ByteString
-> a
-> Test
testCase sch bs expected = case getDecoder sch of
Left err -> TestCase $ assertFailure (show err)
Right f -> expected ~=? evalDecoder f bs
mergeTests :: M.Map TypeRep [Test] -> Test
mergeTests = TestList . concatMap (\(k, v) -> map (show k ~:) v) . M.toList
allTests :: forall a. (TestGen a, Tested a) => M.Map TypeRep [Test]
allTests = M.insertWith (++) (typeRep (Proxy @ a)) (testCases @ a) (inheritedTests (Proxy @ a))
class TestGen a => Tested a where
testCases :: [Test]
instance Tested Void where testCases = []
instance Tested Bool where testCases = []
instance Tested Int where testCases = []
instance Tested Word8 where testCases = []
instance Tested Word16 where testCases = []
instance Tested Word32 where testCases = []
instance Tested Word64 where testCases = []
instance Tested Float where testCases = []
instance Tested Double where testCases = []
instance Tested () where testCases = []
instance Tested Char where testCases = []
instance Tested T.Text where testCases = []
instance Tested B.ByteString where testCases = []
instance Tested a => Tested (Identity a) where
testCases = testCases @ a
instance Tested a => Tested (S.Seq a) where testCases = []
instance Tested a => Tested [a] where testCases = []
instance (Tested a, Tested b) => Tested (Either a b) where testCases = []
instance (Tested a, Tested b) => Tested (a, b) where testCases = []
instance (Tested a, Tested b, Tested c) => Tested (a, b, c) where testCases = []
instance (Tested a, Tested b, Tested c, Tested d) => Tested (a, b, c, d) where testCases = []
instance Tested a => Tested (V.Vector a) where testCases = []
instance (UV.Unbox a, Tested a) => Tested (UV.Vector a) where testCases = []
instance (Hashable k, Tested k, Tested a) => Tested (HM.HashMap k a) where testCases = []
instance (Ord k, Tested k, Tested a) => Tested (M.Map k a) where testCases = []
printTests :: forall a. (TestGen a, Serialise a, Show a) => IO ()
printTests = putStrLn $ showTests (genTestCases :: [a])
showTests :: (Serialise a, Show a) => [a] -> String
showTests xs = showListWith ppTest xs ""
ppTest :: (Serialise a, Show a) => a -> ShowS
ppTest a = showString "testCase "
. showsPrec 11 (schema [a])
. showChar ' '
. showsPrec 11 (serialiseOnly a)
. showChar ' '
. showsPrec 11 a
class Typeable a => TestGen a where
genTestCases :: [a]
inheritedTests :: Proxy a -> M.Map TypeRep [Test]
default genTestCases :: (Generic a, GTestGen (Rep a)) => [a]
genTestCases = fmap to ggenTestCases
default inheritedTests :: (GTestGen (Rep a)) => Proxy a -> M.Map TypeRep [Test]
inheritedTests _ = ginheritedTests (Proxy @ (Rep a))
class GTestGen f where
ggenTestCases :: [f x]
ginheritedTests :: proxy f -> M.Map TypeRep [Test]
instance GTestGen V1 where
ggenTestCases = mempty
ginheritedTests _ = mempty
instance GTestGen U1 where
ggenTestCases = [U1]
ginheritedTests _ = mempty
instance GTestGen f => GTestGen (Rec1 f) where
ggenTestCases = fmap Rec1 ggenTestCases
ginheritedTests _ = ginheritedTests (Proxy @ f)
instance (Tested c, TestGen c) => GTestGen (K1 i c) where
ggenTestCases = fmap K1 genTestCases
ginheritedTests _ = allTests @ c
instance GTestGen f => GTestGen (M1 i c f) where
ggenTestCases = fmap M1 ggenTestCases
ginheritedTests _ = ginheritedTests (Proxy @ f)
instance (GTestGen f, GTestGen g) => GTestGen (f :+: g) where
ggenTestCases = fmap L1 ggenTestCases ++ fmap R1 ggenTestCases
ginheritedTests _ = ginheritedTests (Proxy @ f)
`mappend` ginheritedTests (Proxy @ g)
instance (GTestGen f, GTestGen g) => GTestGen (f :*: g) where
ggenTestCases = ((:*:) <$> ggenTestCases <*> xs)
++ ((:*:) <$> take 1 ggenTestCases <*> ys)
where
(xs, ys) = splitAt 1 ggenTestCases
ginheritedTests _ = ginheritedTests (Proxy @ f)
`mappend` ginheritedTests (Proxy @ g)
deriving instance TestGen a => TestGen (Identity a)
instance TestGen ()
instance TestGen Bool
instance (Tested a, Tested b) => TestGen (a, b)
instance (Tested a, Tested b, Tested c) => TestGen (a, b, c)
instance (Tested a, Tested b, Tested c, Tested d) => TestGen (a, b, c, d)
instance (Tested a, Tested b) => TestGen (Either a b)
instance Tested a => TestGen [a] where
genTestCases = [[]]
inheritedTests _ = allTests @ a
instance Tested a => TestGen (S.Seq a) where
genTestCases = [mempty]
inheritedTests _ = allTests @ a
instance Tested a => TestGen (V.Vector a) where
genTestCases = [V.empty]
inheritedTests _ = allTests @ a
instance (UV.Unbox a, Tested a) => TestGen (UV.Vector a) where
genTestCases = [UV.empty]
inheritedTests _ = allTests @ a
instance (Hashable k, Tested k, Tested a) => TestGen (HM.HashMap k a) where
genTestCases = HM.singleton <$> genTestCases <*> genTestCases
inheritedTests _ = allTests @ k `mappend` allTests @ a
instance (Ord k, Tested k, Tested a) => TestGen (M.Map k a) where
genTestCases = [M.empty]
inheritedTests _ = allTests @ k `mappend` allTests @ a
instance TestGen Void where
genTestCases = []
inheritedTests = mempty
instance TestGen Int where
genTestCases = [42]
inheritedTests = mempty
instance TestGen Word8 where
genTestCases = [42]
inheritedTests = mempty
instance TestGen Word16 where
genTestCases = [42]
inheritedTests = mempty
instance TestGen Word32 where
genTestCases = [42]
inheritedTests = mempty
instance TestGen Word64 where
genTestCases = [42]
inheritedTests = mempty
instance TestGen Float where
genTestCases = [pi]
inheritedTests = mempty
instance TestGen Double where
genTestCases = [pi]
inheritedTests = mempty
instance TestGen Char where
genTestCases = ['X']
inheritedTests = mempty
instance TestGen T.Text where
genTestCases = [mempty]
inheritedTests = mempty
instance TestGen B.ByteString where
genTestCases = [mempty]
inheritedTests = mempty