{-# 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