packages feed

smarties-1.2.1: test/unit/SmartiesSpec.hs

{-# OPTIONS_GHC -fno-warn-missing-signatures #-}
{-# OPTIONS_GHC -fno-warn-type-defaults #-}
{-# LANGUAGE TemplateHaskell #-}

-- NOTE these tests are almost identical to the ones in SmartiesSpec
-- I wish there was a nice way to reuse code here :\

module SmartiesSpec where

import           Prelude                hiding (sequence)

import           Test.Hspec
import           Test.QuickCheck

import           Control.Monad
import           Control.Monad.Identity (runIdentity)
import           Data.Function
import           Data.List
import           Data.Maybe             (fromMaybe)
import           System.Random

import           Smarties

data BranchType = BrSelector | BrSequence | BrNot deriving (Show)

instance Arbitrary BranchType where
  arbitrary = oneof (fmap return [BrSelector, BrSequence])

data Tree = Leaf Bool | Branch BranchType [Tree] deriving (Show)

-- TODO find better formula for number of branches/children
instance Arbitrary Tree where
  arbitrary = sized tree' where
    tree' 0 = liftM Leaf arbitrary
    tree' n = frequency [
        (1, liftM Leaf arbitrary)
        , (1, liftM2 Branch (return BrNot) subtree2)
        , (3, liftM2 Branch arbitrary subtree)
      ]
      where
        subtree = replicateM n $ tree' (n `div` 2)
        subtree2 = replicateM 1 $ tree' (n `div` 2)


-- for these examples, the output is designed to operate on the perception
type GeneratorType = ()
type PerceptionType = Int
type OutputType = Int -> Int

-- | action that increases perception by n (used for tracking which node was executed)
addAction :: Int -> NodeSequence GeneratorType PerceptionType OutputType ()
addAction n = fromAction $ SimpleAction (\_ -> (+n))

-- | adds an action that sets perception to n (used for tracking which node was executed)
setAction :: Int -> NodeSequence g PerceptionType OutputType ()
setAction n = fromPerception $ SimplePerception (const n)

prop_selector_basic :: Bool -> Bool
prop_selector_basic b = let
  tree = selector [result FAIL, result FAIL, result FAIL, result FAIL, result FAIL, result FAIL, result FAIL, result (if b then SUCCESS else FAIL)]
  (_,_,_,s,_) = runIdentity $ (runNodeSequenceT tree) () ()
  in if b then s == SUCCESS else s == FAIL


mostCommon :: Ord a => [a] -> a
mostCommon = head . maximumBy (compare `on` length) . group . sort

test_weightedSelector :: Expectation
test_weightedSelector = mostCommon as `shouldBe` cnt where
  cnt = 10
  geta (a,_,_,_,_) = a
  tree = do
    weightedSelector $ map (\n-> (n, setAction n)) [0..cnt]
    getPerception
  as = geta $ runNodeSequence (forM [0..1000] (\_ -> tree)) (mkStdGen 0) 0

prop_utilitySelector :: [Int] -> Bool
prop_utilitySelector w = r where
  tree = utilitySelector $ map (\n-> addAction n >> return (w!!n)) [0..(length w -1)]
  (_,p,s,os) = execNodeSequence tree () 0
  rslt = reduce os p
  r = if length w == 0
    then s == FAIL
    else rslt == fromMaybe (-1) (findIndex (maximum w ==) w)

test_utilityWeightedSelector :: Expectation
test_utilityWeightedSelector = mostCommon as `shouldBe` cnt where
  cnt = 10
  geta (a,_,_,_,_) = a
  tree = do
    _ <- utilityWeightedSelector $ map (\n-> (setAction n >> return n)) [0..cnt]
    getPerception
  as = geta $ runNodeSequence (forM [0..1000] (\_ -> tree)) (mkStdGen 0) 0

prop_addition :: Int -> (NonNegative Int) -> Bool
prop_addition a (NonNegative b) = execNodeSequenceTimesFinalize b (addAction a) () 0 == a*b

prop_addition_sequence :: Int -> (NonNegative Int) -> Bool
prop_addition_sequence a (NonNegative b) = (reduce os p == a*b) && s == FAIL where
  (_,p,s,os) = execNodeSequence tree () 0
  tree = do
    forM_ [0..(b-1)] (\_->addAction a)
    -- execution should stop here
    result FAIL
    forM_ [0..100] (\_->addAction a)

-- hspec nonsense
spec = do
  describe "selector" $
    it "satisfies basic property test" $
      property prop_selector_basic
  describe "utilitySelector" $
    it "satisfies basic property test" $
      property prop_utilitySelector
  describe "weightedSelector" $
    it "passes basic tests" $
      test_weightedSelector
  describe "utilityWeightedSelector" $
    it "passes basic tests" $
      test_utilityWeightedSelector
  describe "other" $ do
    it "passes addition property test" $
      property prop_addition
    it "passes addition in sequence property test" $
      property prop_addition_sequence