File: Suite.hs

package info (click to toggle)
haskell-deepseq-generics 0.2.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 68 kB
  • sloc: haskell: 135; makefile: 3
file content (127 lines) | stat: -rw-r--r-- 3,830 bytes parent folder | download
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
{-# LANGUAGE CPP, TupleSections, DeriveGeneric #-}

module Main (main) where

import Control.Concurrent.MVar
import Control.DeepSeq
import Control.Exception
import Control.Monad
import Data.Bits
import Data.IORef
import Data.Word
import GHC.Generics
import System.IO.Unsafe (unsafePerformIO)

-- import Test.Framework (defaultMain, testGroup, testCase)
import Test.Framework
import Test.Framework.Providers.HUnit
import Test.HUnit

-- IUT
import Control.DeepSeq.Generics

----------------------------------------------------------------------------
-- simple hacky abstraction for testing forced evaluation via `rnf`-like functions

seqStateLock :: MVar ()
seqStateLock = unsafePerformIO $ newMVar ()
{-# NOINLINE seqStateLock #-}

withSeqState :: Word64 -> IO () -> IO ()
withSeqState expectedState act = withMVar seqStateLock $ \() -> do
    0  <- resetSeqState
    () <- act
    st <- resetSeqState
    unless (st == expectedState) $
        assertFailure ("withSeqState: actual seq-state ("++show st++") doesn't match expected value ("++
                       show expectedState++")")

seqState :: IORef Word64
seqState = unsafePerformIO $ newIORef 0
{-# NOINLINE seqState #-}

resetSeqState :: IO Word64
resetSeqState = atomicModifyIORef' seqState (0,)

-- |Set flag and raise exception is flag already set
setSeqState :: Int -> IO ()
setSeqState i | 0 <= i && i < 64 = atomicModifyIORef' seqState go
              | otherwise        = error "seqSeqState: flag index must be in [0..63]"
  where
    go x | testBit x i = error ("setSeqState: flag #"++show i++" already set")
         | otherwise   = (setBit x i, ())

-- weird type whose NFData instacne calls 'setSeqState' when rnf-ed
data SeqSet = SeqSet !Int | SeqIgnore
              deriving Show

instance NFData SeqSet where
    rnf (SeqSet i)  = unsafePerformIO $ setSeqState i
    rnf (SeqIgnore) = ()
    {-# NOINLINE rnf #-}

-- |Exception to be thrown for testing 'seq'/'rnf'
data RnfEx = RnfEx deriving (Eq, Show)

instance Exception RnfEx

instance NFData RnfEx where rnf e = throw e

assertRnfEx :: () -> IO ()
assertRnfEx v = handleJust isWanted (const $ return ()) $ do
    () <- evaluate v
    assertFailure "failed to trigger expected RnfEx exception"
  where isWanted = guard . (== RnfEx)

----------------------------------------------------------------------------

case_1, case_2, case_3, case_4_1, case_4_2, case_4_3, case_4_4 :: Test.Framework.Test

newtype Case1 = Case1 Int
              deriving Generic
case_1 = testCase "Case1" $ do
    assertRnfEx $ genericRnf $ (Case1 (throw RnfEx))

----

data Case2 = Case2 Int
           deriving Generic
case_2 = testCase "Case2" $ do
    assertRnfEx $ genericRnf $ (Case2 (throw RnfEx))

----

data Case3 = Case3 RnfEx
           deriving Generic
case_3 = testCase "Case3" $ do
    assertRnfEx $ genericRnf $ Case3 RnfEx

----

data Case4 a = Case4a
             | Case4b a a
             | Case4c a (Case4 a)
             deriving Generic
instance NFData a => NFData (Case4 a) where rnf = genericRnf

case_4_1 = testCase "Case4.1" $ withSeqState 0x0 $ do
    evaluate $ rnf $ (Case4a :: Case4 SeqSet)

case_4_2 = testCase "Case4.2" $ withSeqState 0x3 $ do
    evaluate $ rnf $ (Case4b (SeqSet 0) (SeqSet 1) :: Case4 SeqSet)

case_4_3 = testCase "Case4.3" $ withSeqState (bit 55) $ do
    evaluate $ rnf $ (Case4b SeqIgnore (SeqSet 55) :: Case4 SeqSet)

case_4_4 = testCase "Case4.4" $ withSeqState 0xffffffffffffffff $ do
    evaluate $ rnf $ (genCase 63)
  where
    genCase n | n > 1      = Case4c (SeqSet n) (genCase (n-1))
              | otherwise  = Case4b (SeqSet 0) (SeqSet 1)

----------------------------------------------------------------------------

main :: IO ()
main = defaultMain [tests]
  where
    tests = testGroup "" [case_1, case_2, case_3, case_4_1, case_4_2, case_4_3, case_4_4]