{-# LANGUAGE ScopedTypeVariables #-} module Main(main) where import Safe import Safe.Exact import qualified Safe.Foldable as F import Control.DeepSeq import Control.Exception import Control.Monad import Data.Char import Data.List import Data.Maybe import System.IO.Unsafe import Test.QuickCheck.Test import Test.QuickCheck hiding ((===)) --------------------------------------------------------------------- -- TESTS main :: IO () main = do -- All from the docs, so check they match tailMay dNil === Nothing tailMay [1,3,4] === Just [3,4] tailDef [12] [] === [12] tailDef [12] [1,3,4] === [3,4] tailNote "help me" dNil `err` "Safe.tailNote [], help me" tailNote "help me" [1,3,4] === [3,4] tailSafe [] === dNil tailSafe [1,3,4] === [3,4] findJust (== 2) [d1,2,3] === 2 findJust (== 4) [d1,2,3] `err` "Safe.findJust" F.findJust (== 2) [d1,2,3] === 2 F.findJust (== 4) [d1,2,3] `err` "Safe.Foldable.findJust" F.findJustDef 20 (== 4) [d1,2,3] === 20 F.findJustNote "my note" (== 4) [d1,2,3] `errs` ["Safe.Foldable.findJustNote","my note"] takeExact 3 [d1,2] `errs` ["Safe.Exact.takeExact","index=3","length=2"] takeExact (-1) [d1,2] `errs` ["Safe.Exact.takeExact","negative","index=-1"] takeExact 1 (takeExact 3 [d1,2]) === [1] -- test is lazy quickCheck_ $ \(Int10 i) (List10 (xs :: [Int])) -> do let (t,d) = splitAt i xs let good = length t == i let f name exact may note res = if good then do exact i xs === res note "foo" i xs === res may i xs === Just res else do exact i xs `err` ("Safe.Exact." ++ name ++ "Exact") note "foo" i xs `errs` ["Safe.Exact." ++ name ++ "ExactNote","foo"] may i xs === Nothing f "take" takeExact takeExactMay takeExactNote t f "drop" dropExact dropExactMay dropExactNote d f "splitAt" splitAtExact splitAtExactMay splitAtExactNote (t, d) return True take 2 (zipExact [1,2,3] [1,2]) === [(1,1),(2,2)] zipExact [d1,2,3] [d1,2] `errs` ["Safe.Exact.zipExact","first list is longer than the second"] zipExact [d1,2] [d1,2,3] `errs` ["Safe.Exact.zipExact","second list is longer than the first"] zipExact dNil dNil === [] predMay (minBound :: Int) === Nothing succMay (maxBound :: Int) === Nothing predMay ((minBound + 1) :: Int) === Just minBound succMay ((maxBound - 1) :: Int) === Just maxBound quickCheck_ $ \(List10 (xs :: [Int])) x -> do let ys = maybeToList x ++ xs let res = zip xs ys let f name exact may note = if isNothing x then do exact xs ys === res note "foo" xs ys === res may xs ys === Just res else do exact xs ys `err` ("Safe.Exact." ++ name ++ "Exact") note "foo" xs ys `errs` ["Safe.Exact." ++ name ++ "ExactNote","foo"] may xs ys === Nothing f "zip" zipExact zipExactMay zipExactNote f "zipWith" (zipWithExact (,)) (zipWithExactMay (,)) (`zipWithExactNote` (,)) return True take 2 (zip3Exact [1,2,3] [1,2,3] [1,2]) === [(1,1,1),(2,2,2)] zip3Exact [d1,2] [d1,2,3] [d1,2,3] `errs` ["Safe.Exact.zip3Exact","first list is shorter than the others"] zip3Exact [d1,2,3] [d1,2] [d1,2,3] `errs` ["Safe.Exact.zip3Exact","second list is shorter than the others"] zip3Exact [d1,2,3] [d1,2,3] [d1,2] `errs` ["Safe.Exact.zip3Exact","third list is shorter than the others"] zip3Exact dNil dNil dNil === [] quickCheck_ $ \(List10 (xs :: [Int])) x1 x2 -> do let ys = maybeToList x1 ++ xs let zs = maybeToList x2 ++ xs let res = zip3 xs ys zs let f name exact may note = if isNothing x1 && isNothing x2 then do exact xs ys zs === res note "foo" xs ys zs === res may xs ys zs === Just res else do exact xs ys zs `err` ("Safe.Exact." ++ name ++ "Exact") note "foo" xs ys zs `errs` ["Safe.Exact." ++ name ++ "ExactNote","foo"] may xs ys zs === Nothing f "zip3" zip3Exact zip3ExactMay zip3ExactNote f "zipWith3" (zipWith3Exact (,,)) (zipWith3ExactMay (,,)) (flip zipWith3ExactNote (,,)) return True --------------------------------------------------------------------- -- UTILITIES quickCheck_ prop = do r <- quickCheckResult prop unless (isSuccess r) $ error "Test failed" d1 = 1 :: Double dNil = [] :: [Double] (===) :: (Show a, Eq a) => a -> a -> IO () (===) a b = when (a /= b) $ error $ "Mismatch: " ++ show a ++ " /= " ++ show b err :: NFData a => a -> String -> IO () err a b = errs a [b] errs :: NFData a => a -> [String] -> IO () errs a bs = do res <- try $ evaluate $ rnf a case res of Right v -> error $ "Expected error, but succeeded: " ++ show bs Left (msg :: SomeException) -> forM_ bs $ \b -> do let s = show msg unless (b `isInfixOf` s) $ error $ "Invalid error string, got " ++ show s ++ ", want " ++ show b let f xs = " " ++ map (\x -> if sepChar x then ' ' else x) xs ++ " " unless (f b `isInfixOf` f s) $ error $ "Not standalone error string, got " ++ show s ++ ", want " ++ show b sepChar x = isSpace x || x `elem` ",;." newtype Int10 = Int10 Int deriving Show instance Arbitrary Int10 where arbitrary = fmap Int10 $ choose (-3, 10) newtype List10 a = List10 [a] deriving Show instance Arbitrary a => Arbitrary (List10 a) where arbitrary = do i <- choose (0, 10); fmap List10 $ vector i instance Testable a => Testable (IO a) where property = property . unsafePerformIO