{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE RecursiveDo #-} module Reflex.Test.Micro (testCases) where import Reflex import Reflex.TestPlan import Control.Applicative import Control.Monad import Control.Monad.Fix import Data.Char import Data.Foldable import Data.Functor.Misc import qualified Data.Map as Map import Data.Monoid import Prelude pushDyn :: (Reflex t, MonadHold t m) => (a -> PushM t b) -> Dynamic t a -> m (Dynamic t b) pushDyn f d = buildDynamic (sample (current d) >>= f) (pushAlways f (updated d)) foldedDyn :: (Reflex t, MonadHold t m) => (a -> a -> a) -> Dynamic t a -> m (Dynamic t a) foldedDyn f d = fmap join $ flip buildDynamic never $ do a <- sample (current d) foldDyn f a (updated d) scannedDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> m (Dynamic t [a]) scannedDyn = fmap (fmap reverse) . foldedDyn (<>) . fmap pure scanInnerDyns :: (Reflex t, MonadHold t m) => Dynamic t (Dynamic t a) -> m (Dynamic t [a]) scanInnerDyns d = do scans <- scannedDyn d return (join (fmap distributeListOverDynPure scans)) {-# ANN testCases "HLint: ignore Functor law" #-} testCases :: [(String, TestCase)] testCases = [ testB "hold" $ hold "0" =<< events1 , testB "count" $ do b <- current <$> (count =<< events2) return $ (+ (0::Int)) <$> b , testB "pull-1" $ do b <- hold "0" =<< events1 return (pull $ sample $ pull $ sample b) , testB "pull-2" $ do b1 <- behavior1 return (pull $ liftA2 (<>) (sample b1) (sample b1)) , testB "pull-3" $ do b1 <- behavior1 b2 <- behavior2 return (pull $ liftA2 (<>) (sample b1) (sample b2)) , testB "pull-4" $ do es <- planList ["a", "b", "c"] e <- plan [(0, ())] b <- hold (constant "") $ pushAlways (const $ hold "z" es) e return (join b) , testE "id" $ do events2 , testE "fmap-id" $ do e <- events2 return $ fmap id e , testE "tag-1" $ do b1 <- behavior1 e <- events2 return (tag b1 e) , testE "tag-2" $ do b1 <- behavior1 e <- events2 return (tag (map toUpper <$> b1) e) , testE "attach-1" $ do b1 <- behavior1 e <- events2 return (attachWith (++) (map toUpper <$> b1) e) , testE "leftmost" $ liftA2 leftmost2 events1 events2 , testE "appendEvents-1" $ liftA2 mappend events1 events2 , testE "appendEvents-2" $ liftA2 mappend events3 events2 , testE "merge-1" $ do e <- events1 return $ leftmost ["x" <$ e, "y" <$ e] , testE "merge-2" $ do e <- events1 let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)] let ee = flip pushAlways e $ const $ return m return $ coincidence ee , testE "headE-1" $ do e <- events1 headE $ leftmost [e, e] , testE "headE-2" $ do e <- events1 b <- hold never (e <$ e) headE $ switch b , testE "switch-1" $ do e <- events1 b <- hold never (e <$ e) return $ switch b , testE "switch-2" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ do switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) (e <$ e) , testE "switch-3" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ do switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) never , testE "switch-4" $ do e <- events1 switch <$> hold (deep e) (e <$ e) , testE "switch-5" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ return $ leftmost ["x" <$ e, "y" <$ e, "z" <$ e] , testE "switch-6" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ do switch <$> hold ("x" <$ e) (e <$ e) , testE "switchHoldPromptly-1" $ do e <- events1 let e' = e <$ e switchHoldPromptly never $ e <$ e' , testE "switchHoldPromptly-2" $ do e <- events1 switchHoldPromptly never $ deep (e <$ e) , testE "switchHoldPromptly-3" $ do e <- events1 switchHoldPromptly never $ (e <$ deep e) , testE "switchHoldPromptly-4" $ do e <- events1 switchHoldPromptly never $ (deep e <$ e) , testE "switch-5" $ do e <- events1 switch <$> hold never (deep e <$ e) , testE "switchHoldPromptly-5" $ do e <- events1 switchHoldPromptly never $ flip push e $ const (Just <$> headE e) , testE "switchHoldPromptly-6" $ do e <- events1 switchHoldPromptly never $ flip pushAlways e $ const (switchHoldPromptly e never) , testE "coincidence-1" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ return e , testE "coincidence-2" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ return (deep e) , testE "coincidence-3" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ return (coincidence (e <$ e)) , testE "coincidence-4" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const (headE e) , testE "coincidence-5" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ do let e' = deep e return (coincidence (e' <$ e')) , testE "coincidence-6" $ do e <- events1 return $ coincidence $ flip pushAlways e $ const $ do let e' = coincidence (e <$ e) return $ deep e' , testE "coincidence-7" $ do e <- events1 return $ coincidence (deep e <$ e) , testB "holdWhileFiring" $ do e <- events1 eo <- headE e bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo return $ pull $ sample =<< sample bb , testB "foldDynWhileFiring" $ do e <- events1 d <- foldDyn (:) [] $ pushAlways (\a -> foldDyn (:) [a] e) e return $ current (join (fmap distributeListOverDynPure d)) , testE "joinDyn" $ do e <- events1 bb <- hold "b" e bd <- hold never . fmap (const e) =<< headE e eOuter <- pushAlways sample . fmap (const bb) <$> headE e let eInner = switch bd return $ leftmost [eOuter, eInner] , testB "foldDyn" $ do d <- foldDyn (++) "0" =<< events1 return (current d) , testB "mapDyn" $ do d <- foldDyn (++) "0" =<< events1 return $ current $ fmap (map toUpper) d , testB "combineDyn" $ do d1 <- foldDyn (++) "0" =<< events1 d2 <- fmap (fmap (map toUpper)) $ foldDyn (++) "0" =<< events2 return $ current $ zipDynWith (<>) d1 d2 , testB "buildDynamicStrictness" $ do rec d'' <- pushDyn return d' d' <- pushDyn return d d <- holdDyn "0" =<< events1 _ <- sample (current d'') return (current d'') , testB "factorDyn" $ do d <- holdDyn (Left "a") =<< eithers eithers' <- eitherDyn d let unFactor = either id id return $ current (join (fmap unFactor eithers')) , testB "pushDynDeep" $ do _ <- events1 _ <- events2 d1 <- holdDyn "d1" =<< events1 d2 <- holdDyn "d2" =<< events2 d <- flip pushDyn d1 $ \a -> flip pushDyn d2 $ \b -> flip pushDyn d1 $ \c -> return (a <> b <> c) d' <- pushDyn scanInnerDyns d >>= scanInnerDyns return $ current d' , testE "fan-1" $ do e <- fmap toMap <$> events1 let es = select (fanMap e) . Const2 <$> values return (mergeList es) , testE "fan-2" $ do e <- fmap toMap <$> events3 let es = select (fanMap e) . Const2 <$> values return (mergeList es) , testE "fan-3" $ do f <- fanMap . fmap toMap <$> events3 return $ select f (Const2 'c') , testE "fan-4" $ do e <- fmap toMap <$> events1 return $ toUpper <$> select (fanMap e) (Const2 'a') , testE "fan-5" $ do e <- fmap toMap <$> events2 return $ toUpper <$> select (fanMap e) (Const2 'c') , testE "fan-6" $ do f <- fanMap . fmap toMap <$> events1 return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ] , testE "difference" $ do e1 <- events1 e2 <- events2 return $ e1 `difference ` e2 , testE "lazy-hold" $ do let lazyHold :: forall t m. (Reflex t, MonadHold t m, MonadFix m) => m (Event t ()) lazyHold = do rec !b <- hold never e let e = never <$ switch b return $ void e lazyHold ] where events1, events2, events3 :: TestPlan t m => m (Event t String) events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")] events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")] events3 = liftA2 mappend events1 events2 eithers :: TestPlan t m => m (Event t (Either String String)) eithers = plan [(1, Left "e"), (3, Left "d"), (4, Right "c"), (6, Right "b"), (7, Left "a")] values = "abcde" toMap str = Map.fromList $ map (\c -> (c, c)) str behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String) behavior1 = hold "1" =<< events1 behavior2 = hold "2" =<< events2 deep e = leftmost [e, e] leftmost2 e1 e2 = leftmost [e1, e2]