packages feed

AlanDeniseEricLauren-0.1.0.0: test/Main.hs

{-# LANGUAGE FlexibleInstances, ScopedTypeVariables, TupleSections,TypeFamilies #-}
import Control.Exception (ErrorCall(..), try)
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Random (evalRand, Rand, RandT)
import Data.Functor.Identity (Identity)
import Data.IORef (newIORef, readIORef, writeIORef)
import Data.List (sort)
import qualified Data.Map.Strict as M
import Data.Maybe (fromJust, isNothing)
import Data.Ord (Down(..))
import System.Random (getStdGen, mkStdGen, RandomGen, StdGen)
import Test.Hspec
import Test.Hspec.Core.Spec
import Test.Hspec.QuickCheck
import Test.QuickCheck

import ADEL

main :: IO ()
main = hspec $ do
    describe "minimalSubmapSatisfying" $ do
        before getStdGen $ do
            it "finds the minimal submap of empty maps" $ idRand $ do
                res <- minimalSubmapSatisfying
                    (makeSizedMap 0)
                    (const (return True))
                return (res `shouldBe` M.empty)
            it "finds a subset of size 5" $ idRand $ do
                res <- minimalSubmapSatisfying
                    (makeSizedMap 200)
                    (\m -> return $ M.size m >= 5)
                return (res `shouldSatisfy` (\m -> M.size m == 5))
        prop "finds a subset of arbitrary size" $
            \sizeA sizeB -> idRand $ do
                let [NonNegative smaller, NonNegative bigger] =
                        sort [sizeA, sizeB]
                submap <- minimalSubmapSatisfying
                    (makeSizedMap bigger)
                    (\m -> return $ M.size m >= smaller)
                return $ M.size submap == smaller
        prop "finds a specific arbitrary subset" $
            \nums -> not (null nums) ==> idRand $ do
                let nums' :: [Int] = map (\(Down n) -> n) $ sort $
                        map (Down . getPositive) nums
                    bigSet = makeSizedMap (head nums')
                    subsetNums = M.fromList $ map (,()) $ tail nums'
                res <- minimalSubmapSatisfying
                    bigSet
                    (return . M.isSubmapOf subsetNums)
                return $ res == subsetNums
        prop
            "throws an exception if the property is not true of the argument"
            $ \size -> ioProperty $ do
                res <- try (minimalSubmapSatisfying
                           (makeSizedMap size)
                           (return . const False))
                case res of
                    Left (ErrorCall _) -> return True
                    _                  -> return False
    describe "minimalDifferenceSatisfying" $ do
        prop "finds minimal additions to an empty set"
          $ \(els :: [(Int, ())]) -> idRand $ do
            let targetSet = M.fromList els
            res <- minimalDifferenceSatisfying
              M.empty
              targetSet
              (\s -> return $ targetSet `M.isSubmapOf` s)
            return $ res == fmap Added targetSet
        prop "adds the minimal set with arbitrary false and true parameters"
          $ \(elsTrue :: [(Int, ())]) cutoff (elsFalse :: [(Int, ())]) ->
            cutoff < length elsTrue &&
            disjoint (M.fromList elsTrue) (M.fromList elsFalse) ==>
            idRand $ do
              let alreadyThere = M.fromList $ take cutoff elsTrue
                  falseSet = M.fromList elsFalse
                  trueSet = M.fromList elsTrue
                  expectedResult = fmap Added (M.difference trueSet alreadyThere)
              res <- minimalDifferenceSatisfying
                (falseSet `M.union` alreadyThere)
                (falseSet `M.union` trueSet)
                (\s -> return $ trueSet `M.isSubmapOf` s)
              return $ res === expectedResult
        prop "removes the correct arbitrary element" $
          \(els1 :: [(Int, Char)])
           (els2 :: [(Int, Char)])
           (toRemove :: (Int, Char)) -> idRand $ do
            let trueSet = M.delete (fst toRemove) (M.fromList els1)
                falseSet = uncurry M.insert toRemove $ M.fromList els2
            res <- minimalDifferenceSatisfying
              falseSet
              trueSet
              (return . isNothing . M.lookup (fst toRemove))
            return (res === M.singleton (fst toRemove) Removed)

disjoint :: (Ord k, Eq v) => M.Map k v -> M.Map k v -> Bool
disjoint m1 m2 = M.intersection m1 m2 == M.empty

instance RandomGen g => Example (RandT g Identity Expectation) where
    type Arg (RandT g Identity Expectation) = g
    evaluateExample randEx params act progressCallback = do
        resRef <- newIORef Nothing
        act $ \gen -> writeIORef resRef $ Just $ evalRand randEx gen
        res <- fromJust <$> readIORef resRef
        evaluateExample res params (\act' -> act' ()) progressCallback

instance Arbitrary StdGen where
    arbitrary = mkStdGen <$> arbitrary

idRand :: Rand StdGen a -> Rand StdGen a
idRand = id

makeSizedMap :: Int -> M.Map Int ()
makeSizedMap 0 = M.empty
makeSizedMap n | n > 0 = M.insert n () $ makeSizedMap (n-1)
               | otherwise = error "makeSizedMap negative"

instance Testable prop => Testable (Rand StdGen prop) where
    property randAct = property $ \g -> evalRand randAct g