packages feed

essence-of-live-coding-0.2.9: test/Cell/Util.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE Arrows #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

module Cell.Util where

-- base
import qualified Control.Category as C
import Control.Monad
import Data.Functor.Identity
import Data.List
import Data.Maybe
import GHC.TypeLits (KnownNat)

-- containers
import Data.Map (Map)
import Data.Sequence (Seq)

-- transformers
import Control.Monad.Trans.Reader
import Control.Monad.Trans.State.Lazy

-- vector-sized
import qualified Data.Vector.Sized as V

-- selective
import Control.Selective

-- test-framework
import Test.Framework

-- test-framework-quickcheck2
import Test.Framework.Providers.QuickCheck2

-- QuickCheck
import Test.QuickCheck hiding (output)

-- essence-of-live-coding
import LiveCoding

import Util

test =
  testGroup
    "Utility unit tests"
    --   [ testProperty "Buffer works as expected" CellSimulation
    --       { cell = buffer
    --       , input = []
    --     --   , input =
    --     --       [ []
    --     --       , [Pop]
    --     --       , [Push (23 :: Int)]
    --     --       , []
    --     --       , [Pop, Pop]
    --     --       , [Push 42, Pop]
    --     --       , [Push 1, Push 2]
    --     --       , []
    --     --       , []
    --     --       , [Pop, Push 3]
    --     --       , []
    --     --       , [Pop]
    --     --       , [Pop]
    --     --       , []
    --     --       ]
    --       , output = []
    --     --   , output =
    --     --       [ Nothing
    --     --       , Nothing
    --     --       , Just 23
    --     --       , Just 23
    --     --       , Nothing
    --     --       , Nothing
    --     --       , Just 1
    --     --       , Just 1
    --     --       , Just 1
    --     --       , Just 2
    --     --       , Just 2
    --     --       , Just 3
    --     --       , Nothing
    --     --       , Nothing
    --     --       ]
    --       }
    [ testProperty
        "buffered works as expected"
        CellSimulation
          { cell = buffered C.id
          , input =
              [ Just (23 :: Int)
              ]
          , output =
              [ Nothing
              ]
          }
    , testProperty "buffered can be used in an asynchronous setting" $ do
        jointInputs <- arbitrary -- Simulates when input arrives and when inner cell is activated
        -- Make sure cell is ticked at the last time, and no new input arrives
        let innerCell = proc (aMaybe :: Maybe Int) -> do
              isScheduled <- constM ask -< ()
              returnA -< guard isScheduled >> aMaybe
            outerCell = buffered innerCell
            (outputs, _) = runIdentity $ steps (runReaderC' outerCell) jointInputs
            labelString = unwords [show jointInputs, show outputs, show $ length jointInputs, show $ length outputs]
            inputs = snd $ unzip jointInputs
            bufferNotEmpty = isJust $ listToMaybe $ reverse outputs
        -- Make sure each message arrived exactly once, in order
        return $
          counterexample labelString $
            catMaybes inputs
              === catMaybes outputs
              .||. bufferNotEmpty
    , testProperty "delay a >>> changes >>> hold a = delay a" $
        \(inputs :: [Int]) (startValue :: Int) ->
          CellIdentitySimulation (delay startValue) (delay startValue >>> changes >>> hold startValue) inputs
    , testProperty "changes applied to a cell that outputs a constant, always outputs Nothing" $
        \(value :: Int) (inputs :: [Int]) ->
          []
            === catMaybes (fst (runIdentity $ steps (arr (const value) >>> changes) inputs))
    , testProperty
        "changes works as expected"
        CellSimulation
          { cell = changes
          , input =
              [ 1 :: Int
              , 1 :: Int
              , 2 :: Int
              , 2 :: Int
              ]
          , output =
              [ Nothing
              , Nothing
              , Just (2 :: Int)
              , Nothing
              ]
          }
    , testProperty
        "changes migrates correctly to itself"
        CellMigrationSimulation
          { cell1 = changes
          , cell2 = changes
          , input1 = [1, 2] :: [Int]
          , input2 = [3, 4] :: [Int]
          , output1 = [Nothing, Just 2]
          , output2 = [Just 3, Just 4]
          }
    , testProperty
        "delay migrates correctly to itself"
        CellMigrationSimulation
          { cell1 = LiveCoding.delay 0
          , cell2 = LiveCoding.delay 0
          , input1 = [1 :: Int, 2, 3, 4]
          , input2 = [5 :: Int, 6, 7, 8]
          , output1 = [0, 1, 2, 3]
          , output2 = [4, 5, 6, 7]
          }
    , testProperty
        "delay migrates correctly with original type wrapped in data type with single constructor"
        CellMigrationSimulation
          { cell1 = LiveCoding.delay 0 :: Cell Identity Int Int
          , cell2 = arr Stuff >>> LiveCoding.delay (Stuff 99) >>> arr (\(Stuff a) -> a) :: Cell Identity Int Int
          , input1 = [1, 2, 3, 4] :: [Int]
          , input2 = [10, 10, 10, 10] :: [Int]
          , output1 = [0, 1, 2, 3] :: [Int]
          , output2 = [4, 10, 10, 10] :: [Int]
          }
    , testProperty "resampleListPar works as expected" $
        forAll (vector 100) $
          \(inputs :: [(Int, Int)]) ->
            let
              inputs' = fmap pairToList inputs
              pairToList :: (a, a) -> [a]
              pairToList (x, y) = [x, y]
             in
              CellSimulation
                { cell = resampleListPar (sumC :: Cell Identity Int Int)
                , input = inputs'
                , output = fmap sum . transpose <$> [[0 :: Int, 0]] : tail (inits (init inputs'))
                }
    , testProperty
        "resampleListPar grow"
        CellSimulation
          { cell = resampleListPar (sumC :: Cell Identity Int Int)
          , input = [[1, 1, 1], [1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1, 1]]
          , output = [[0, 0, 0], [1, 1, 1], [2, 2, 2, 0], [3, 3, 3, 1], [4, 4, 4, 2, 0]]
          }
    , testProperty
        "resampleListPar shrink"
        CellSimulation
          { cell = resampleListPar (sumC :: Cell Identity Int Int)
          , input = [[1, 1, 1], [1, 1, 1], [1, 1], [1, 1], [1], []]
          , output = [[0, 0, 0], [1, 1, 1], [2, 2], [3, 3], [4], []]
          }
    , testProperty
        "resampleListPar grow then shrink"
        CellSimulation
          { cell = resampleListPar (sumC :: Cell Identity Int Int)
          , input = [[1, 1, 1], [1, 1, 1, 1], [1, 1, 1]]
          , output = [[0, 0, 0], [1, 1, 1, 0], [2, 2, 2]]
          }
    , testProperty
        "resampleListPar shrink then grow"
        CellSimulation
          { cell = resampleListPar (sumC :: Cell Identity Int Int)
          , input = [[1, 1, 1], [1, 1], [1, 1, 1]]
          , output = [[0, 0, 0], [1, 1], [2, 2, 0]]
          }
    , testProperty
        "Selective instance is sound"
        CellSimulation
          { cell = select C.id (pure length) :: Cell Identity (Either [()] Int) Int
          , input = [Right 42, Left [(), ()]]
          , output = [42, 2]
          }
    ]

data Stuff a = Stuff a deriving (Eq, Data)