/tp-1/codigo/HUnitBase.lhs

http://tps-paradigmas.googlecode.com/ · Haskell · 230 lines · 152 code · 72 blank · 6 comment · 8 complexity · 5aa89e490293851f865c59cfb23939f7 MD5 · raw file

  1. HUnitBase.lhs -- basic definitions
  2. $Id: HUnitBase.lhs,v 1.12 2002/02/14 19:31:57 heringto Exp $
  3. > module HUnitBase
  4. > (
  5. > {- from HUnitLang: -} Assertion, assertFailure,
  6. > assertString, assertBool, assertEqual,
  7. > Assertable(..), ListAssertable(..),
  8. > AssertionPredicate, AssertionPredicable(..),
  9. > (@?), (@=?), (@?=),
  10. > Test(..), Node(..), Path,
  11. > testCaseCount,
  12. > Testable(..),
  13. > (~?), (~=?), (~?=), (~:),
  14. > Counts(..), State(..),
  15. > ReportStart, ReportProblem,
  16. > testCasePaths,
  17. > performTest
  18. > )
  19. > where
  20. > import Monad (unless, foldM)
  21. Assertion Definition
  22. ====================
  23. > import HUnitLang
  24. Conditional Assertion Functions
  25. -------------------------------
  26. > assertBool :: String -> Bool -> Assertion
  27. > assertBool msg b = unless b (assertFailure msg)
  28. > assertString :: String -> Assertion
  29. > assertString s = unless (null s) (assertFailure s)
  30. > assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion
  31. > assertEqual preface expected actual =
  32. > unless (actual == expected) (assertFailure msg)
  33. > where msg = (if null preface then "" else preface ++ "\n") ++
  34. > "expected: " ++ show expected ++ "\n but got: " ++ show actual
  35. Overloaded `assert` Function
  36. ----------------------------
  37. > class Assertable t
  38. > where assert :: t -> Assertion
  39. > instance Assertable ()
  40. > where assert = return
  41. > instance Assertable Bool
  42. > where assert = assertBool ""
  43. > instance (ListAssertable t) => Assertable [t]
  44. > where assert = listAssert
  45. > instance (Assertable t) => Assertable (IO t)
  46. > where assert = (>>= assert)
  47. We define the assertability of `[Char]` (that is, `String`) and leave
  48. other types of list to possible user extension.
  49. > class ListAssertable t
  50. > where listAssert :: [t] -> Assertion
  51. > instance ListAssertable Char
  52. > where listAssert = assertString
  53. Overloaded `assertionPredicate` Function
  54. ----------------------------------------
  55. > type AssertionPredicate = IO Bool
  56. > class AssertionPredicable t
  57. > where assertionPredicate :: t -> AssertionPredicate
  58. > instance AssertionPredicable Bool
  59. > where assertionPredicate = return
  60. > instance (AssertionPredicable t) => AssertionPredicable (IO t)
  61. > where assertionPredicate = (>>= assertionPredicate)
  62. Assertion Construction Operators
  63. --------------------------------
  64. > infix 1 @?, @=?, @?=
  65. > (@?) :: (AssertionPredicable t) => t -> String -> Assertion
  66. > pred @? msg = assertionPredicate pred >>= assertBool msg
  67. > (@=?) :: (Eq a, Show a) => a -> a -> Assertion
  68. > expected @=? actual = assertEqual "" expected actual
  69. > (@?=) :: (Eq a, Show a) => a -> a -> Assertion
  70. > actual @?= expected = assertEqual "" expected actual
  71. Test Definition
  72. ===============
  73. > data Test = TestCase Assertion
  74. > | TestList [Test]
  75. > | TestLabel String Test
  76. > instance Show Test where
  77. > showsPrec p (TestCase _) = showString "TestCase _"
  78. > showsPrec p (TestList ts) = showString "TestList " . showList ts
  79. > showsPrec p (TestLabel l t) = showString "TestLabel " . showString l
  80. > . showChar ' ' . showsPrec p t
  81. > testCaseCount :: Test -> Int
  82. > testCaseCount (TestCase _) = 1
  83. > testCaseCount (TestList ts) = sum (map testCaseCount ts)
  84. > testCaseCount (TestLabel _ t) = testCaseCount t
  85. > data Node = ListItem Int | Label String
  86. > deriving (Eq, Show, Read)
  87. > type Path = [Node] -- Node order is from test case to root.
  88. > testCasePaths :: Test -> [Path]
  89. > testCasePaths t = tcp t []
  90. > where tcp (TestCase _) p = [p]
  91. > tcp (TestList ts) p =
  92. > concat [ tcp t (ListItem n : p) | (t,n) <- zip ts [0..] ]
  93. > tcp (TestLabel l t) p = tcp t (Label l : p)
  94. Overloaded `test` Function
  95. --------------------------
  96. > class Testable t
  97. > where test :: t -> Test
  98. > instance Testable Test
  99. > where test = id
  100. > instance (Assertable t) => Testable (IO t)
  101. > where test = TestCase . assert
  102. > instance (Testable t) => Testable [t]
  103. > where test = TestList . map test
  104. Test Construction Operators
  105. ---------------------------
  106. > infix 1 ~?, ~=?, ~?=
  107. > infixr 0 ~:
  108. > (~?) :: (AssertionPredicable t) => t -> String -> Test
  109. > pred ~? msg = TestCase (pred @? msg)
  110. > (~=?) :: (Eq a, Show a) => a -> a -> Test
  111. > expected ~=? actual = TestCase (expected @=? actual)
  112. > (~?=) :: (Eq a, Show a) => a -> a -> Test
  113. > actual ~?= expected = TestCase (actual @?= expected)
  114. > (~:) :: (Testable t) => String -> t -> Test
  115. > label ~: t = TestLabel label (test t)
  116. Test Execution
  117. ==============
  118. > data Counts = Counts { cases, tried, errors, failures :: Int }
  119. > deriving (Eq, Show, Read)
  120. > data State = State { path :: Path, counts :: Counts }
  121. > deriving (Eq, Show, Read)
  122. > type ReportStart us = State -> us -> IO us
  123. > type ReportProblem us = String -> State -> us -> IO us
  124. Note that the counts in a start report do not include the test case
  125. being started, whereas the counts in a problem report do include the
  126. test case just finished. The principle is that the counts are sampled
  127. only between test case executions. As a result, the number of test
  128. case successes always equals the difference of test cases tried and
  129. the sum of test case errors and failures.
  130. > performTest :: ReportStart us -> ReportProblem us -> ReportProblem us
  131. > -> us -> Test -> IO (Counts, us)
  132. > performTest reportStart reportError reportFailure us t = do
  133. > (ss', us') <- pt initState us t
  134. > unless (null (path ss')) $ error "performTest: Final path is nonnull"
  135. > return (counts ss', us')
  136. > where
  137. > initState = State{ path = [], counts = initCounts }
  138. > initCounts = Counts{ cases = testCaseCount t, tried = 0,
  139. > errors = 0, failures = 0}
  140. > pt ss us (TestCase a) = do
  141. > us' <- reportStart ss us
  142. > r <- performTestCase a
  143. > case r of Nothing -> do return (ss', us')
  144. > Just (True, m) -> do usF <- reportFailure m ssF us'
  145. > return (ssF, usF)
  146. > Just (False, m) -> do usE <- reportError m ssE us'
  147. > return (ssE, usE)
  148. > where c@Counts{ tried = t } = counts ss
  149. > ss' = ss{ counts = c{ tried = t + 1 } }
  150. > ssF = ss{ counts = c{ tried = t + 1, failures = failures c + 1 } }
  151. > ssE = ss{ counts = c{ tried = t + 1, errors = errors c + 1 } }
  152. > pt ss us (TestList ts) = foldM f (ss, us) (zip ts [0..])
  153. > where f (ss, us) (t, n) = withNode (ListItem n) ss us t
  154. > pt ss us (TestLabel label t) = withNode (Label label) ss us t
  155. > withNode node ss0 us0 t = do (ss2, us1) <- pt ss1 us0 t
  156. > return (ss2{ path = path0 }, us1)
  157. > where path0 = path ss0
  158. > ss1 = ss0{ path = node : path0 }