File: TestUtil.hs

package info (click to toggle)
haskell-extra 1.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 280 kB
  • sloc: haskell: 1,902; makefile: 2
file content (103 lines) | stat: -rw-r--r-- 2,965 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
{-# LANGUAGE ScopedTypeVariables, FlexibleInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-} -- OK because a test module

module TestUtil
    (runTests
    ,testGen, testRaw
    ,erroneous, erroneousIO
    ,(====), (==>)
    ,ASCIIString(..)
    ,module X
    ) where

import Test.QuickCheck
import System.IO.Unsafe
import Text.Show.Functions()

import Control.Concurrent.Extra as X
import Control.Exception.Extra as X
import Control.Monad.Extra as X
import Data.Char as X
import Data.Either.Extra as X
import Data.Function as X
import Data.IORef.Extra as X
import Data.List.Extra as X hiding (union, unionBy)
import Data.List.NonEmpty.Extra as X (NonEmpty(..), (|>), (|:), appendl, appendr, union, unionBy)
import Data.Maybe as X
import Data.Monoid.Extra as X
import Data.Tuple.Extra as X
import Data.Version.Extra as X
import Numeric.Extra as X
import System.Directory.Extra as X
import System.FilePath as X
import System.Info.Extra as X
import System.IO.Extra as X
import System.Process.Extra as X
import System.Time.Extra as X


{-# NOINLINE testCount #-}
testCount :: IORef Int
testCount = unsafePerformIO $ newIORef 0

testGen :: Testable prop => String -> prop -> IO ()
testGen msg prop = testRaw msg $ do
    r <- quickCheckResult prop
    case r of
        Success{} -> pure ()
        _ -> errorIO "Test failed"

testRaw :: String -> IO () -> IO ()
testRaw msg test = do
    putStrLn msg
    test
    modifyIORef testCount (+1)


erroneous :: Show a => a -> Bool
erroneous x = unsafePerformIO $ fmap isLeft $ try_ $ evaluate $ length $ show x

erroneousIO :: Show a => IO a -> Bool
erroneousIO x = unsafePerformIO $ fmap isLeft $ try_ $ evaluate . length . show =<< x

(====) :: (Show a, Eq a) => a -> a -> Bool
a ==== b
    | a == b = True
    | otherwise = error $ "Not equal!\n" ++ show a ++ "\n" ++ show b

runTests :: IO () -> IO ()
runTests t = do
    -- ensure that capturing output is robust
    hSetBuffering stdout NoBuffering
    hSetBuffering stderr NoBuffering
    writeIORef testCount 0
    t
    n <- readIORef testCount
    putStrLn $ "Success (" ++ show n ++ " tests)"

instance Testable a => Testable (IO a) where
    property = property . unsafePerformIO

-- We only use this property to assert equality as a property
-- And the Show instance is useless (since it may be non-deterministic)
-- So we print out full information on failure
instance (Show a, Eq a) => Eq (IO a) where
    a == b = unsafePerformIO $ do
        a <- captureOutput $ try_ a
        b <- captureOutput $ try_ b
        if a == b then pure True else
            error $ show ("IO values not equal", a, b)

instance Show (IO a) where
    show _ = "<<IO>>"

instance Arbitrary a => Arbitrary (IO a) where
    arbitrary = do
        (prnt :: Maybe Int, thrw :: Maybe Int, res) <- arbitrary
        pure $ do
            whenJust prnt print
            whenJust thrw (fail . show)
            pure res

instance Eq SomeException where
    a == b = show a == show b