{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE UndecidableInstances #-} {-# LANGUAGE FlexibleContexts #-} module Data.Sv.Core.Laws (test_Laws) where import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.List.NonEmpty (NonEmpty ((:|))) import Data.Monoid (Last) import Data.Profunctor (Profunctor (dimap)) import Data.Semigroupoid (Semigroupoid (o)) import qualified Data.Validation as V import Data.Vector (Vector, fromList, toList) import Test.Tasty (TestTree, testGroup) import Test.Tasty.QuickCheck (Arbitrary (arbitrary), CoArbitrary (coarbitrary), Gen, testProperty, oneof, listOf) import Data.Sv.Decode.Type import qualified Data.Sv.Decode.Core as D test_Laws :: TestTree test_Laws = testGroup "Laws" [ testGroup "Decode Semigroupoid" $ fmap (uncurry testProperty) [ ("Decode Semigroupoid associativity", decodeAssoc) ] ] decodeAssoc :: Gen Bool decodeAssoc = do dec1 <- decodeGen :: Gen (Decode ByteString ByteString ByteString) dec2 <- decodeGen :: Gen (Decode ByteString ByteString ByteString) dec3 <- decodeGen :: Gen (Decode ByteString ByteString ByteString) v <- arbOut i <- arbOut let comp1 = (dec1 `o` dec2) `o` dec3 let comp2 = dec1 `o` (dec2 `o` dec3) pure (eqDecode v i comp1 comp2) eqDecode :: (Eq e, Eq a) => Vector s -> Ind -> Decode e s a -> Decode e s a -> Bool eqDecode v i d e = D.runDecode d v i == D.runDecode e v i decodeGen :: (CoArbitrary (In s), Arbitrary (Out a), Arbitrary (Out e)) => Gen (Decode e s a) decodeGen = buildDecode <$> (unwrap <$> arbitrary) unwrap :: (Input s -> Output e a) -> Vector s -> Ind -> (Validation e a, Last Bool, Ind) unwrap = curry . dimap Input unwrapO newtype Output e a = Output { unwrapO :: (Validation e a, Last Bool, Ind) } newtype Input s = Input (Vector s, Ind) newtype In a = In a newtype Out a = Out { unout :: a } coarbIn :: CoArbitrary (In a) => a -> Gen b -> Gen b coarbIn = coarbitrary . In arbOut :: Arbitrary (Out a) => Gen a arbOut = unout <$> arbitrary instance (Arbitrary (Out e), Arbitrary (Out a)) => Arbitrary (Output e a) where arbitrary = Output <$> ((,,) <$> oneof [V.Failure <$> arbOut, V.Success <$> arbOut] <*> arbitrary <*> (Ind <$> arbOut)) instance (CoArbitrary (In (Vector s)), CoArbitrary (In Ind)) => CoArbitrary (Input s) where coarbitrary (Input (v,i)) = coarbIn v . coarbIn i instance Arbitrary (Out ByteString) where arbitrary = Out <$> (BS.pack <$> arbitrary) instance CoArbitrary (In ByteString) where coarbitrary (In bs) = coarbitrary (BS.unpack bs) instance Arbitrary (Out Ind) where arbitrary = Out . Ind <$> arbitrary instance Arbitrary (Out Int) where arbitrary = Out <$> arbitrary instance CoArbitrary (In s) => CoArbitrary (In (Vector s)) where coarbitrary (In v) = coarbitrary (In <$> toList v) instance CoArbitrary (In Ind) where coarbitrary (In (Ind i)) = coarbitrary i instance Arbitrary (Out e) => Arbitrary (Out (DecodeErrors e)) where arbitrary = Out . DecodeErrors <$> arbOut instance Arbitrary (Out e) => Arbitrary (Out (NonEmpty e)) where arbitrary = Out <$> ((:|) <$> arbOut <*> arbOut) instance Arbitrary (Out e) => Arbitrary (Out [e]) where arbitrary = Out <$> listOf arbOut instance Arbitrary (Out e) => Arbitrary (Out (Vector e)) where arbitrary = Out . fromList <$> arbOut instance Arbitrary (Out e) => Arbitrary (Out (DecodeError e)) where arbitrary = Out <$> oneof [ pure UnexpectedEndOfRow , ExpectedEndOfRow <$> arbOut , UnknownCategoricalValue <$> arbOut <*> arbOut , BadParse <$> arbOut , BadDecode <$> arbOut ]