{-# LANGUAGE DeriveGeneric, FlexibleContexts, MultiParamTypeClasses, RankNTypes,
ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wno-unused-binds #-}
{- |
Module : Main
Description : Round-trip property testing
Copyright : (c) Ivan Lazar Miljenovic
License : MIT
Maintainer : Ivan.Miljenovic@gmail.com
-}
import Streamly.Csv
import Streamly
import qualified Streamly.Prelude as S
import Test.Hspec (describe, hspec)
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck (Arbitrary(..))
import Test.QuickCheck.Monadic
import Test.QuickCheck.Instances ()
import Control.Monad.Catch (try, MonadCatch(..), SomeException)
import Data.Text (Text)
import qualified Data.Vector as V
import GHC.Generics (Generic)
--------------------------------------------------------------------------------
main :: IO ()
main = hspec $ do
describe "Plain records" $ do
prop "Just data" $ \recs ->
monadicIO $ run (useType encodeDecode recs) >>= assert
prop "With headers" $ \recs ->
monadicIO $ run (useType encodeDecodeHeader recs) >>= assert
describe "Named records" $ do
prop "Default order" $ \recs ->
monadicIO $ run (useType encodeDecodeNamed recs) >>= assert
prop "Reversed order" $ \recs ->
monadicIO $ run (useType encodeDecodeNamedReordered recs) >>= assert
encodeDecode :: (FromRecord a, ToRecord a, Eq a, MonadAsync m, MonadCatch m)
=> [a] -> m Bool
encodeDecode = encodeDecodeWith (decode NoHeader . encode Nothing)
encodeDecodeHeader :: (DefaultOrdered a, FromRecord a, ToRecord a, Eq a
, MonadAsync m, MonadCatch m)
=> [a] -> m Bool
encodeDecodeHeader = encodeDecodeWith (decode HasHeader . encodeDefault)
encodeDecodeNamed :: (DefaultOrdered a, FromNamedRecord a, ToNamedRecord a
, Eq a, MonadAsync m, MonadCatch m)
=> [a] -> m Bool
encodeDecodeNamed = encodeDecodeWith (decodeByName . encodeByNameDefault)
encodeDecodeNamedReordered :: forall a m. (DefaultOrdered a, FromNamedRecord a
,ToNamedRecord a, Eq a, MonadAsync m, MonadCatch m)
=> [a] -> m Bool
encodeDecodeNamedReordered = encodeDecodeWith (decodeByName . encodeByName hdr)
where
hdr = V.reverse (headerOrder (undefined :: a))
encodeDecodeWith :: forall a m. (Eq a, MonadAsync m, MonadCatch m)
=> (SerialT m a -> SerialT m a)
-> [a] -> m Bool
encodeDecodeWith f as = fmap (either (const False) (as==))
. (try :: m [a] -> m (Either SomeException [a]))
. S.toList
. f
. S.fromList
$ as
useType :: ([Test] -> r) -> [Test] -> r
useType = id
data Test = Test
{ columnA :: !Int
, longer_column_name :: !Text
, mebbe :: !(Maybe Double)
} deriving (Eq, Show, Read, Generic)
-- DeriveAnyClass doesn't work with these types because of the Maybe
instance FromRecord Test
instance ToRecord Test
instance DefaultOrdered Test
instance FromNamedRecord Test
instance ToNamedRecord Test
instance Arbitrary Test where
arbitrary = Test <$> arbitrary <*> arbitrary <*> arbitrary