{-# LANGUAGE TypeApplications #-} import Control.Arrow (ArrowMonad) import Control.Selective import Data.Functor.Identity import Data.Maybe hiding (maybe) import Prelude hiding (maybe) import Test.Tasty import Test.Tasty.QuickCheck hiding (Success, Failure) import Test.Tasty.ExpectedFailure import Build import Laws import Validation import qualified Control.Selective.Free as F import qualified Control.Selective.Rigid.Free as FR import qualified Teletype as F import qualified Teletype.Rigid as FR main :: IO () main = defaultMain $ testGroup "Tests" [pingPong, build, over, under, validation, arrowMonad, maybe, identity] -------------------------------------------------------------------------------- ------------------------ Ping-pong---------------------------------------------- -------------------------------------------------------------------------------- pingPong :: TestTree pingPong = testGroup "pingPong" [ testProperty "Free.getEffects pingPongS == [Read,Write \"pong\"]" $ F.getEffects F.pingPongS == [F.Read (const ()),F.Write "pong" ()] , testProperty "Free.getNecessaryEffects pingPongS == [Read]" $ F.getNecessaryEffects F.pingPongS == [F.Read (const ())] , testProperty "Free.Rigid.getEffects pingPongS == [Read,Write \"pong\"]" $ FR.getEffects FR.pingPongS == [FR.Read (const ()),FR.Write "pong" ()] ] -------------------------------------------------------------------------------- ------------------------ Build ------------------------------------------------- -------------------------------------------------------------------------------- build :: TestTree build = testGroup "Build" [cyclicDeps, taskBindDeps, runBuildDeps] cyclicDeps :: TestTree cyclicDeps = testGroup "cyclicDeps" [ testProperty "dependenciesOver (fromJust $ cyclic \"B1\") == [\"C1\",\"B2\",\"A2\"]" $ dependenciesOver (fromJust $ cyclic "B1") == ["C1","B2","A2"] , testProperty "dependenciesOver cyclic \"B2\") == [\"C1\",\"A1\",\"B1\"]" $ dependenciesOver (fromJust $ cyclic "B2") == ["C1","A1","B1"] , testProperty "dependenciesUnder (fromJust $ cyclic \"B1\") == [\"C1\"]" $ dependenciesUnder (fromJust $ cyclic "B1") == ["C1"] , testProperty "dependenciesUnder cyclic \"B2\") == [\"C1\"]" $ dependenciesUnder (fromJust $ cyclic "B2") == ["C1"] ] taskBindDeps :: TestTree taskBindDeps = testGroup "taskBindDeps" [ testProperty "dependenciesOver taskBind == [\"A1\",\"A2\",\"C5\",\"C6\",\"A2\",\"D5\",\"D6\"]" $ dependenciesOver taskBind == ["A1","A2","C5","C6","A2","D5","D6"] , testProperty "dependenciesUnder taskBind == [\"A1\"]" $ dependenciesUnder taskBind == ["A1"] ] runBuildDeps :: TestTree runBuildDeps = testGroup "runBuildDeps" [ testProperty "runBuild (fromJust $ cyclic \"B1\") == [Fetch \"C1\",Fetch \"B2\",Fetch \"A2\"]" $ runBuild (fromJust $ cyclic "B1") == [Fetch "C1" (const ()),Fetch "B2" (const ()),Fetch "A2" (const ())] ] -------------------------------------------------------------------------------- ------------------------ Over -------------------------------------------------- -------------------------------------------------------------------------------- over :: TestTree over = testGroup "Over" [overLaws, overTheorems, overProperties] overLaws :: TestTree overLaws = testGroup "Laws" [ testProperty "Identity: (x <*? pure id) == (either id id <$> x)" $ \x -> lawIdentity @(Over String) x , testProperty "Distributivity: (pure x <*? (y *> z)) == ((pure x <*? y) *> (pure x <*? z))" $ \x -> lawDistributivity @(Over String) @Int @Int x , testProperty "Associativity: take a look at tests/Laws.hs" $ \x -> lawAssociativity @(Over String) @Int @Int x ] overTheorems :: TestTree overTheorems = testGroup "Theorems" [ testProperty "Apply a pure function to the result: (f <$> select x y) == (select (second f <$> x) ((f .) <$> y))" $ \x -> theorem1 @(Over String) @Int @Int x , testProperty "Apply a pure function to the Left case of the first argument: (select (first f <$> x) y) == (select x ((. f) <$> y))" $ \x -> theorem2 @(Over String) @Int @Int @Int x , testProperty "Apply a pure function to the second argument: (select x (f <$> y)) == (select (first (flip f) <$> x) ((&) <$> y))" $ \x -> theorem3 @(Over String) @Int @Int @Int x , testProperty "Generalised identity: (x <*? pure y) == (either y id <$> x)" $ \x -> theorem4 @(Over String) @Int @Int x , testProperty "(f <*> g) == (f `apS` g)" $ \x -> theorem5 @(Over String) @Int @Int x , testProperty "Interchange: (x *> (y <*? z)) == ((x *> y) <*? z)" $ \x -> theorem6 @(Over String) @Int @Int x ] overProperties :: TestTree overProperties = testGroup "Properties" [ expectFail $ testProperty "pure-right: pure (Right x) <*? y = pure x" $ \x -> propertyPureRight @(Over String) @Int @Int x , testProperty "pure-left: pure (Left x) <*? y = ($x) <$> y" $ \x -> propertyPureLeft @(Over String) @Int @Int x ] -------------------------------------------------------------------------------- ------------------------ Under ------------------------------------------------- -------------------------------------------------------------------------------- under :: TestTree under = testGroup "Under" [underLaws, underTheorems, underProperties] underLaws :: TestTree underLaws = testGroup "Laws" [ testProperty "Identity: (x <*? pure id) == (either id id <$> x)" $ \x -> lawIdentity @(Under String) x , testProperty "Distributivity: (pure x <*? (y *> z)) == ((pure x <*? y) *> (pure x <*? z))" $ \x -> lawDistributivity @(Under String) @Int @Int x , testProperty "Associativity: take a look at tests/Laws.hs" $ \x -> lawAssociativity @(Under String) @Int @Int x ] underTheorems :: TestTree underTheorems = testGroup "Theorems" [ testProperty "Apply a pure function to the result: (f <$> select x y) == (select (second f <$> x) ((f .) <$> y))" $ \x -> theorem1 @(Under String) @Int @Int x , testProperty "Apply a pure function to the Left case of the first argument: (select (first f <$> x) y) == (select x ((. f) <$> y))" $ \x -> theorem2 @(Under String) @Int @Int @Int x , testProperty "Apply a pure function to the second argument: (select x (f <$> y)) == (select (first (flip f) <$> x) ((&) <$> y))" $ \x -> theorem3 @(Under String) @Int @Int @Int x , testProperty "Generalised identity: (x <*? pure y) == (either y id <$> x)" $ \x -> theorem4 @(Under String) @Int @Int x -- 'Under' is a non-rigid selective functor , expectFail $ testProperty "(f <*> g) == (f `apS` g)" $ \x -> theorem5 @(Under String) @Int @Int x , testProperty "Interchange: (x *> (y <*? z)) == ((x *> y) <*? z)" $ \x -> theorem6 @(Under String) @Int @Int x ] underProperties :: TestTree underProperties = testGroup "Properties" [ testProperty "pure-right: pure (Right x) <*? y = pure x" $ \x -> propertyPureRight @(Under String) @Int @Int x , expectFail $ testProperty "pure-left: pure (Left x) <*? y = ($x) <$> y" $ \x -> propertyPureLeft @(Under String) @Int @Int x ] -------------------------------------------------------------------------------- ------------------------ Validation -------------------------------------------- -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- validation :: TestTree validation = testGroup "Validation" [validationLaws, validationTheorems, validationProperties, validationExample] validationLaws :: TestTree validationLaws = testGroup "Laws" [ testProperty "Identity: (x <*? pure id) == (either id id <$> x)" $ \x -> lawIdentity @(Validation String) @Int x , testProperty "Distributivity: (pure x <*? (y *> z)) == ((pure x <*? y) *> (pure x <*? z))" $ \x -> lawDistributivity @(Validation String) @Int @Int x , testProperty "Associativity: take a look at tests/Laws.hs" $ \x -> lawAssociativity @(Validation String) @Int @Int @Int x ] validationTheorems :: TestTree validationTheorems = testGroup "Theorems" [ testProperty "Apply a pure function to the result: (f <$> select x y) == (select (second f <$> x) ((f .) <$> y))" $ \x -> theorem1 @(Validation String) @Int @Int @Int x , testProperty "Apply a pure function to the Left case of the first argument: (select (first f <$> x) y) == (select x ((. f) <$> y))" $ \x -> theorem2 @(Validation String) @Int @Int @Int x , testProperty "Apply a pure function to the second argument: (select x (f <$> y)) == (select (first (flip f) <$> x) ((&) <$> y))" $ \x -> theorem3 @(Validation String) @Int @Int @Int x , testProperty "Generalised identity: (x <*? pure y) == (either y id <$> x)" $ \x -> theorem4 @(Validation String) @Int @Int x -- 'Validation' is a non-rigid selective functor , expectFail $ testProperty "(f <*> g) == (f `apS` g)" $ \x -> theorem5 @(Validation String) @Int @Int x -- 'Validation' is a non-rigid selective functor , expectFail $ testProperty "Interchange: (x *> (y <*? z)) == ((x *> y) <*? z)" $ \x -> theorem6 @(Validation String) @Int @Int @Int x ] validationProperties :: TestTree validationProperties = testGroup "Properties" [ testProperty "pure-right: pure (Right x) <*? y = pure x" $ \x -> propertyPureRight @(Validation String) @Int @Int x , testProperty "pure-left: pure (Left x) <*? y = ($x) <$> y" $ \x -> propertyPureLeft @(Validation String) @Int @Int x ] validationExample :: TestTree validationExample = testGroup "validationExample" [ testProperty "shape (Success True) (Success 1) (Failure [\"width?\"]) (Failure [\"height?\"])" $ shape (Success True) (Success 1) (Failure ["width?"]) (Failure ["height?"]) == Success (Circle 1) , testProperty "shape (Success False) (Failure [\"radius?\"]) (Success 2) (Success 3)" $ shape (Success False) (Failure ["radius?"]) (Success 2) (Success 3) == Success (Rectangle 2 3) , testProperty "shape (Success False) (Failure [\"radius?\"]) (Success 2) (Failure [\"height?\"])" $ shape (Success False) (Failure ["radius?"]) (Success 2) (Failure ["height?"]) == Failure ["height?"] , testProperty "shape (Success False) (Success 1) (Failure [\"width?\"]) (Failure [\"height?\"])" $ shape (Success False) (Success 1) (Failure ["width?"]) (Failure ["height?"]) == Failure ["width?", "height?"] , testProperty "shape (Failure [\"choice?\"]) (Failure [\"radius?\"]) (Success 2) (Failure [\"height?\"])" $ shape (Failure ["choice?"]) (Failure ["radius?"]) (Success 2) (Failure ["height?"]) == Failure ["choice?"] , testProperty "twoShapes s1 s2" $ twoShapes (shape (Failure ["choice 1?"]) (Success 1) (Failure ["width 1?"]) (Success 3)) (shape (Success False) (Success 1) (Success 2) (Failure ["height 2?"])) == Failure ["choice 1?","height 2?"] ] -------------------------------------------------------------------------------- ------------------------ ArrowMonad -------------------------------------------- -------------------------------------------------------------------------------- arrowMonad :: TestTree arrowMonad = testGroup "ArrowMonad (->)" [arrowMonadLaws, arrowMonadTheorems, arrowMonadProperties] arrowMonadLaws :: TestTree arrowMonadLaws = testGroup "Laws" [ testProperty "Identity: (x <*? pure id) == (either id id <$> x)" $ \x -> lawIdentity @(ArrowMonad (->)) @Int x , testProperty "Distributivity: (pure x <*? (y *> z)) == ((pure x <*? y) *> (pure x <*? z))" $ \x -> lawDistributivity @(ArrowMonad (->)) @Int @Int x , testProperty "Associativity: take a look at tests/Laws.hs" $ \x -> lawAssociativity @(ArrowMonad (->)) @Int @Int @Int x , testProperty "select == selectM" $ \x -> lawMonad @(ArrowMonad (->)) @Int @Int x , testProperty "select == selectA" $ \x -> selectALaw @(ArrowMonad (->)) @Int @Int x ] arrowMonadTheorems :: TestTree arrowMonadTheorems = testGroup "Theorems" [ testProperty "Apply a pure function to the result: (f <$> select x y) == (select (second f <$> x) ((f .) <$> y))" $ \x -> theorem1 @(ArrowMonad (->)) @Int @Int @Int x , testProperty "Apply a pure function to the Left case of the first argument: (select (first f <$> x) y) == (select x ((. f) <$> y))" $ \x -> theorem2 @(ArrowMonad (->)) @Int @Int @Int x , testProperty "Apply a pure function to the second argument: (select x (f <$> y)) == (select (first (flip f) <$> x) ((&) <$> y))" $ \x -> theorem3 @(ArrowMonad (->)) @Int @Int @Int x , testProperty "Generalised identity: (x <*? pure y) == (either y id <$> x)" $ \x -> theorem4 @(ArrowMonad (->)) @Int @Int x , testProperty "(f <*> g) == (f `apS` g)" $ \x -> theorem5 @(ArrowMonad (->)) @Int @Int x , testProperty "Interchange: (x *> (y <*? z)) == ((x *> y) <*? z)" $ \x -> theorem6 @(ArrowMonad (->)) @Int @Int @Int x ] arrowMonadProperties :: TestTree arrowMonadProperties = testGroup "Properties" [ testProperty "pure-right: pure (Right x) <*? y = pure x" $ \x -> propertyPureRight @(ArrowMonad (->)) @Int @Int x , testProperty "pure-left: pure (Left x) <*? y = ($x) <$> y" $ \x -> propertyPureLeft @(ArrowMonad (->)) @Int @Int x ] -------------------------------------------------------------------------------- ------------------------ Maybe ------------------------------------------------- -------------------------------------------------------------------------------- maybe :: TestTree maybe = testGroup "Maybe" [maybeLaws, maybeTheorems, maybeProperties] maybeLaws :: TestTree maybeLaws = testGroup "Laws" [ testProperty "Identity: (x <*? pure id) == (either id id <$> x)" $ \x -> lawIdentity @Maybe @Int x , testProperty "Distributivity: (pure x <*? (y *> z)) == ((pure x <*? y) *> (pure x <*? z))" $ \x -> lawDistributivity @Maybe @Int @Int x , testProperty "Associativity: take a look at tests/Laws.hs" $ \x -> lawAssociativity @Maybe @Int @Int @Int x , testProperty "select == selectM" $ \x -> lawMonad @Maybe @Int @Int x ] maybeTheorems :: TestTree maybeTheorems = testGroup "Theorems" [ testProperty "Apply a pure function to the result: (f <$> select x y) == (select (second f <$> x) ((f .) <$> y))" $ \x -> theorem1 @Maybe @Int @Int @Int x , testProperty "Apply a pure function to the Left case of the first argument: (select (first f <$> x) y) == (select x ((. f) <$> y))" $ \x -> theorem2 @Maybe @Int @Int @Int x , testProperty "Apply a pure function to the second argument: (select x (f <$> y)) == (select (first (flip f) <$> x) ((&) <$> y))" $ \x -> theorem3 @Maybe @Int @Int @Int x , testProperty "Generalised identity: (x <*? pure y) == (either y id <$> x)" $ \x -> theorem4 @Maybe @Int @Int x , testProperty "(f <*> g) == (f `apS` g)" $ \x -> theorem5 @Maybe @Int @Int x , testProperty "Interchange: (x *> (y <*? z)) == ((x *> y) <*? z)" $ \x -> theorem6 @Maybe @Int @Int @Int x ] maybeProperties :: TestTree maybeProperties = testGroup "Properties" [ testProperty "pure-right: pure (Right x) <*? y = pure x" $ \x -> propertyPureRight @Maybe @Int @Int x , testProperty "pure-left: pure (Left x) <*? y = ($x) <$> y" $ \x -> propertyPureLeft @Maybe @Int @Int x ] -------------------------------------------------------------------------------- ------------------------ Identity ---------------------------------------------- -------------------------------------------------------------------------------- identity :: TestTree identity = testGroup "Identity" [identityLaws, identityTheorems, identityProperties] identityLaws :: TestTree identityLaws = testGroup "Laws" [ testProperty "Identity: (x <*? pure id) == (either id id <$> x)" $ \x -> lawIdentity @Identity @Int x , testProperty "Distributivity: (pure x <*? (y *> z)) == ((pure x <*? y) *> (pure x <*? z))" $ \x -> lawDistributivity @Identity @Int @Int x , testProperty "Associativity: take a look at tests/Laws.hs" $ \x -> lawAssociativity @Identity @Int @Int @Int x , testProperty "select == selectM" $ \x -> lawMonad @Identity @Int @Int x ] identityTheorems :: TestTree identityTheorems = testGroup "Theorems" [ testProperty "Apply a pure function to the result: (f <$> select x y) == (select (second f <$> x) ((f .) <$> y))" $ \x -> theorem1 @Identity @Int @Int @Int x , testProperty "Apply a pure function to the Left case of the first argument: (select (first f <$> x) y) == (select x ((. f) <$> y))" $ \x -> theorem2 @Identity @Int @Int @Int x , testProperty "Apply a pure function to the second argument: (select x (f <$> y)) == (select (first (flip f) <$> x) ((&) <$> y))" $ \x -> theorem3 @Identity @Int @Int @Int x , testProperty "Generalised identity: (x <*? pure y) == (either y id <$> x)" $ \x -> theorem4 @Identity @Int @Int x , testProperty "(f <*> g) == (f `apS` g)" $ \x -> theorem5 @Identity @Int @Int x , testProperty "Interchange: (x *> (y <*? z)) == ((x *> y) <*? z)" $ \x -> theorem6 @Identity @Int @Int @Int x ] identityProperties :: TestTree identityProperties = testGroup "Properties" [ testProperty "pure-right: pure (Right x) <*? y = pure x" $ \x -> propertyPureRight @Identity @Int @Int x , testProperty "pure-left: pure (Left x) <*? y = ($x) <$> y" $ \x -> propertyPureLeft @Identity @Int @Int x ]