packages feed

HUnit 1.2.5.2 → 1.6.2.0

raw patch · 26 files changed

Files

+ CHANGELOG.md view
@@ -0,0 +1,43 @@+## Changes++#### 1.6.2.0++- Add support for GHC 7.0.* and 7.2.*++#### 1.6.1.0++- Add `Test.HUnit.Text.runTestTTAndExit`++#### 1.6.0.0++- Generalize return type of `assertFailure` to `IO a`++#### 1.5.0.0++- Preserve actual/expected for `assertEqual` failures++#### 1.4.0.0++- Depend on `call-stack`++#### 1.3.1.2++- Fixes the test suite on GHC 8++#### 1.3.1.1++- Various updates to metadata and documentation removing outdated information and making other things more visible++### 1.3.1.0++- add minimal support for GHC 8.0++### 1.3.0.0++- removed support for old compilers++- add source locations for failing assertions (GHC >= 7.10.2 only)++#### 1.2.5.2++- Added support for GHC 7.7
HUnit.cabal view
@@ -1,59 +1,66 @@-Name:                   HUnit-Version:                1.2.5.2-Cabal-Version:          >= 1.8-License:                BSD3-License-File:           LICENSE-Author:                 Dean Herington-Maintainer:             hunit@richardg.name-Stability:              stable-Homepage:               http://hunit.sourceforge.net/-Category:               Testing-Synopsis:               A unit testing framework for Haskell-Description:-    HUnit is a unit testing framework for Haskell, inspired by the-    JUnit tool for Java, see: <http://www.junit.org>.-Tested-With:-    GHC == 7.4.1-    GHC == 7.4.2-    GHC == 7.7-Build-Type:             Simple-Extra-Source-Files:-    HUnit.cabal.hugs-    HUnit.cabal.tests-    Setup.hs.hugs-    tests/HUnitTests.hs-    tests/HUnitTestBase.lhs-    tests/HUnitTestExtended.hs-    tests/HUnitTestOptimize.hs-    tests/TerminalTest.hs-Data-Files:-    doc/Guide.html-    examples/Example.hs-    prologue.txt-    README+cabal-version: 1.12 +-- This file has been generated from package.yaml by hpack version 0.34.3.+--+-- see: https://github.com/sol/hpack++name:                   HUnit+version:                1.6.2.0+license:                BSD3+license-file:           LICENSE+author:                 Dean Herington+maintainer:             Simon Hengel <sol@typeful.net>+stability:              stable+homepage:               https://github.com/hspec/HUnit#readme+bug-reports:            https://github.com/hspec/HUnit/issues+category:               Testing+synopsis:               A unit testing framework for Haskell+description:            HUnit is a unit testing framework for Haskell, inspired by the+                        JUnit tool for Java, see: <http://www.junit.org>.+build-type:             Simple+extra-source-files:+  CHANGELOG.md+  README.md+ source-repository head-    type:     darcs-    location: http://code.haskell.org/HUnit/+  type: git+  location: https://github.com/hspec/HUnit -flag base4+library+  hs-source-dirs:+    src+  build-depends:+    base ==4.*,+    call-stack >=0.3.0,+    deepseq+  exposed-modules:+    Test.HUnit.Base+    Test.HUnit.Lang+    Test.HUnit.Terminal+    Test.HUnit.Text+    Test.HUnit+  other-modules:+    Paths_HUnit+  default-language: Haskell2010+  ghc-options: -Wall -Library-    Build-Depends: -        base < 5,-        deepseq-    if flag(base4)-        Build-Depends: base >= 4-        CPP-Options: -DBASE4-        GHC-Options: -Wall-    else-        Build-Depends: base < 4-    if impl(ghc >= 6.10)-        Build-Depends: base >= 4-    Exposed-Modules:-        Test.HUnit.Base,-        Test.HUnit.Lang,-        Test.HUnit.Terminal,-        Test.HUnit.Text,-        Test.HUnit-    Extensions: CPP+test-suite tests+  type: exitcode-stdio-1.0+  main-is: HUnitTests.hs+  hs-source-dirs:+    tests+    examples+  build-depends:+    HUnit,+    base ==4.*,+    call-stack >=0.3.0,+    deepseq,+    filepath+  other-modules:+    HUnitTestBase+    HUnitTestExtended+    TerminalTest+    Example+    Paths_HUnit+  default-language: Haskell2010+  ghc-options: -Wall
− HUnit.cabal.hugs
@@ -1,20 +0,0 @@-Name:                   HUnit-Version:                1.2.5.0-License:                BSD3-License-File:           LICENSE-Author:                 Dean Herington-Homepage:               http://hunit.sourceforge.net/-Category:               Testing-Build-Depends:          base-Synopsis:               A unit testing framework for Haskell-Description:-    HUnit is a unit testing framework for Haskell, inspired by the-    JUnit tool for Java, see: <http://www.junit.org>.-Exposed-Modules:-    Test.HUnit.Base,-    Test.HUnit.Lang,-    Test.HUnit.Terminal,-    Test.HUnit.Text,-    Test.HUnit-Extensions:             CPP-Hugs-Options:           -98
− HUnit.cabal.tests
@@ -1,122 +0,0 @@--- This CABAL file is used to build and run all of the various tests for the --- project.  This separate file is necessary because Hackage does not allow--- the optimization level to be specified when building executables and some--- tests require different optimization levels to be set.------ To use this file, simply copy it to HUnit.cabal, then build as normal.  You--- may rename the existing version of HUnit.cabal first, if you like.-Name:                   HUnit-Version:                1.2.5.2-Cabal-Version:          >= 1.8-License:                BSD3-License-File:           LICENSE-Author:                 Dean Herington-Maintainer:             hunit@richardg.name-Stability:              stable-Homepage:               http://hunit.sourceforge.net/-Category:               Testing-Synopsis:               A unit testing framework for Haskell-Description:-    HUnit is a unit testing framework for Haskell, inspired by the-    JUnit tool for Java, see: <http://www.junit.org>.-Tested-With:-    GHC == 7.4.1-    GHC == 7.4.2-    GHC == 7.7-Build-Type:             Simple-Extra-Source-Files:-    HUnit.cabal.hugs-    HUnit.cabal.tests-    Setup.hs.hugs-    tests/HUnitTests.hs-    tests/HUnitTestBase.lhs-    tests/HUnitTestExtended.hs-    tests/HUnitTestOptimize.hs-    tests/TerminalTest.hs-Data-Files:-    doc/Guide.html-    examples/Example.hs-    prologue.txt-    README--source-repository head-    type:     darcs-    location: http://code.haskell.org/HUnit/--flag base4--Library-    Build-Depends: -        base < 5,-        deepseq-    if flag(base4)-        Build-Depends: base >= 4-        CPP-Options: -DBASE4-        GHC-Options: -Wall-    else-        Build-Depends: base < 4-    if impl(ghc >= 6.10)-        Build-Depends: base >= 4-    Exposed-Modules:-        Test.HUnit.Base,-        Test.HUnit.Lang,-        Test.HUnit.Terminal,-        Test.HUnit.Text,-        Test.HUnit-    Extensions: CPP--Test-Suite hunit-tests-optimize-0-    Type:               exitcode-stdio-1.0-    Main-Is:            HUnitTests.hs-    HS-Source-Dirs:     . tests-    Build-Depends: -        base < 5,-        deepseq,-        filepath-    GHC-Options:        -O0-    if flag(base4)-        Build-Depends: base >= 4-        CPP-Options: -DBASE4 -DO0-        GHC-Options: -Wall-    else-        Build-Depends: base < 4-    if impl(ghc >= 6.10)-        Build-Depends: base >= 4-        Extensions: CPP-        -Test-Suite hunit-tests-optimize-1-    Type:               exitcode-stdio-1.0-    Main-Is:            HUnitTests.hs-    HS-Source-Dirs:     . tests-    Build-Depends: -        base < 5,-        deepseq-    GHC-Options:        -O1-    if flag(base4)-        Build-Depends: base >= 4-        CPP-Options: -DBASE4 -DO1-        GHC-Options: -Wall-    else-        Build-Depends: base < 4-    if impl(ghc >= 6.10)-        Build-Depends: base >= 4-        Extensions: CPP-        -Test-Suite hunit-tests-optimize-2-    Type:               exitcode-stdio-1.0-    Main-Is:            HUnitTests.hs-    HS-Source-Dirs:     . tests-    Build-Depends: -        base < 5,-        deepseq-    GHC-Options:        -O2-    if flag(base4)-        Build-Depends: base >= 4-        CPP-Options: -DBASE4 -DO2-        GHC-Options: -Wall-    else-        Build-Depends: base < 4-    if impl(ghc >= 6.10)-        Build-Depends: base >= 4-        Extensions: CPP-        
− README
@@ -1,12 +0,0 @@-HUnit is a unit testing framework for Haskell, inspired by the JUnit-tool for Java.  HUnit is free software; see its "License" file for-details.  HUnit is available at <http://hunit.sourceforge.net>.--HUnit 1.1.1 consists of a number of files.  Besides Haskell source files-in Test/HUnit (whose names end in ".hs" or ".lhs"), these files include:--  * README          -- this file-  * doc/Guide.html  -- user's guide, in HTML format-  * LICENSE         -- license for use of HUnit--See the user's guide for more information.
+ README.md view
@@ -0,0 +1,545 @@+# HUnit User's Guide++HUnit is a unit testing framework for Haskell, inspired by the JUnit tool for Java. This+guide describes how to use HUnit, assuming you are familiar with Haskell, though not+necessarily with JUnit. You can obtain HUnit, including this guide, at+[https://github.com/hspec/HUnit](https://github.com/hspec/HUnit)++## Introduction+A test-centered methodology for software development is most effective when tests are+easy to create, change, and execute. The [JUnit](www.junit.org) tool+pioneered support for test-first development in [Java](http://java.sun.com).+HUnit is an adaptation of JUnit to Haskell, a general-purpose, purely functional+programming language. (To learn more about Haskell, see [www.haskell.org](http://www.haskell.org)).++With HUnit, as with JUnit, you can easily create tests, name them, group them into+suites, and execute them, with the framework checking the results automatically. Test+specification in HUnit is even more concise and flexible than in JUnit, thanks to the+nature of the Haskell language. HUnit currently includes only a text-based test+controller, but the framework is designed for easy extension. (Would anyone care to+write a graphical test controller for HUnit?)++The next section helps you get started using HUnit in simple ways. Subsequent sections+give details on [writing tests](#writing-tests) and [running tests](#running-tests).+The document concludes with a section describing HUnit's [constituent files](#constituent-files)+and a section giving [references](#references) to further information.++## Getting Started++In the Haskell module where your tests will reside, import module `Test.HUnit`:++```haskell+import Test.HUnit+```++Define test cases as appropriate:++```haskell+test1 = TestCase (assertEqual "for (foo 3)," (1,2) (foo 3))+test2 = TestCase (do (x,y) <- partA 3+                     assertEqual "for the first result of partA," 5 x+                     b <- partB y+                     assertBool ("(partB " ++ show y ++ ") failed") b)+```++Name the test cases and group them together:++```haskell+tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]+```++Run the tests as a group. At a Haskell interpreter prompt, apply the+function `runTestTT` to the collected tests. (The `TT` suggests+**T**ext orientation with output to the **T**erminal.)++```haskell+> runTestTT tests+Cases: 2  Tried: 2  Errors: 0  Failures: 0+>+```++If the tests are proving their worth, you might see:++```haskell+> runTestTT tests+### Failure in: 0:test1+for (foo 3),+expected: (1,2)+ but got: (1,3)+Cases: 2  Tried: 2  Errors: 0  Failures: 1+>+```++Isn't that easy?++You can specify tests even more succinctly using operators and+overloaded functions that HUnit provides:++```haskell+tests = test [ "test1" ~: "(foo 3)" ~: (1,2) ~=? (foo 3),+               "test2" ~: do (x, y) <- partA 3+                             assertEqual "for the first result of partA," 5 x+                             partB y @? "(partB " ++ show y ++ ") failed" ]+```++Assuming the same test failures as before, you would see:++```haskell+> runTestTT tests+### Failure in: 0:test1:(foo 3)+expected: (1,2)+ but got: (1,3)+Cases: 2  Tried: 2  Errors: 0  Failures: 1+>+```++## Writing Tests++Tests are specified compositionally. [Assertions](#assertions) are+combined to make a [test case](#test-case), and test cases are combined+into [tests](#tests). HUnit also provides [advanced+features](#advanced-features) for more convenient test specification.++### Assertions++ The basic building block of a test is an **assertion**.++```haskell+type Assertion = IO ()+```++An assertion is an `IO` computation that always produces a void result. Why is an assertion an `IO` computation? So that programs with real-world side effects can be tested. How does an assertion assert anything if it produces no useful result? The answer is that an assertion can signal failure by calling `assertFailure`.++```haskell+assertFailure :: String -> Assertion+assertFailure msg = ioError (userError ("HUnit:" ++ msg))+```++`(assertFailure msg)` raises an exception. The string argument identifies the+ failure. The failure message is prefixed by "`HUnit:`" to mark it as an HUnit+ assertion failure message. The HUnit test framework interprets such an exception as+ indicating failure of the test whose execution raised the exception. (Note: The details+ concerning the implementation of `assertFailure` are subject to change and should+ not be relied upon.)++`assertFailure` can be used directly, but it is much more common to use it+ indirectly through other assertion functions that conditionally assert failure.++```haskell+assertBool :: String -> Bool -> Assertion+assertBool msg b = unless b (assertFailure msg)++assertString :: String -> Assertion+assertString s = unless (null s) (assertFailure s)++assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion+assertEqual preface expected actual =+  unless (actual == expected) (assertFailure msg)+ where msg = (if null preface then "" else preface ++ "\n") +++             "expected: " ++ show expected ++ "\n but got: " ++ show actual+```++With `assertBool` you give the assertion condition and failure message separately.+ With `assertString` the two are combined. With `assertEqual` you provide a+ "preface", an expected value, and an actual value; the failure message shows the two+ unequal values and is prefixed by the preface. Additional ways to create assertions are+ described later under [Advanced Features](#advanced-features)++Since assertions are `IO` computations, they may be combined--along with other+     `IO` computations--using `(>>=)`, `(>>)`, and the `do`+ notation. As long as its result is of type `(IO ())`, such a combination+ constitutes a single, collective assertion, incorporating any number of constituent+ assertions. The important features of such a collective assertion are that it fails if+ any of its constituent assertions is executed and fails, and that the first constituent+ assertion to fail terminates execution of the collective assertion. Such behavior is+ essential to specifying a test case.++### Test Case++A **test case** is the unit of test execution. That is, distinct test cases are+ executed independently. The failure of one is independent of the failure of any other.++A test case consists of a single, possibly collective, assertion. The possibly multiple+ constituent assertions in a test case's collective assertion are **not** independent.+ Their interdependence may be crucial to specifying correct operation for a test. A test+ case may involve a series of steps, each concluding in an assertion, where each step+ must succeed in order for the test case to continue. As another example, a test may+ require some "set up" to be performed that must be undone ("torn down" in JUnit+ parlance) once the test is complete. In this case, you could use Haskell's+     `IO.bracket` function to achieve the desired effect.++You can make a test case from an assertion by applying the `TestCase` constructor.+ For example, `(TestCase (return ()))` is a test case that never+ fails, and `(TestCase (assertEqual "for x," 3 x))`+ is a test case that checks that the value of `x` is 3.  Additional ways+ to create test cases are described later under [Advanced Features](#advanced-eatures).++### Tests++As soon as you have more than one test, you'll want to name them to tell them apart. As+ soon as you have more than several tests, you'll want to group them to process them more+ easily. So, naming and grouping are the two keys to managing collections of tests.++In tune with the "composite" design pattern [1], a+ **test** is defined as a package of test cases. Concretely, a test is either a single+ test case, a group of tests, or either of the first two identified by a label.++```haskell+data Test = TestCase Assertion+          | TestList [Test]+          | TestLabel String Test+```++There are three important features of this definition to note:+++* A `TestList` consists of a list of tests rather than a list of test cases.+   This means that the structure of a `Test` is actually a tree. Using a+   hierarchy helps organize tests just as it helps organize files in a file system.+* A `TestLabel` is attached to a test rather than to a test case. This means+   that all nodes in the test tree, not just test case (leaf) nodes, can be labeled.+   Hierarchical naming helps organize tests just as it helps organize files in a file+   system.+* A `TestLabel` is separate from both `TestCase` and `TestList`.+   This means that labeling is optional everywhere in the tree. Why is this a good+   thing? Because of the hierarchical structure of a test, each constituent test case+   is uniquely identified by its path in the tree, ignoring all labels. Sometimes a+   test case's path (or perhaps its subpath below a certain node) is a perfectly+   adequate "name" for the test case (perhaps relative to a certain node). In this+   case, creating a label for the test case is both unnecessary and inconvenient.+++The number of test cases that a test comprises can be computed with `testCaseCount`.++```haskell+testCaseCount :: Test -> Int+```++As mentioned above, a test is identified by its **path** in the test hierarchy.++```haskell+data Node  = ListItem Int | Label String+  deriving (Eq, Show, Read)++type Path = [Node]    -- Node order is from test case to root.+```++Each occurrence of `TestList` gives rise to a `ListItem` and each+ occurrence of `TestLabel` gives rise to a `Label`. The `ListItem`s+ by themselves ensure uniqueness among test case paths, while the `Label`s allow+ you to add mnemonic names for individual test cases and collections of them.++Note that the order of nodes in a path is reversed from what you might expect: The first+ node in the list is the one deepest in the tree. This order is a concession to+ efficiency: It allows common path prefixes to be shared.++The paths of the test cases that a test comprises can be computed with+ `testCasePaths`. The paths are listed in the order in which the corresponding+ test cases would be executed.++```haskell+testCasePaths :: Test -> [Path]+```++The three variants of `Test` can be constructed simply by applying+ `TestCase`, `TestList`, and `TestLabel` to appropriate arguments.+ Additional ways to create tests are described later under [Advanced Features](#advanced-features).++The design of the type `Test` provides great conciseness, flexibility, and+ convenience in specifying tests. Moreover, the nature of Haskell significantly augments+ these qualities:++* Combining assertions and other code to construct test cases is easy with the+    `IO` monad.+* Using overloaded functions and special operators (see below), specification of+    assertions and tests is extremely compact.+* Structuring a test tree by value, rather than by name as in JUnit, provides for more+    convenient, flexible, and robust test suite specification. In particular, a test+    suite can more easily be computed "on the fly" than in other test frameworks.+* Haskell's powerful abstraction facilities provide unmatched support for test+    refactoring.++### Advanced Features++HUnit provides additional features for specifying assertions and tests more conveniently+ and concisely. These facilities make use of Haskell type classes.++The following operators can be used to construct assertions.++```haskell+infix 1 @?, @=?, @?=++(@?) :: (AssertionPredicable t) => t -> String -> Assertion+pred @? msg = assertionPredicate pred >>= assertBool msg++(@=?) :: (Eq a, Show a) => a -> a -> Assertion+expected @=? actual = assertEqual "" expected actual++(@?=) :: (Eq a, Show a) => a -> a -> Assertion+actual @?= expected = assertEqual "" expected actual+```++You provide a boolean condition and failure message separately to `(@?)`, as for+     `assertBool`, but in a different order. The `(@=?)` and `(@?=)`+ operators provide shorthands for `assertEqual` when no preface is required. They+ differ only in the order in which the expected and actual values are provided. (The+ actual value--the uncertain one--goes on the "?" side of the operator.)++The `(@?)` operator's first argument is something from which an assertion+ predicate can be made, that is, its type must be `AssertionPredicable`.++```haskell+type AssertionPredicate = IO Bool++class AssertionPredicable t+ where assertionPredicate :: t -> AssertionPredicate++instance AssertionPredicable Bool+ where assertionPredicate = return++instance (AssertionPredicable t) => AssertionPredicable (IO t)+ where assertionPredicate = (>>= assertionPredicate)+```++The overloaded `assert` function in the `Assertable` type class constructs+ an assertion.++```haskell+class Assertable t+ where assert :: t -> Assertion++instance Assertable ()+ where assert = return++instance Assertable Bool+ where assert = assertBool ""++instance (ListAssertable t) => Assertable [t]+ where assert = listAssert++instance (Assertable t) => Assertable (IO t)+ where assert = (>>= assert)+```++The `ListAssertable` class allows `assert` to be applied to `[Char]`+ (that is, `String`).++```haskell+class ListAssertable t+ where listAssert :: [t] -> Assertion++instance ListAssertable Char+ where listAssert = assertString+```++With the above declarations, `(assert ())`,+ `(assert True)`, and `(assert "")` (as well as+ `IO` forms of these values, such as `(return ())`) are all+ assertions that never fail, while `(assert False)` and+     `(assert "some failure message")` (and their+     `IO` forms) are assertions that always fail. You may define additional+ instances for the type classes `Assertable`, `ListAssertable`, and+     `AssertionPredicable` if that should be useful in your application.++The overloaded `test` function in the `Testable` type class constructs a+ test.++```haskell+class Testable t+ where test :: t -> Test++instance Testable Test+ where test = id++instance (Assertable t) => Testable (IO t)+ where test = TestCase . assert++instance (Testable t) => Testable [t]+ where test = TestList . map test+```++The `test` function makes a test from either an `Assertion` (using+     `TestCase`), a list of `Testable` items (using `TestList`), or+ a `Test` (making no change).++The following operators can be used to construct tests.++```haskell+infix  1 ~?, ~=?, ~?=+infixr 0 ~:++(~?) :: (AssertionPredicable t) => t -> String -> Test+pred ~? msg = TestCase (pred @? msg)++(~=?) :: (Eq a, Show a) => a -> a -> Test+expected ~=? actual = TestCase (expected @=? actual)++(~?=) :: (Eq a, Show a) => a -> a -> Test+actual ~?= expected = TestCase (actual @?= expected)++(~:) :: (Testable t) => String -> t -> Test+label ~: t = TestLabel label (test t)+```++`(~?)`, `(~=?)`, and `(~?=)` each make an assertion, as for+ `(@?)`, `(@=?)`, and `(@?=)`, respectively, and then a test case+ from that assertion. `(~:)` attaches a label to something that is+ `Testable`. You may define additional instances for the type class+ `Testable` should that be useful.++## Running Tests++HUnit is structured to support multiple test controllers. The first+ subsection below describes the [test execution](#test-execution)+ characteristics common to all test controllers. The second subsection+ describes the text-based controller that is included with HUnit.++## Test Execution++All test controllers share a common test execution model. They differ only in how the+ results of test execution are shown.++The execution of a test (a value of type `Test`) involves the serial execution (in+ the `IO` monad) of its constituent test cases. The test cases are executed in a+ depth-first, left-to-right order. During test execution, four counts of test cases are+ maintained:++```haskell+data Counts = Counts { cases, tried, errors, failures :: Int }+  deriving (Eq, Show, Read)+```+++* `cases` is the number of test cases included in the test. This number is a+    static property of a test and remains unchanged during test execution.+* `tried` is the number of test cases that have been executed so far during the+    test execution.+* `errors` is the number of test cases whose execution ended with an unexpected+    exception being raised. Errors indicate problems with test cases, as opposed to the+    code under test.+* `failures` is the number of test cases whose execution asserted failure.+    Failures indicate problems with the code under test.+++Why is there no count for test case successes? The technical reason is that the counts+ are maintained such that the number of test case successes is always equal to+     `(tried - (errors + failures))`. The+ psychosocial reason is that, with test-centered development and the expectation that+ test failures will be few and short-lived, attention should be focused on the failures+ rather than the successes.++As test execution proceeds, three kinds of reporting event are communicated to the test+ controller. (What the controller does in response to the reporting events depends on the+ controller.)++* *start* -- Just prior to initiation of a test case, the path of the test case+    and the current counts (excluding the current test case) are reported.+* *error* -- When a test case terminates with an error, the error message is+    reported, along with the test case path and current counts (including the current+    test case).+* *failure* -- When a test case terminates with a failure, the failure message is+    reported, along with the test case path and current counts (including the current+    test case).++Typically, a test controller shows *error* and *failure* reports immediately+ but uses the *start* report merely to update an indication of overall test+ execution progress.++### Text-Based Controller++A text-based test controller is included with HUnit.++```haskell+runTestText :: PutText st -> Test -> IO (Counts, st)+```++`runTestText` is generalized on a *reporting scheme* given as its first+ argument. During execution of the test given as its second argument, the controller+ creates a string for each reporting event and processes it according to the reporting+ scheme. When test execution is complete, the controller returns the final counts along+ with the final state for the reporting scheme.++The strings for the three kinds of reporting event are as follows.++* A *start* report is the result of the function `showCounts` applied to+    the counts current immediately prior to initiation of the test case being started.+* An *error* report is of the form+            "`Error in:   *path*\n*message*`",+    where *path* is the path of the test case in error, as shown by+    `showPath`, and *message* is a message describing the error. If the path+    is empty, the report has the form "`Error:\n*message*`".+* A *failure* report is of the form+            "`Failure in: *path*\n*message*`", where+        *path* is the path of the test case in error, as shown by+    `showPath`, and *message* is the failure message. If the path is empty,+    the report has the form "`Failure:\n*message*`".++The function `showCounts` shows a set of counts.++```haskell+showCounts :: Counts -> String+```++The form of its result is+`Cases: *cases*  Tried: *tried*  Errors: *errors*  Failures: *failures*`+where *cases*, *tried*, *errors*, and *failures* are the count values.++The function `showPath` shows a test case path.++```haskell+ showPath :: Path -> String+```++The nodes in the path are reversed (so that the path reads from the root down to the test+ case), and the representations for the nodes are joined by '`:`' separators. The+ representation for `(ListItem *n*)` is `(show n)`. The representation+ for `(Label *label*)` is normally *label*. However, if *label*+ contains a colon or if `(show *label*)` is different from *label*+ surrounded by quotation marks--that is, if any ambiguity could exist--then `(Label+         *label*)` is represented as `(show *label*)`.++HUnit includes two reporting schemes for the text-based test controller. You may define+ others if you wish.++```haskell+putTextToHandle :: Handle -> Bool -> PutText Int+```++`putTextToHandle` writes error and failure reports, plus a report of the final+ counts, to the given handle. Each of these reports is terminated by a newline. In+ addition, if the given flag is `True`, it writes start reports to the handle as+ well. A start report, however, is not terminated by a newline. Before the next report is+ written, the start report is "erased" with an appropriate sequence of carriage return+ and space characters. Such overwriting realizes its intended effect on terminal devices.++```haskell+putTextToShowS :: PutText ShowS+```++`putTextToShowS` ignores start reports and simply accumulates error and failure+ reports, terminating them with newlines. The accumulated reports are returned (as the+ second element of the pair returned by `runTestText`) as a `ShowS`+ function (that is, one with type `(String -> String)`) whose+ first argument is a string to be appended to the accumulated report lines.++HUnit provides a shorthand for the most common use of the text-based test controller.++```haskell+runTestTT :: Test -> IO Counts+```++`runTestTT` invokes `runTestText`, specifying `(putTextToHandle stderr+True)` for the reporting scheme, and returns the final counts from the+test execution.++## References++* [1] Gamma, E., et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, Reading, MA, 1995: The classic book describing design patterns in an object-oriented context.++* [junit.org](http://www.junit.org): Web page for JUnit, the tool after which HUnit is modeled.++* [http://junit.sourceforge.net/doc/testinfected/testing.htm](http://junit.sourceforge.net/doc/testinfected/testing.htm): A good introduction to test-first development and the use of JUnit.++* [http://junit.sourceforge.net/doc/cookstour/cookstour.htm](http://junit.sourceforge.net/doc/cookstour/cookstour.htm): A description of the internal structure of JUnit. Makes for an interesting comparison between JUnit and HUnit.++The HUnit software and this guide were written by Dean Herington [heringto@cs.unc.edu](mailto:heringto@cs.unc.edu)
− Setup.hs
@@ -1,2 +0,0 @@-import Distribution.Simple-main = defaultMain
− Setup.hs.hugs
@@ -1,7 +0,0 @@-#!/usr/bin/env runghc-module Main (main) where--import Distribution.Simple--main :: IO ()-main = defaultMain
+ Setup.lhs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+> import Distribution.Simple+> main = defaultMain
− Test/HUnit.hs
@@ -1,80 +0,0 @@--- | HUnit is a unit testing framework for Haskell, inspired by the JUnit tool 
--- for Java. This guide describes how to use HUnit, assuming you are familiar 
--- with Haskell, though not necessarily with JUnit.
---
--- In the Haskell module where your tests will reside, import module 
--- @Test.HUnit@:
---
--- @
---    import Test.HUnit
--- @
---
---  Define test cases as appropriate:
--- 
--- @
---    test1 = TestCase (assertEqual "for (foo 3)," (1,2) (foo 3))
---    test2 = TestCase (do (x,y) <- partA 3
---                         assertEqual "for the first result of partA," 5 x
---                         b <- partB y
---                         assertBool ("(partB " ++ show y ++ ") failed") b)
--- @
---
--- Name the test cases and group them together:
---
--- @
---    tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]
--- @
---
--- Run the tests as a group. At a Haskell interpreter prompt, apply the function
--- @runTestTT@ to the collected tests. (The /TT/ suggests /T/ext orientation 
--- with output to the /T/erminal.)
---
--- @
---    \> runTestTT tests
---    Cases: 2  Tried: 2  Errors: 0  Failures: 0
---    \>
--- @
---
--- If the tests are proving their worth, you might see:
---
--- @
---    \> runTestTT tests
---    ### Failure in: 0:test1
---    for (foo 3),
---    expected: (1,2)
---     but got: (1,3)
---    Cases: 2  Tried: 2  Errors: 0  Failures: 1
---    \>
--- @
---
--- You can specify tests even more succinctly using operators and overloaded 
--- functions that HUnit provides:
---
--- @
---    tests = test [ "test1" ~: "(foo 3)" ~: (1,2) ~=? (foo 3),
---                   "test2" ~: do (x, y) <- partA 3
---                                 assertEqual "for the first result of partA," 5 x
---                                 partB y \@? "(partB " ++ show y ++ ") failed" ]
--- @
---
--- Assuming the same test failures as before, you would see:
---
--- @
---    \> runTestTT tests
---    ### Failure in: 0:test1:(foo 3)
---    expected: (1,2)
---     but got: (1,3)
---    Cases: 2  Tried: 2  Errors: 0  Failures: 1
---    \>
--- @
-
-module Test.HUnit
-(
-  module Test.HUnit.Base,
-  module Test.HUnit.Text
-)
-where
-
-import Test.HUnit.Base
-import Test.HUnit.Text
-
− Test/HUnit/Base.hs
@@ -1,355 +0,0 @@--- | Basic definitions for the HUnit library.------   This module contains what you need to create assertions and test cases and---   combine them into test suites. ------   This module also provides infrastructure for ---   implementing test controllers (which are used to execute tests). ---   See "Test.HUnit.Text" for a great example of how to implement a test ---   controller.--module Test.HUnit.Base-(-  -- ** Declaring tests-  Test(..),-  (~=?), (~?=), (~:), (~?),-  -  -- ** Making assertions-  assertFailure, {- from Test.HUnit.Lang: -}-  assertBool, assertEqual, assertString, -  Assertion, {- from Test.HUnit.Lang: -}-  (@=?), (@?=), (@?),--  -- ** Extending the assertion functionality-  Assertable(..), ListAssertable(..),-  AssertionPredicate, AssertionPredicable(..),-  Testable(..),--  -- ** Test execution-  -- $testExecutionNote-  State(..), Counts(..), -  Path, Node(..), -  testCasePaths,-  testCaseCount,-  ReportStart, ReportProblem,-  performTest-)-where--import Control.Monad (unless, foldM)----- Assertion Definition--- ====================--import Test.HUnit.Lang----- Conditional Assertion Functions--- ----------------------------------- | Asserts that the specified condition holds.-assertBool :: String    -- ^ The message that is displayed if the assertion fails-           -> Bool      -- ^ The condition-           -> Assertion-assertBool msg b = unless b (assertFailure msg)---- | Signals an assertion failure if a non-empty message (i.e., a message--- other than @\"\"@) is passed.-assertString :: String    -- ^ The message that is displayed with the assertion failure -             -> Assertion-assertString s = unless (null s) (assertFailure s)---- | Asserts that the specified actual value is equal to the expected value.--- The output message will contain the prefix, the expected value, and the --- actual value.---  --- If the prefix is the empty string (i.e., @\"\"@), then the prefix is omitted--- and only the expected and actual values are output.-assertEqual :: (Eq a, Show a) => String -- ^ The message prefix -                              -> a      -- ^ The expected value -                              -> a      -- ^ The actual value-                              -> Assertion-assertEqual preface expected actual =-  unless (actual == expected) (assertFailure msg)- where msg = (if null preface then "" else preface ++ "\n") ++-             "expected: " ++ show expected ++ "\n but got: " ++ show actual----- Overloaded `assert` Function--- -------------------------------- | Allows the extension of the assertion mechanism.------ Since an 'Assertion' can be a sequence of @Assertion@s and @IO@ actions, --- there is a fair amount of flexibility of what can be achieved.  As a rule,--- the resulting @Assertion@ should be the body of a 'TestCase' or part of--- a @TestCase@; it should not be used to assert multiple, independent --- conditions.------ If more complex arrangements of assertions are needed, 'Test's and--- 'Testable' should be used.-class Assertable t- where assert :: t -> Assertion--instance Assertable ()- where assert = return--instance Assertable Bool- where assert = assertBool ""--instance (ListAssertable t) => Assertable [t]- where assert = listAssert--instance (Assertable t) => Assertable (IO t)- where assert = (>>= assert)---- | A specialized form of 'Assertable' to handle lists.-class ListAssertable t- where listAssert :: [t] -> Assertion--instance ListAssertable Char- where listAssert = assertString----- Overloaded `assertionPredicate` Function--- -------------------------------------------- | The result of an assertion that hasn't been evaluated yet.------ Most test cases follow the following steps:------ 1. Do some processing or an action.------ 2. Assert certain conditions.------ However, this flow is not always suitable.  @AssertionPredicate@ allows for--- additional steps to be inserted without the initial action to be affected--- by side effects.  Additionally, clean-up can be done before the test case--- has a chance to end.  A potential work flow is:------ 1. Write data to a file.------ 2. Read data from a file, evaluate conditions.------ 3. Clean up the file.--- --- 4. Assert that the side effects of the read operation meet certain conditions.------ 5. Assert that the conditions evaluated in step 2 are met.-type AssertionPredicate = IO Bool---- | Used to signify that a data type can be converted to an assertion --- predicate.-class AssertionPredicable t- where assertionPredicate :: t -> AssertionPredicate--instance AssertionPredicable Bool- where assertionPredicate = return--instance (AssertionPredicable t) => AssertionPredicable (IO t)- where assertionPredicate = (>>= assertionPredicate)----- Assertion Construction Operators--- ----------------------------------infix  1 @?, @=?, @?=---- | Asserts that the condition obtained from the specified---   'AssertionPredicable' holds.-(@?) :: (AssertionPredicable t) => t          -- ^ A value of which the asserted condition is predicated-                                -> String     -- ^ A message that is displayed if the assertion fails-                                -> Assertion-predi @? msg = assertionPredicate predi >>= assertBool msg---- | Asserts that the specified actual value is equal to the expected value---   (with the expected value on the left-hand side).-(@=?) :: (Eq a, Show a) => a -- ^ The expected value-                        -> a -- ^ The actual value-                        -> Assertion-expected @=? actual = assertEqual "" expected actual---- | Asserts that the specified actual value is equal to the expected value---   (with the actual value on the left-hand side).-(@?=) :: (Eq a, Show a) => a -- ^ The actual value-                        -> a -- ^ The expected value-                        -> Assertion-actual @?= expected = assertEqual "" expected actual------ Test Definition--- ===============---- | The basic structure used to create an annotated tree of test cases.-data Test-    -- | A single, independent test case composed.-    = TestCase Assertion-    -- | A set of @Test@s sharing the same level in the hierarchy. -    | TestList [Test]-    -- | A name or description for a subtree of the @Test@s.-    | TestLabel String Test--instance Show Test where-  showsPrec _ (TestCase _)    = showString "TestCase _"-  showsPrec _ (TestList ts)   = showString "TestList " . showList ts-  showsPrec p (TestLabel l t) = showString "TestLabel " . showString l-                                . showChar ' ' . showsPrec p t---- Overloaded `test` Function--- ------------------------------ | Provides a way to convert data into a @Test@ or set of @Test@.-class Testable t- where test :: t -> Test--instance Testable Test- where test = id--instance (Assertable t) => Testable (IO t)- where test = TestCase . assert--instance (Testable t) => Testable [t]- where test = TestList . map test----- Test Construction Operators--- -----------------------------infix  1 ~?, ~=?, ~?=-infixr 0 ~:---- | Creates a test case resulting from asserting the condition obtained ---   from the specified 'AssertionPredicable'.-(~?) :: (AssertionPredicable t) => t       -- ^ A value of which the asserted condition is predicated-                                -> String  -- ^ A message that is displayed on test failure-                                -> Test-predi ~? msg = TestCase (predi @? msg)---- | Shorthand for a test case that asserts equality (with the expected ---   value on the left-hand side, and the actual value on the right-hand---   side).-(~=?) :: (Eq a, Show a) => a     -- ^ The expected value -                        -> a     -- ^ The actual value-                        -> Test-expected ~=? actual = TestCase (expected @=? actual)---- | Shorthand for a test case that asserts equality (with the actual ---   value on the left-hand side, and the expected value on the right-hand---   side).-(~?=) :: (Eq a, Show a) => a     -- ^ The actual value-                        -> a     -- ^ The expected value -                        -> Test-actual ~?= expected = TestCase (actual @?= expected)---- | Creates a test from the specified 'Testable', with the specified ---   label attached to it.------ Since 'Test' is @Testable@, this can be used as a shorthand way of attaching--- a 'TestLabel' to one or more tests.  -(~:) :: (Testable t) => String -> t -> Test-label ~: t = TestLabel label (test t)------ Test Execution--- ==============---- $testExecutionNote--- Note: the rest of the functionality in this module is intended for --- implementors of test controllers. If you just want to run your tests cases,--- simply use a test controller, such as the text-based controller in --- "Test.HUnit.Text".---- | A data structure that hold the results of tests that have been performed--- up until this point.-data Counts = Counts { cases, tried, errors, failures :: Int }-  deriving (Eq, Show, Read)---- | Keeps track of the remaining tests and the results of the performed tests.--- As each test is performed, the path is removed and the counts are--- updated as appropriate.-data State = State { path :: Path, counts :: Counts }-  deriving (Eq, Show, Read)---- | Report generator for reporting the start of a test run.-type ReportStart us = State -> us -> IO us---- | Report generator for reporting problems that have occurred during---   a test run. Problems may be errors or assertion failures.-type ReportProblem us = String -> State -> us -> IO us---- | Uniquely describes the location of a test within a test hierarchy.--- Node order is from test case to root.-type Path = [Node]---- | Composed into 'Path's.-data Node  = ListItem Int | Label String-  deriving (Eq, Show, Read)---- | Determines the paths for all 'TestCase's in a tree of @Test@s.-testCasePaths :: Test -> [Path]-testCasePaths t0 = tcp t0 []- where tcp (TestCase _) p = [p]-       tcp (TestList ts) p =-         concat [ tcp t (ListItem n : p) | (t,n) <- zip ts [0..] ]-       tcp (TestLabel l t) p = tcp t (Label l : p)---- | Counts the number of 'TestCase's in a tree of @Test@s.-testCaseCount :: Test -> Int-testCaseCount (TestCase _)    = 1-testCaseCount (TestList ts)   = sum (map testCaseCount ts)-testCaseCount (TestLabel _ t) = testCaseCount t---- | Performs a test run with the specified report generators.  ------ This handles the actual running of the tests.  Most developers will want --- to use @HUnit.Text.runTestTT@ instead.  A developer could use this function --- to execute tests via another IO system, such as a GUI, or to output the --- results in a different manner (e.g., upload XML-formatted results to a --- webservice).  ------ Note that the counts in a start report do not include the test case--- being started, whereas the counts in a problem report do include the--- test case just finished.  The principle is that the counts are sampled--- only between test case executions.  As a result, the number of test--- case successes always equals the difference of test cases tried and--- the sum of test case errors and failures.-performTest :: ReportStart us   -- ^ report generator for the test run start -            -> ReportProblem us -- ^ report generator for errors during the test run-            -> ReportProblem us -- ^ report generator for assertion failures during the test run-            -> us -            -> Test             -- ^ the test to be executed -            -> IO (Counts, us)-performTest reportStart reportError reportFailure initialUs initialT = do-  (ss', us') <- pt initState initialUs initialT-  unless (null (path ss')) $ error "performTest: Final path is nonnull"-  return (counts ss', us')- where-  initState  = State{ path = [], counts = initCounts }-  initCounts = Counts{ cases = testCaseCount initialT, tried = 0,-                       errors = 0, failures = 0}--  pt ss us (TestCase a) = do-    us' <- reportStart ss us-    r <- performTestCase a-    case r of Nothing         -> do return (ss', us')-              Just (True,  m) -> do usF <- reportFailure m ssF us'-                                    return (ssF, usF)-              Just (False, m) -> do usE <- reportError   m ssE us'-                                    return (ssE, usE)-   where c@Counts{ tried = n } = counts ss-         ss' = ss{ counts = c{ tried = n + 1 } }-         ssF = ss{ counts = c{ tried = n + 1, failures = failures c + 1 } }-         ssE = ss{ counts = c{ tried = n + 1, errors   = errors   c + 1 } }--  pt ss us (TestList ts) = foldM f (ss, us) (zip ts [0..])-   where f (ss', us') (t, n) = withNode (ListItem n) ss' us' t--  pt ss us (TestLabel label t) = withNode (Label label) ss us t--  withNode node ss0 us0 t = do (ss2, us1) <- pt ss1 us0 t-                               return (ss2{ path = path0 }, us1)-   where path0 = path ss0-         ss1 = ss0{ path = node : path0 }
− Test/HUnit/Lang.hs
@@ -1,149 +0,0 @@--- | This module abstracts the differences between implementations of 
--- Haskell (e.g., GHC, Hugs, and NHC).
-
-{-# LANGUAGE CPP #-} 
-#if __GLASGOW_HASKELL__ >= 707
-{-# LANGUAGE DeriveDataTypeable #-}
-#endif
-
-module Test.HUnit.Lang
-(
-  Assertion,
-  assertFailure,
-  performTestCase,
-#if defined(__GLASGOW_HASKELL__) || defined(__HUGS__)
--- * Internals
--- |
--- /Note:/ This is not part of the public API!  It is exposed so that you can
--- tinker with the internals of HUnit, but do not expect it to be stable!
-  HUnitFailure (..)
-#endif
-)
-where
-
-
--- When adapting this module for other Haskell language systems, change
--- the imports and the implementations but not the interfaces.
-
-
-
--- Imports
--- -------
-
-#if defined(__GLASGOW_HASKELL__) || defined(__HUGS__)
-import Data.Dynamic
-import Control.Exception as E
-#else
-import Data.List (isPrefixOf)
-import System.IO.Error (ioeGetErrorString, try)
-#endif
-
-import Control.DeepSeq
-
-
--- Interfaces
--- ----------
-
--- | When an assertion is evaluated, it will output a message if and only if the
--- assertion fails.  
---
--- Test cases are composed of a sequence of one or more assertions.
-
-type Assertion = IO ()
-
--- | Unconditionally signals that a failure has occured.  All
--- other assertions can be expressed with the form:
---
--- @
---    if conditionIsMet 
---        then IO () 
---        else assertFailure msg
--- @ 
-
-assertFailure :: String -- ^ A message that is displayed with the assertion failure 
-              -> Assertion
-
--- | Performs a single test case.  The meaning of the result is as follows:
---
---     [@Nothing@]           test case success
---
---     [@Just (True,  msg)@] test case failure with the given message
---
---     [@Just (False, msg)@] test case error with the given message
-
-performTestCase :: Assertion -- ^ an assertion to be made during the test case run 
-                -> IO (Maybe (Bool, String))
-
-
--- Implementations
--- ---------------
-
-#if defined(__GLASGOW_HASKELL__) || defined(__HUGS__)
-data HUnitFailure = HUnitFailure String
-#if __GLASGOW_HASKELL__ >= 707
-    deriving (Show, Typeable)
-#else
-    deriving Show
-
-hunitFailureTc :: TyCon
-#if MIN_VERSION_base(4,4,0)
-hunitFailureTc = mkTyCon3 "HUnit" "Test.HUnit.Lang" "HUnitFailure"
-#else
-hunitFailureTc = mkTyCon "HUnitFailure"
-#endif
-{-# NOINLINE hunitFailureTc #-}
- 
-instance Typeable HUnitFailure where
-    typeOf _ = mkTyConApp hunitFailureTc []
-#endif
-
-#ifdef BASE4
-instance Exception HUnitFailure
-
-assertFailure msg = msg `deepseq` E.throwIO (HUnitFailure msg)
-
-performTestCase action = 
-    do action
-       return Nothing
-     `E.catches`
-      [E.Handler (\(HUnitFailure msg) -> return $ Just (True, msg)),
-
-       -- Re-throw AsyncException, otherwise execution will not terminate on
-       -- SIGINT (ctrl-c).  Currently, all AsyncExceptions are being thrown
-       -- because it's thought that none of them will be encountered during
-       -- normal HUnit operation.  If you encounter an example where this
-       -- is not the case, please email the maintainer.
-       E.Handler (\e -> throw (e :: E.AsyncException)),
-
-       E.Handler (\e -> return $ Just (False, show (e :: E.SomeException)))]
-#else
-assertFailure msg = msg `deepseq` E.throwDyn (HUnitFailure msg)
-
-performTestCase action = 
-    do r <- E.try action
-       case r of 
-         Right () -> return Nothing
-         Left e@(E.DynException dyn) -> 
-             case fromDynamic dyn of
-               Just (HUnitFailure msg) -> return $ Just (True, msg)
-               Nothing                 -> return $ Just (False, show e)
-         Left e -> return $ Just (False, show e)
-#endif
-#else
-hunitPrefix = "HUnit:"
-
-nhc98Prefix = "I/O error (user-defined), call to function `userError':\n  "
-
-assertFailure msg = msg `deepseq` ioError (userError (hunitPrefix ++ msg))
-
-performTestCase action = do r <- try action
-                            case r of Right () -> return Nothing
-                                      Left  e  -> return (Just (decode e))
- where
-  decode e = let s0 = ioeGetErrorString e
-                 (_, s1) = dropPrefix nhc98Prefix s0
-             in            dropPrefix hunitPrefix s1
-  dropPrefix pref str = if pref `isPrefixOf` str
-                          then (True, drop (length pref) str)
-                          else (False, str)
-#endif
− Test/HUnit/Terminal.hs
@@ -1,42 +0,0 @@--- | This module handles the complexities of writing information to the
--- terminal, including modifying text in place.
-
-module Test.HUnit.Terminal (
-        terminalAppearance
-    ) where
-
-import Data.Char (isPrint)
-
-
--- | Simplifies the input string by interpreting @\\r@ and @\\b@ characters
--- specially so that the result string has the same final (or /terminal/,
--- pun intended) appearance as would the input string when written to a
--- terminal that overwrites character positions following carriage
--- returns and backspaces.
-
-terminalAppearance :: String -> String
-terminalAppearance str = ta id "" "" str
-
--- | The helper function @ta@ takes an accumulating @ShowS@-style function
--- that holds /committed/ lines of text, a (reversed) list of characters
--- on the current line /before/ the cursor, a (normal) list of characters
--- on the current line /after/ the cursor, and the remaining input.
-
-ta 
-    :: ([Char] -> t) -- ^ An accumulating @ShowS@-style function
-                     -- that holds /committed/ lines of text
-    -> [Char] -- ^ A (reversed) list of characters
-              -- on the current line /before/ the cursor
-    -> [Char] -- ^ A (normal) list of characters
-              -- on the current line /after/ the cursor
-    -> [Char] -- ^ The remaining input
-    -> t
-ta f    bs  as ('\n':cs) = ta (\t -> f (reverse bs ++ as ++ '\n' : t)) "" "" cs
-ta f    bs  as ('\r':cs) = ta f "" (reverse bs ++ as) cs
-ta f (b:bs) as ('\b':cs) = ta f bs (b:as) cs
-ta _    ""   _ ('\b': _) = error "'\\b' at beginning of line"
-ta f    bs  as (c:cs) 
-    | not (isPrint c)    = error "invalid nonprinting character"
-    | null as            = ta f (c:bs) ""        cs
-    | otherwise          = ta f (c:bs) (tail as) cs
-ta f    bs  as       ""  = f (reverse bs ++ as)
− Test/HUnit/Text.hs
@@ -1,128 +0,0 @@--- | Text-based test controller for running HUnit tests and reporting
---   results as text, usually to a terminal.
-
-module Test.HUnit.Text
-(
-  PutText(..),
-  putTextToHandle, putTextToShowS,
-  runTestText,
-  showPath, showCounts,
-  runTestTT
-)
-where
-
-import Test.HUnit.Base
-
-import Control.Monad (when)
-import System.IO (Handle, stderr, hPutStr, hPutStrLn)
-
-
--- | As the general text-based test controller ('runTestText') executes a
---   test, it reports each test case start, error, and failure by
---   constructing a string and passing it to the function embodied in a
---   'PutText'.  A report string is known as a \"line\", although it includes
---   no line terminator; the function in a 'PutText' is responsible for
---   terminating lines appropriately.  Besides the line, the function
---   receives a flag indicating the intended \"persistence\" of the line:
---   'True' indicates that the line should be part of the final overall
---   report; 'False' indicates that the line merely indicates progress of
---   the test execution.  Each progress line shows the current values of
---   the cumulative test execution counts; a final, persistent line shows
---   the final count values.
---
---   The 'PutText' function is also passed, and returns, an arbitrary state
---   value (called 'st' here).  The initial state value is given in the
---   'PutText'; the final value is returned by 'runTestText'.
-
-data PutText st = PutText (String -> Bool -> st -> IO st) st
-
-
--- | Two reporting schemes are defined here.  @putTextToHandle@ writes
--- report lines to a given handle.  'putTextToShowS' accumulates
--- persistent lines for return as a whole by 'runTestText'.
---
--- @putTextToHandle@ writes persistent lines to the given handle,
--- following each by a newline character.  In addition, if the given flag
--- is @True@, it writes progress lines to the handle as well.  A progress
--- line is written with no line termination, so that it can be
--- overwritten by the next report line.  As overwriting involves writing
--- carriage return and blank characters, its proper effect is usually
--- only obtained on terminal devices.
-
-putTextToHandle 
-    :: Handle 
-    -> Bool -- ^ Write progress lines to handle? 
-    -> PutText Int
-putTextToHandle handle showProgress = PutText put initCnt
- where
-  initCnt = if showProgress then 0 else -1
-  put line pers (-1) = do when pers (hPutStrLn handle line); return (-1)
-  put line True  cnt = do hPutStrLn handle (erase cnt ++ line); return 0
-  put line False _   = do hPutStr handle ('\r' : line); return (length line)
-    -- The "erasing" strategy with a single '\r' relies on the fact that the
-    -- lengths of successive summary lines are monotonically nondecreasing.
-  erase cnt = if cnt == 0 then "" else "\r" ++ replicate cnt ' ' ++ "\r"
-
-
--- | Accumulates persistent lines (dropping progess lines) for return by 
---   'runTestText'.  The accumulated lines are represented by a 
---   @'ShowS' ('String' -> 'String')@ function whose first argument is the
---   string to be appended to the accumulated report lines.
-
-putTextToShowS :: PutText ShowS
-putTextToShowS = PutText put id
- where put line pers f = return (if pers then acc f line else f)
-       acc f line rest = f (line ++ '\n' : rest)
-
-
--- | Executes a test, processing each report line according to the given 
---   reporting scheme.  The reporting scheme's state is threaded through calls 
---   to the reporting scheme's function and finally returned, along with final 
---   count values.
-
-runTestText :: PutText st -> Test -> IO (Counts, st)
-runTestText (PutText put us0) t = do
-  (counts', us1) <- performTest reportStart reportError reportFailure us0 t
-  us2 <- put (showCounts counts') True us1
-  return (counts', us2)
- where
-  reportStart ss us = put (showCounts (counts ss)) False us
-  reportError   = reportProblem "Error:"   "Error in:   "
-  reportFailure = reportProblem "Failure:" "Failure in: "
-  reportProblem p0 p1 msg ss us = put line True us
-   where line  = "### " ++ kind ++ path' ++ '\n' : msg
-         kind  = if null path' then p0 else p1
-         path' = showPath (path ss)
-
-
--- | Converts test execution counts to a string.
-
-showCounts :: Counts -> String
-showCounts Counts{ cases = cases', tried = tried',
-                   errors = errors', failures = failures' } =
-  "Cases: " ++ show cases' ++ "  Tried: " ++ show tried' ++
-  "  Errors: " ++ show errors' ++ "  Failures: " ++ show failures'
-
-
--- | Converts a test case path to a string, separating adjacent elements by 
---   the colon (\':\'). An element of the path is quoted (as with 'show') when
---   there is potential ambiguity.
-
-showPath :: Path -> String
-showPath [] = ""
-showPath nodes = foldl1 f (map showNode nodes)
- where f b a = a ++ ":" ++ b
-       showNode (ListItem n) = show n
-       showNode (Label label) = safe label (show label)
-       safe s ss = if ':' `elem` s || "\"" ++ s ++ "\"" /= ss then ss else s
-
-
--- | Provides the \"standard\" text-based test controller. Reporting is made to
---   standard error, and progress reports are included. For possible 
---   programmatic use, the final counts are returned.
---
---   The \"TT\" in the name suggests \"Text-based reporting to the Terminal\".
-
-runTestTT :: Test -> IO Counts
-runTestTT t = do (counts', 0) <- runTestText (putTextToHandle stderr True) t
-                 return counts'
− doc/Guide.html
@@ -1,539 +0,0 @@-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"-    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">-<html xmlns="http://www.w3.org/1999/xhtml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"-    xsi:schemaLocation="http://www.w3.org/MarkUp/SCHEMA/xhtml11.xsd" xml:lang="en">-    <head>-        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>-        <meta name="Author" content="Dean Herington"/>-        <meta name="KeyWords" content="HUnit, unit testing, test-first development, Haskell, JUnit"/>-        <title>HUnit 1.0 User's Guide</title>-    </head>-    <body>--        <h1>HUnit 1.2 User's Guide</h1>--        <p>HUnit is a unit testing framework for Haskell, inspired by the JUnit tool for Java. This-            guide describes how to use HUnit, assuming you are familiar with Haskell, though not-            necessarily with JUnit. You can obtain HUnit, including this guide, at <a-                href="http://code.haskell.org/HUnit">http://code.haskell.org/HUnit</a>.</p>--        <h2>Introduction</h2>--        <p>A test-centered methodology for software development is most effective when tests are-            easy to create, change, and execute. The <a href="http://www.junit.org">JUnit</a> tool-            pioneered support for test-first development in <a href="http://java.sun.com">Java</a>.-            HUnit is an adaptation of JUnit to Haskell, a general-purpose, purely functional-            programming language. (To learn more about Haskell, see <a href="http://www.haskell.org"-                >http://www.haskell.org</a>.)</p>--        <p>With HUnit, as with JUnit, you can easily create tests, name them, group them into-            suites, and execute them, with the framework checking the results automatically. Test-            specification in HUnit is even more concise and flexible than in JUnit, thanks to the-            nature of the Haskell language. HUnit currently includes only a text-based test-            controller, but the framework is designed for easy extension. (Would anyone care to-            write a graphical test controller for HUnit?)</p>--        <p>The next section helps you get started using HUnit in simple ways. Subsequent sections-            give details on <a href="#WritingTests">writing tests</a> and <a href="#RunningTests"-                >running tests</a>. The document concludes with a section describing HUnit's <a-                href="#ConstituentFiles">constituent files</a> and a section giving <a-                href="#References">references</a> to further information.</p>--        <h2 id="GettingStarted">Getting Started</h2>--        <p>In the Haskell module where your tests will reside, import module <tt>Test.HUnit</tt>:</p>-        <pre>-    import Test.HUnit-</pre>-        <p>Define test cases as appropriate:</p>-        <pre>-    test1 = TestCase (assertEqual "for (foo 3)," (1,2) (foo 3))-    test2 = TestCase (do (x,y) &lt;- partA 3-                         assertEqual "for the first result of partA," 5 x-                         b &lt;- partB y-                         assertBool ("(partB " ++ show y ++ ") failed") b)-</pre>-        <p>Name the test cases and group them together:</p>-        <pre>-    tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]-</pre>-        <p>Run the tests as a group. At a Haskell interpreter prompt, apply the function-                <tt>runTestTT</tt> to the collected tests. (The "<tt>TT</tt>" suggests-            <strong>T</strong>ext orientation with output to the <strong>T</strong>erminal.)</p>-        <pre>-    > runTestTT tests-    Cases: 2  Tried: 2  Errors: 0  Failures: 0-    >-</pre>-        <p>If the tests are proving their worth, you might see:</p>-        <pre>-    > runTestTT tests-    ### Failure in: 0:test1-    for (foo 3),-    expected: (1,2)-     but got: (1,3)-    Cases: 2  Tried: 2  Errors: 0  Failures: 1-    >-</pre>-        <p>Isn't that easy?</p>--        <p>You can specify tests even more succinctly using operators and overloaded functions that-            HUnit provides:</p>-        <pre>-    tests = test [ "test1" ~: "(foo 3)" ~: (1,2) ~=? (foo 3),-                   "test2" ~: do (x, y) &lt;- partA 3-                                 assertEqual "for the first result of partA," 5 x-                                 partB y @? "(partB " ++ show y ++ ") failed" ]-</pre>-        <p>Assuming the same test failures as before, you would see:</p>-        <pre>-    > runTestTT tests-    ### Failure in: 0:test1:(foo 3)-    expected: (1,2)-     but got: (1,3)-    Cases: 2  Tried: 2  Errors: 0  Failures: 1-    >-</pre>--        <h2 id="WritingTests">Writing Tests</h2>--        <p>Tests are specified compositionally. <a href="#Assertions">Assertions</a> are combined to-            make a <a href="#TestCase">test case</a>, and test cases are combined into <a-                href="#Tests">tests</a>. HUnit also provides <a href="#AdvancedFeatures">advanced-                features</a> for more convenient test specification.</p>--        <h3 id="Assertions">Assertions</h3>--        <p>The basic building block of a test is an <b>assertion</b>.</p>-        <pre>-    type Assertion = IO ()-</pre>-        <p>An assertion is an <tt>IO</tt> computation that always produces a void result. Why is an-            assertion an <tt>IO</tt> computation? So that programs with real-world side effects can-            be tested. How does an assertion assert anything if it produces no useful result? The-            answer is that an assertion can signal failure by calling <tt>assertFailure</tt>.</p>-        <pre>-    assertFailure :: String -> Assertion-    assertFailure msg = ioError (userError ("HUnit:" ++ msg))-</pre>-        <p><tt>(assertFailure msg)</tt> raises an exception. The string argument identifies the-            failure. The failure message is prefixed by "<tt>HUnit:</tt>" to mark it as an HUnit-            assertion failure message. The HUnit test framework interprets such an exception as-            indicating failure of the test whose execution raised the exception. (Note: The details-            concerning the implementation of <tt>assertFailure</tt> are subject to change and should-            not be relied upon.)</p>--        <p><tt>assertFailure</tt> can be used directly, but it is much more common to use it-            indirectly through other assertion functions that conditionally assert failure.</p>-        <pre>-    assertBool :: String -> Bool -> Assertion-    assertBool msg b = unless b (assertFailure msg)--    assertString :: String -> Assertion-    assertString s = unless (null s) (assertFailure s)--    assertEqual :: (Eq a, Show a) => String -> a -> a -> Assertion-    assertEqual preface expected actual =-      unless (actual == expected) (assertFailure msg)-     where msg = (if null preface then "" else preface ++ "\n") ++-                 "expected: " ++ show expected ++ "\n but got: " ++ show actual-</pre>-        <p>With <tt>assertBool</tt> you give the assertion condition and failure message separately.-            With <tt>assertString</tt> the two are combined. With <tt>assertEqual</tt> you provide a-            "preface", an expected value, and an actual value; the failure message shows the two-            unequal values and is prefixed by the preface. Additional ways to create assertions are-            described later under <a href="#AdvancedFeatures">Advanced Features</a>.</p>--        <p>Since assertions are <tt>IO</tt> computations, they may be combined--along with other-                <tt>IO</tt> computations--using <tt>(>>=)</tt>, <tt>(>>)</tt>, and the <tt>do</tt>-            notation. As long as its result is of type <tt>(IO ())</tt>, such a combination-            constitutes a single, collective assertion, incorporating any number of constituent-            assertions. The important features of such a collective assertion are that it fails if-            any of its constituent assertions is executed and fails, and that the first constituent-            assertion to fail terminates execution of the collective assertion. Such behavior is-            essential to specifying a test case.</p>--        <h3 id="TestCase">Test Case</h3>--        <p>A <b>test case</b> is the unit of test execution. That is, distinct test cases are-            executed independently. The failure of one is independent of the failure of any other.</p>--        <p>A test case consists of a single, possibly collective, assertion. The possibly multiple-            constituent assertions in a test case's collective assertion are <b>not</b> independent.-            Their interdependence may be crucial to specifying correct operation for a test. A test-            case may involve a series of steps, each concluding in an assertion, where each step-            must succeed in order for the test case to continue. As another example, a test may-            require some "set up" to be performed that must be undone ("torn down" in JUnit-            parlance) once the test is complete. In this case, you could use Haskell's-                <tt>IO.bracket</tt> function to achieve the desired effect.</p>--        <p>You can make a test case from an assertion by applying the <tt>TestCase</tt> constructor.-            For example, <tt>(TestCase&nbsp;(return&nbsp;()))</tt> is a test case that never-            fails, and-                <tt>(TestCase&nbsp;(assertEqual&nbsp;"for&nbsp;x,"&nbsp;3&nbsp;x))</tt>-            is a test case that checks that the value of <tt>x</tt> is 3.&nbsp; Additional ways-            to create test cases are described later under <a href="#AdvancedFeatures">Advanced-                Features</a>.</p>--        <h3 id="Tests">Tests</h3>--        <p>As soon as you have more than one test, you'll want to name them to tell them apart. As-            soon as you have more than several tests, you'll want to group them to process them more-            easily. So, naming and grouping are the two keys to managing collections of tests.</p>--        <p>In tune with the "composite" design pattern [<a href="#DesignPatterns">1</a>], a-            <b>test</b> is defined as a package of test cases. Concretely, a test is either a single-            test case, a group of tests, or either of the first two identified by a label.</p>-        <pre>-    data Test = TestCase Assertion-              | TestList [Test]-              | TestLabel String Test-</pre>-        <p>There are three important features of this definition to note:</p>-        <ul>-            <li>A <tt>TestList</tt> consists of a list of tests rather than a list of test cases.-                This means that the structure of a <tt>Test</tt> is actually a tree. Using a-                hierarchy helps organize tests just as it helps organize files in a file system.</li>-            <li>A <tt>TestLabel</tt> is attached to a test rather than to a test case. This means-                that all nodes in the test tree, not just test case (leaf) nodes, can be labeled.-                Hierarchical naming helps organize tests just as it helps organize files in a file-                system.</li>-            <li>A <tt>TestLabel</tt> is separate from both <tt>TestCase</tt> and <tt>TestList</tt>.-                This means that labeling is optional everywhere in the tree. Why is this a good-                thing? Because of the hierarchical structure of a test, each constituent test case-                is uniquely identified by its path in the tree, ignoring all labels. Sometimes a-                test case's path (or perhaps its subpath below a certain node) is a perfectly-                adequate "name" for the test case (perhaps relative to a certain node). In this-                case, creating a label for the test case is both unnecessary and inconvenient.</li>-        </ul>-        <p>The number of test cases that a test comprises can be computed with-            <tt>testCaseCount</tt>.</p>-        <pre>-    testCaseCount :: Test -> Int-</pre>-        <p>As mentioned above, a test is identified by its <b>path</b> in the test hierarchy.</p>-        <pre>-    data Node  = ListItem Int | Label String-      deriving (Eq, Show, Read)--    type Path = [Node]    -- Node order is from test case to root.-</pre>-        <p>Each occurrence of <tt>TestList</tt> gives rise to a <tt>ListItem</tt> and each-            occurrence of <tt>TestLabel</tt> gives rise to a <tt>Label</tt>. The <tt>ListItem</tt>s-            by themselves ensure uniqueness among test case paths, while the <tt>Label</tt>s allow-            you to add mnemonic names for individual test cases and collections of them.</p>--        <p>Note that the order of nodes in a path is reversed from what you might expect: The first-            node in the list is the one deepest in the tree. This order is a concession to-            efficiency: It allows common path prefixes to be shared.</p>--        <p>The paths of the test cases that a test comprises can be computed with-            <tt>testCasePaths</tt>. The paths are listed in the order in which the corresponding-            test cases would be executed.</p>-        <pre>-    testCasePaths :: Test -> [Path]-</pre>--        <p>The three variants of <tt>Test</tt> can be constructed simply by applying-            <tt>TestCase</tt>, <tt>TestList</tt>, and <tt>TestLabel</tt> to appropriate arguments.-            Additional ways to create tests are described later under <a href="#AdvancedFeatures"-                >Advanced Features</a>.</p>--        <p>The design of the type <tt>Test</tt> provides great conciseness, flexibility, and-            convenience in specifying tests. Moreover, the nature of Haskell significantly augments-            these qualities:</p>-        <ul>-            <li>Combining assertions and other code to construct test cases is easy with the-                <tt>IO</tt> monad.</li>-            <li>Using overloaded functions and special operators (see below), specification of-                assertions and tests is extremely compact.</li>-            <li>Structuring a test tree by value, rather than by name as in JUnit, provides for more-                convenient, flexible, and robust test suite specification. In particular, a test-                suite can more easily be computed "on the fly" than in other test frameworks.</li>-            <li>Haskell's powerful abstraction facilities provide unmatched support for test-                refactoring.</li>-        </ul>--        <h3 id="AdvancedFeatures">Advanced Features</h3>--        <p>HUnit provides additional features for specifying assertions and tests more conveniently-            and concisely. These facilities make use of Haskell type classes.</p>--        <p>The following operators can be used to construct assertions.</p>-        <pre>-    infix 1 @?, @=?, @?=--    (@?) :: (AssertionPredicable t) => t -> String -> Assertion-    pred @? msg = assertionPredicate pred >>= assertBool msg--    (@=?) :: (Eq a, Show a) => a -> a -> Assertion-    expected @=? actual = assertEqual "" expected actual--    (@?=) :: (Eq a, Show a) => a -> a -> Assertion-    actual @?= expected = assertEqual "" expected actual-</pre>-        <p>You provide a boolean condition and failure message separately to <tt>(@?)</tt>, as for-                <tt>assertBool</tt>, but in a different order. The <tt>(@=?)</tt> and <tt>(@?=)</tt>-            operators provide shorthands for <tt>assertEqual</tt> when no preface is required. They-            differ only in the order in which the expected and actual values are provided. (The-            actual value--the uncertain one--goes on the "?" side of the operator.)</p>--        <p>The <tt>(@?)</tt> operator's first argument is something from which an assertion-            predicate can be made, that is, its type must be <tt>AssertionPredicable</tt>.</p>-        <pre>-    type AssertionPredicate = IO Bool--    class AssertionPredicable t-     where assertionPredicate :: t -> AssertionPredicate--    instance AssertionPredicable Bool-     where assertionPredicate = return--    instance (AssertionPredicable t) => AssertionPredicable (IO t)-     where assertionPredicate = (>>= assertionPredicate)-</pre>-        <p>The overloaded <tt>assert</tt> function in the <tt>Assertable</tt> type class constructs-            an assertion.</p>-        <pre>-    class Assertable t-     where assert :: t -> Assertion--    instance Assertable ()-     where assert = return--    instance Assertable Bool-     where assert = assertBool ""--    instance (ListAssertable t) => Assertable [t]-     where assert = listAssert--    instance (Assertable t) => Assertable (IO t)-     where assert = (>>= assert)-</pre>-        <p>The <tt>ListAssertable</tt> class allows <tt>assert</tt> to be applied to <tt>[Char]</tt>-            (that is, <tt>String</tt>).</p>-        <pre>-    class ListAssertable t-     where listAssert :: [t] -> Assertion--    instance ListAssertable Char-     where listAssert = assertString-</pre>-        <p>With the above declarations, <tt>(assert&nbsp;())</tt>,-            <tt>(assert&nbsp;True)</tt>, and <tt>(assert&nbsp;"")</tt> (as well as-            <tt>IO</tt> forms of these values, such as <tt>(return&nbsp;())</tt>) are all-            assertions that never fail, while <tt>(assert&nbsp;False)</tt> and-                <tt>(assert&nbsp;"some&nbsp;failure&nbsp;message")</tt> (and their-                <tt>IO</tt> forms) are assertions that always fail. You may define additional-            instances for the type classes <tt>Assertable</tt>, <tt>ListAssertable</tt>, and-                <tt>AssertionPredicable</tt> if that should be useful in your application.</p>--        <p>The overloaded <tt>test</tt> function in the <tt>Testable</tt> type class constructs a-            test.</p>-        <pre>-    class Testable t-     where test :: t -> Test--    instance Testable Test-     where test = id--    instance (Assertable t) => Testable (IO t)-     where test = TestCase . assert--    instance (Testable t) => Testable [t]-     where test = TestList . map test-</pre>-        <p>The <tt>test</tt> function makes a test from either an <tt>Assertion</tt> (using-                <tt>TestCase</tt>), a list of <tt>Testable</tt> items (using <tt>TestList</tt>), or-            a <tt>Test</tt> (making no change).</p>--        <p>The following operators can be used to construct tests.</p>-        <pre>-    infix  1 ~?, ~=?, ~?=-    infixr 0 ~:--    (~?) :: (AssertionPredicable t) => t -> String -> Test-    pred ~? msg = TestCase (pred @? msg)--    (~=?) :: (Eq a, Show a) => a -> a -> Test-    expected ~=? actual = TestCase (expected @=? actual)--    (~?=) :: (Eq a, Show a) => a -> a -> Test-    actual ~?= expected = TestCase (actual @?= expected)--    (~:) :: (Testable t) => String -> t -> Test-    label ~: t = TestLabel label (test t)-</pre>-        <p><tt>(~?)</tt>, <tt>(~=?)</tt>, and <tt>(~?=)</tt> each make an assertion, as for-            <tt>(@?)</tt>, <tt>(@=?)</tt>, and <tt>(@?=)</tt>, respectively, and then a test case-            from that assertion. <tt>(~:)</tt> attaches a label to something that is-            <tt>Testable</tt>. You may define additional instances for the type class-            <tt>Testable</tt> should that be useful.</p>--        <h2 id="RunningTests">Running Tests</h2>--        <p>HUnit is structured to support multiple test controllers. The first subsection below-            describes the <a href="#TestExecution">test execution</a> characteristics common to all-            test controllers. The second subsection describes the <a href="#Text-BasedController"-                >text-based controller</a> that is included with HUnit.</p>--        <h3 id="TestExecution">Test Execution</h3>--        <p>All test controllers share a common test execution model. They differ only in how the-            results of test execution are shown.</p>--        <p>The execution of a test (a value of type <tt>Test</tt>) involves the serial execution (in-            the <tt>IO</tt> monad) of its constituent test cases. The test cases are executed in a-            depth-first, left-to-right order. During test execution, four counts of test cases are-            maintained:</p>-        <pre>-    data Counts = Counts { cases, tried, errors, failures :: Int }-      deriving (Eq, Show, Read)-</pre>-        <ul>-            <li><tt>cases</tt> is the number of test cases included in the test. This number is a-                static property of a test and remains unchanged during test execution.</li>-            <li><tt>tried</tt> is the number of test cases that have been executed so far during the-                test execution.</li>-            <li><tt>errors</tt> is the number of test cases whose execution ended with an unexpected-                exception being raised. Errors indicate problems with test cases, as opposed to the-                code under test.</li>-            <li><tt>failures</tt> is the number of test cases whose execution asserted failure.-                Failures indicate problems with the code under test.</li>-        </ul>-        <p>Why is there no count for test case successes? The technical reason is that the counts-            are maintained such that the number of test case successes is always equal to-                <tt>(tried&nbsp;-&nbsp;(errors&nbsp;+&nbsp;failures))</tt>. The-            psychosocial reason is that, with test-centered development and the expectation that-            test failures will be few and short-lived, attention should be focused on the failures-            rather than the successes.</p>--        <p>As test execution proceeds, three kinds of reporting event are communicated to the test-            controller. (What the controller does in response to the reporting events depends on the-            controller.)</p>-        <ul>-            <li><i>start</i> -- Just prior to initiation of a test case, the path of the test case-                and the current counts (excluding the current test case) are reported.</li>-            <li><i>error</i> -- When a test case terminates with an error, the error message is-                reported, along with the test case path and current counts (including the current-                test case).</li>-            <li><i>failure</i> -- When a test case terminates with a failure, the failure message is-                reported, along with the test case path and current counts (including the current-                test case).</li>-        </ul>-        <p>Typically, a test controller shows <i>error</i> and <i>failure</i> reports immediately-            but uses the <i>start</i> report merely to update an indication of overall test-            execution progress.</p>--        <h3 id="Text-BasedController">Text-Based Controller</h3>--        <p>A text-based test controller is included with HUnit.</p>-        <pre>-    runTestText :: PutText st -> Test -> IO (Counts, st)-</pre>-        <p><tt>runTestText</tt> is generalized on a <i>reporting scheme</i> given as its first-            argument. During execution of the test given as its second argument, the controller-            creates a string for each reporting event and processes it according to the reporting-            scheme. When test execution is complete, the controller returns the final counts along-            with the final state for the reporting scheme.</p>--        <p>The strings for the three kinds of reporting event are as follows.</p>-        <ul>-            <li>A <i>start</i> report is the result of the function <tt>showCounts</tt> applied to-                the counts current immediately prior to initiation of the test case being started.</li>-            <li>An <i>error</i> report is of the form-                        "<tt>Error&nbsp;in:&nbsp;&nbsp;&nbsp;<i>path</i>\n<i>message</i></tt>",-                where <i>path</i> is the path of the test case in error, as shown by-                <tt>showPath</tt>, and <i>message</i> is a message describing the error. If the path-                is empty, the report has the form "<tt>Error:\n<i>message</i></tt>".</li>-            <li>A <i>failure</i> report is of the form-                        "<tt>Failure&nbsp;in:&nbsp;<i>path</i>\n<i>message</i></tt>", where-                    <i>path</i> is the path of the test case in error, as shown by-                <tt>showPath</tt>, and <i>message</i> is the failure message. If the path is empty,-                the report has the form "<tt>Failure:\n<i>message</i></tt>".</li>-        </ul>--        <p>The function <tt>showCounts</tt> shows a set of counts.</p>-        <pre>-    showCounts :: Counts -> String-</pre>-        <p>The form of its result is-                    "<tt>Cases:&nbsp;<i>cases</i>&nbsp;&nbsp;Tried:&nbsp;<i>tried</i>&nbsp;&nbsp;Errors:&nbsp;<i>errors</i>&nbsp;&nbsp;Failures:&nbsp;<i>failures</i></tt>"-            where <i>cases</i>, <i>tried</i>, <i>errors</i>, and <i>failures</i> are the count-            values.</p>--        <p>The function <tt>showPath</tt> shows a test case path.</p>-        <pre>-    showPath :: Path -> String-</pre>-        <p>The nodes in the path are reversed (so that the path reads from the root down to the test-            case), and the representations for the nodes are joined by '<tt>:</tt>' separators. The-            representation for <tt>(ListItem <i>n</i>)</tt> is <tt>(show n)</tt>. The representation-            for <tt>(Label <i>label</i>)</tt> is normally <i>label</i>. However, if <i>label</i>-            contains a colon or if <tt>(show <i>label</i>)</tt> is different from <i>label</i>-            surrounded by quotation marks--that is, if any ambiguity could exist--then <tt>(Label-                    <i>label</i>)</tt> is represented as <tt>(show <i>label</i>)</tt>.</p>--        <p>HUnit includes two reporting schemes for the text-based test controller. You may define-            others if you wish.</p>-        <pre>-    putTextToHandle :: Handle -> Bool -> PutText Int-</pre>-        <p><tt>putTextToHandle</tt> writes error and failure reports, plus a report of the final-            counts, to the given handle. Each of these reports is terminated by a newline. In-            addition, if the given flag is <tt>True</tt>, it writes start reports to the handle as-            well. A start report, however, is not terminated by a newline. Before the next report is-            written, the start report is "erased" with an appropriate sequence of carriage return-            and space characters. Such overwriting realizes its intended effect on terminal devices.</p>-        <pre>-    putTextToShowS :: PutText ShowS-</pre>-        <p><tt>putTextToShowS</tt> ignores start reports and simply accumulates error and failure-            reports, terminating them with newlines. The accumulated reports are returned (as the-            second element of the pair returned by <tt>runTestText</tt>) as a <tt>ShowS</tt>-            function (that is, one with type <tt>(String&nbsp;->&nbsp;String)</tt>) whose-            first argument is a string to be appended to the accumulated report lines.</p>--        <p>HUnit provides a shorthand for the most common use of the text-based test controller.</p>-        <pre>-    runTestTT :: Test -> IO Counts-</pre>-        <p><tt>runTestTT</tt> invokes <tt>runTestText</tt>, specifying <tt>(putTextToHandle stderr-                True)</tt> for the reporting scheme, and returns the final counts from the test-            execution.</p>---        <h2 id="References">References</h2>--        <dl>--            <dt id="DesignPatterns">[1] Gamma, E., et al. Design Patterns: Elements of Reusable-                Object-Oriented Software, Addison-Wesley, Reading, MA, 1995.</dt>-            <dd>The classic book describing design patterns in an object-oriented context.</dd>--            <dt>-                <a href="http://www.junit.org">http://www.junit.org</a>-            </dt>-            <dd>Web page for JUnit, the tool after which HUnit is modeled.</dd>--            <dt>-                <a href="http://junit.sourceforge.net/doc/testinfected/testing.htm">-                    http://junit.sourceforge.net/doc/testinfected/testing.htm</a>-            </dt>-            <dd>A good introduction to test-first development and the use of JUnit.</dd>--            <dt>-                <a href="http://junit.sourceforge.net/doc/cookstour/cookstour.htm">-                    http://junit.sourceforge.net/doc/cookstour/cookstour.htm</a>-            </dt>-            <dd>A description of the internal structure of JUnit. Makes for an interesting-                comparison between JUnit and HUnit.</dd>--        </dl>--        <hr/>--        <p>The HUnit software and this guide were written by Dean Herington (<a-                href="mailto:heringto@cs.unc.edu">heringto@cs.unc.edu</a>).</p>-    </body>-</html>
examples/Example.hs view
@@ -1,9 +1,9 @@ -- Example.hs  --  Examples from HUnit user's guide -- -- For more examples, check out the tests directory.  It contains unit tests--- for HUnit. +-- for HUnit. -module Main where+module Example where  import Test.HUnit @@ -36,5 +36,5 @@                               partB y @? "(partB " ++ show y ++ ") failed" ]  main :: IO Counts-main = do runTestTT tests+main = do _ <- runTestTT tests           runTestTT tests'
− prologue.txt
@@ -1,2 +0,0 @@-HUnit is a unit testing framework for Haskell, inspired by the JUnit-tool for Java, see: <http://www.junit.org>.
+ src/Test/HUnit.hs view
@@ -0,0 +1,80 @@+-- | HUnit is a unit testing framework for Haskell, inspired by the JUnit tool+-- for Java. This guide describes how to use HUnit, assuming you are familiar+-- with Haskell, though not necessarily with JUnit.+--+-- In the Haskell module where your tests will reside, import module+-- @Test.HUnit@:+--+-- @+--    import Test.HUnit+-- @+--+--  Define test cases as appropriate:+--+-- @+--    test1 = TestCase (assertEqual "for (foo 3)," (1,2) (foo 3))+--    test2 = TestCase (do (x,y) <- partA 3+--                         assertEqual "for the first result of partA," 5 x+--                         b <- partB y+--                         assertBool ("(partB " ++ show y ++ ") failed") b)+-- @+--+-- Name the test cases and group them together:+--+-- @+--    tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]+-- @+--+-- Run the tests as a group. At a Haskell interpreter prompt, apply the function+-- @runTestTT@ to the collected tests. (The /TT/ suggests /T/ext orientation+-- with output to the /T/erminal.)+--+-- @+--    \> runTestTT tests+--    Cases: 2  Tried: 2  Errors: 0  Failures: 0+--    \>+-- @+--+-- If the tests are proving their worth, you might see:+--+-- @+--    \> runTestTT tests+--    ### Failure in: 0:test1+--    for (foo 3),+--    expected: (1,2)+--     but got: (1,3)+--    Cases: 2  Tried: 2  Errors: 0  Failures: 1+--    \>+-- @+--+-- You can specify tests even more succinctly using operators and overloaded+-- functions that HUnit provides:+--+-- @+--    tests = test [ "test1" ~: "(foo 3)" ~: (1,2) ~=? (foo 3),+--                   "test2" ~: do (x, y) <- partA 3+--                                 assertEqual "for the first result of partA," 5 x+--                                 partB y \@? "(partB " ++ show y ++ ") failed" ]+-- @+--+-- Assuming the same test failures as before, you would see:+--+-- @+--    \> runTestTT tests+--    ### Failure in: 0:test1:(foo 3)+--    expected: (1,2)+--     but got: (1,3)+--    Cases: 2  Tried: 2  Errors: 0  Failures: 1+--    \>+-- @++module Test.HUnit+(+  module Test.HUnit.Base,+  module Test.HUnit.Text+)+where++import Test.HUnit.Base+import Test.HUnit.Text+
+ src/Test/HUnit/Base.hs view
@@ -0,0 +1,361 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleContexts #-}++#if __GLASGOW_HASKELL__ >= 704+{-# LANGUAGE ConstraintKinds #-}+#define HasCallStack_ HasCallStack =>+#else+#define HasCallStack_+#endif++-- | Basic definitions for the HUnit library.+--+--   This module contains what you need to create assertions and test cases and+--   combine them into test suites.+--+--   This module also provides infrastructure for+--   implementing test controllers (which are used to execute tests).+--   See "Test.HUnit.Text" for a great example of how to implement a test+--   controller.++module Test.HUnit.Base+(+  -- ** Declaring tests+  Test(..),+  (~=?), (~?=), (~:), (~?),++  -- ** Making assertions+  assertFailure, {- from Test.HUnit.Lang: -}+  assertBool, assertEqual, assertString,+  Assertion, {- from Test.HUnit.Lang: -}+  (@=?), (@?=), (@?),++  -- ** Extending the assertion functionality+  Assertable(..), ListAssertable(..),+  AssertionPredicate, AssertionPredicable(..),+  Testable(..),++  -- ** Test execution+  -- $testExecutionNote+  State(..), Counts(..),+  Path, Node(..),+  testCasePaths,+  testCaseCount,+  ReportStart, ReportProblem,+  performTest+) where++import Control.Monad (unless, foldM)+import Data.CallStack+++-- Assertion Definition+-- ====================++import Test.HUnit.Lang+++-- Conditional Assertion Functions+-- -------------------------------++-- | Asserts that the specified condition holds.+assertBool :: HasCallStack_+              String    -- ^ The message that is displayed if the assertion fails+           -> Bool      -- ^ The condition+           -> Assertion+assertBool msg b = unless b (assertFailure msg)++-- | Signals an assertion failure if a non-empty message (i.e., a message+-- other than @\"\"@) is passed.+assertString :: HasCallStack_+                String    -- ^ The message that is displayed with the assertion failure+             -> Assertion+assertString s = unless (null s) (assertFailure s)++-- Overloaded `assert` Function+-- ----------------------------++-- | Allows the extension of the assertion mechanism.+--+-- Since an 'Assertion' can be a sequence of @Assertion@s and @IO@ actions,+-- there is a fair amount of flexibility of what can be achieved.  As a rule,+-- the resulting @Assertion@ should be the body of a 'TestCase' or part of+-- a @TestCase@; it should not be used to assert multiple, independent+-- conditions.+--+-- If more complex arrangements of assertions are needed, 'Test's and+-- 'Testable' should be used.+class Assertable t+ where assert :: HasCallStack_ t -> Assertion++instance Assertable ()+ where assert = return++instance Assertable Bool+ where assert = assertBool ""++instance (ListAssertable t) => Assertable [t]+ where assert = listAssert++instance (Assertable t) => Assertable (IO t)+ where assert = (>>= assert)++-- | A specialized form of 'Assertable' to handle lists.+class ListAssertable t+ where listAssert :: HasCallStack_ [t] -> Assertion++instance ListAssertable Char+ where listAssert = assertString+++-- Overloaded `assertionPredicate` Function+-- ----------------------------------------++-- | The result of an assertion that hasn't been evaluated yet.+--+-- Most test cases follow the following steps:+--+-- 1. Do some processing or an action.+--+-- 2. Assert certain conditions.+--+-- However, this flow is not always suitable.  @AssertionPredicate@ allows for+-- additional steps to be inserted without the initial action to be affected+-- by side effects.  Additionally, clean-up can be done before the test case+-- has a chance to end.  A potential work flow is:+--+-- 1. Write data to a file.+--+-- 2. Read data from a file, evaluate conditions.+--+-- 3. Clean up the file.+--+-- 4. Assert that the side effects of the read operation meet certain conditions.+--+-- 5. Assert that the conditions evaluated in step 2 are met.+type AssertionPredicate = IO Bool++-- | Used to signify that a data type can be converted to an assertion+-- predicate.+class AssertionPredicable t+ where assertionPredicate :: t -> AssertionPredicate++instance AssertionPredicable Bool+ where assertionPredicate = return++instance (AssertionPredicable t) => AssertionPredicable (IO t)+ where assertionPredicate = (>>= assertionPredicate)+++-- Assertion Construction Operators+-- --------------------------------++infix  1 @?, @=?, @?=++-- | Asserts that the condition obtained from the specified+--   'AssertionPredicable' holds.+(@?) :: HasCallStack_ AssertionPredicable t+                                => t          -- ^ A value of which the asserted condition is predicated+                                -> String     -- ^ A message that is displayed if the assertion fails+                                -> Assertion+predi @? msg = assertionPredicate predi >>= assertBool msg++-- | Asserts that the specified actual value is equal to the expected value+--   (with the expected value on the left-hand side).+(@=?) :: HasCallStack_ (Eq a, Show a)+                        => a -- ^ The expected value+                        -> a -- ^ The actual value+                        -> Assertion+expected @=? actual = assertEqual "" expected actual++-- | Asserts that the specified actual value is equal to the expected value+--   (with the actual value on the left-hand side).+(@?=) :: HasCallStack_ (Eq a, Show a)+                        => a -- ^ The actual value+                        -> a -- ^ The expected value+                        -> Assertion+actual @?= expected = assertEqual "" expected actual++++-- Test Definition+-- ===============++-- | The basic structure used to create an annotated tree of test cases.+data Test+    -- | A single, independent test case composed.+    = TestCase Assertion+    -- | A set of @Test@s sharing the same level in the hierarchy.+    | TestList [Test]+    -- | A name or description for a subtree of the @Test@s.+    | TestLabel String Test++instance Show Test where+  showsPrec _ (TestCase _)    = showString "TestCase _"+  showsPrec _ (TestList ts)   = showString "TestList " . showList ts+  showsPrec p (TestLabel l t) = showString "TestLabel " . showString l+                                . showChar ' ' . showsPrec p t++-- Overloaded `test` Function+-- --------------------------++-- | Provides a way to convert data into a @Test@ or set of @Test@.+class Testable t+ where test :: HasCallStack_ t -> Test++instance Testable Test+ where test = id++instance (Assertable t) => Testable (IO t)+ where test = TestCase . assert++instance (Testable t) => Testable [t]+ where test = TestList . map test+++-- Test Construction Operators+-- ---------------------------++infix  1 ~?, ~=?, ~?=+infixr 0 ~:++-- | Creates a test case resulting from asserting the condition obtained+--   from the specified 'AssertionPredicable'.+(~?) :: HasCallStack_ AssertionPredicable t+                                => t       -- ^ A value of which the asserted condition is predicated+                                -> String  -- ^ A message that is displayed on test failure+                                -> Test+predi ~? msg = TestCase (predi @? msg)++-- | Shorthand for a test case that asserts equality (with the expected+--   value on the left-hand side, and the actual value on the right-hand+--   side).+(~=?) :: HasCallStack_ (Eq a, Show a)+                        => a     -- ^ The expected value+                        -> a     -- ^ The actual value+                        -> Test+expected ~=? actual = TestCase (expected @=? actual)++-- | Shorthand for a test case that asserts equality (with the actual+--   value on the left-hand side, and the expected value on the right-hand+--   side).+(~?=) :: HasCallStack_ (Eq a, Show a)+                        => a     -- ^ The actual value+                        -> a     -- ^ The expected value+                        -> Test+actual ~?= expected = TestCase (actual @?= expected)++-- | Creates a test from the specified 'Testable', with the specified+--   label attached to it.+--+-- Since 'Test' is @Testable@, this can be used as a shorthand way of attaching+-- a 'TestLabel' to one or more tests.+(~:) :: HasCallStack_ Testable t => String -> t -> Test+label ~: t = TestLabel label (test t)++++-- Test Execution+-- ==============++-- $testExecutionNote+-- Note: the rest of the functionality in this module is intended for+-- implementors of test controllers. If you just want to run your tests cases,+-- simply use a test controller, such as the text-based controller in+-- "Test.HUnit.Text".++-- | A data structure that hold the results of tests that have been performed+-- up until this point.+data Counts = Counts { cases, tried, errors, failures :: Int }+  deriving (Eq, Show, Read)++-- | Keeps track of the remaining tests and the results of the performed tests.+-- As each test is performed, the path is removed and the counts are+-- updated as appropriate.+data State = State { path :: Path, counts :: Counts }+  deriving (Eq, Show, Read)++-- | Report generator for reporting the start of a test run.+type ReportStart us = State -> us -> IO us++-- | Report generator for reporting problems that have occurred during+--   a test run. Problems may be errors or assertion failures.+type ReportProblem us = Maybe SrcLoc -> String -> State -> us -> IO us++-- | Uniquely describes the location of a test within a test hierarchy.+-- Node order is from test case to root.+type Path = [Node]++-- | Composed into 'Path's.+data Node  = ListItem Int | Label String+  deriving (Eq, Show, Read)++-- | Determines the paths for all 'TestCase's in a tree of @Test@s.+testCasePaths :: Test -> [Path]+testCasePaths t0 = tcp t0 []+ where tcp (TestCase _) p = [p]+       tcp (TestList ts) p =+         concat [ tcp t (ListItem n : p) | (t,n) <- zip ts [0..] ]+       tcp (TestLabel l t) p = tcp t (Label l : p)++-- | Counts the number of 'TestCase's in a tree of @Test@s.+testCaseCount :: Test -> Int+testCaseCount (TestCase _)    = 1+testCaseCount (TestList ts)   = sum (map testCaseCount ts)+testCaseCount (TestLabel _ t) = testCaseCount t++-- | Performs a test run with the specified report generators.+--+-- This handles the actual running of the tests.  Most developers will want+-- to use @HUnit.Text.runTestTT@ instead.  A developer could use this function+-- to execute tests via another IO system, such as a GUI, or to output the+-- results in a different manner (e.g., upload XML-formatted results to a+-- webservice).+--+-- Note that the counts in a start report do not include the test case+-- being started, whereas the counts in a problem report do include the+-- test case just finished.  The principle is that the counts are sampled+-- only between test case executions.  As a result, the number of test+-- case successes always equals the difference of test cases tried and+-- the sum of test case errors and failures.+performTest :: ReportStart us   -- ^ report generator for the test run start+            -> ReportProblem us -- ^ report generator for errors during the test run+            -> ReportProblem us -- ^ report generator for assertion failures during the test run+            -> us+            -> Test             -- ^ the test to be executed+            -> IO (Counts, us)+performTest reportStart reportError reportFailure initialUs initialT = do+  (ss', us') <- pt initState initialUs initialT+  unless (null (path ss')) $ error "performTest: Final path is nonnull"+  return (counts ss', us')+ where+  initState  = State{ path = [], counts = initCounts }+  initCounts = Counts{ cases = testCaseCount initialT, tried = 0,+                       errors = 0, failures = 0}++  pt ss us (TestCase a) = do+    us' <- reportStart ss us+    r <- performTestCase a+    case r of+      Success -> do+        return (ss', us')+      Failure loc m -> do+        usF <- reportFailure loc m ssF us'+        return (ssF, usF)+      Error loc m -> do+        usE <- reportError loc m ssE us'+        return (ssE, usE)+   where c@Counts{ tried = n } = counts ss+         ss' = ss{ counts = c{ tried = n + 1 } }+         ssF = ss{ counts = c{ tried = n + 1, failures = failures c + 1 } }+         ssE = ss{ counts = c{ tried = n + 1, errors   = errors   c + 1 } }++  pt ss us (TestList ts) = foldM f (ss, us) (zip ts [0..])+   where f (ss', us') (t, n) = withNode (ListItem n) ss' us' t++  pt ss us (TestLabel label t) = withNode (Label label) ss us t++  withNode node ss0 us0 t = do (ss2, us1) <- pt ss1 us0 t+                               return (ss2{ path = path0 }, us1)+   where path0 = path ss0+         ss1 = ss0{ path = node : path0 }
+ src/Test/HUnit/Lang.hs view
@@ -0,0 +1,104 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts #-}++#if __GLASGOW_HASKELL__ >= 704+{-# LANGUAGE ConstraintKinds #-}+#define HasCallStack_ HasCallStack =>+#else+#define HasCallStack_+#endif++module Test.HUnit.Lang (+  Assertion,+  assertFailure,+  assertEqual,++  Result (..),+  performTestCase,+-- * Internals+-- |+-- /Note:/ This is not part of the public API!  It is exposed so that you can+-- tinker with the internals of HUnit, but do not expect it to be stable!+  HUnitFailure (..),+  FailureReason (..),+  formatFailureReason+) where++import           Control.DeepSeq+import           Control.Exception as E+import           Control.Monad+import           Data.List+import           Data.Typeable+import           Data.CallStack++-- | When an assertion is evaluated, it will output a message if and only if the+-- assertion fails.+--+-- Test cases are composed of a sequence of one or more assertions.+type Assertion = IO ()++data HUnitFailure = HUnitFailure (Maybe SrcLoc) FailureReason+    deriving (Eq, Show, Typeable)++instance Exception HUnitFailure++data FailureReason = Reason String | ExpectedButGot (Maybe String) String String+    deriving (Eq, Show, Typeable)++location :: HasCallStack_ Maybe SrcLoc+location = case reverse callStack of+  (_, loc) : _ -> Just loc+  [] -> Nothing++-- | Unconditionally signals that a failure has occurred.+assertFailure ::+     HasCallStack_+     String -- ^ A message that is displayed with the assertion failure+  -> IO a+assertFailure msg = msg `deepseq` E.throwIO (HUnitFailure location $ Reason msg)++-- | Asserts that the specified actual value is equal to the expected value.+-- The output message will contain the prefix, the expected value, and the+-- actual value.+--+-- If the prefix is the empty string (i.e., @\"\"@), then the prefix is omitted+-- and only the expected and actual values are output.+assertEqual :: HasCallStack_ (Eq a, Show a)+                              => String -- ^ The message prefix+                              -> a      -- ^ The expected value+                              -> a      -- ^ The actual value+                              -> Assertion+assertEqual preface expected actual =+  unless (actual == expected) $ do+    (prefaceMsg `deepseq` expectedMsg `deepseq` actualMsg `deepseq` E.throwIO (HUnitFailure location $ ExpectedButGot prefaceMsg expectedMsg actualMsg))+  where+    prefaceMsg+      | null preface = Nothing+      | otherwise = Just preface+    expectedMsg = show expected+    actualMsg = show actual++formatFailureReason :: FailureReason -> String+formatFailureReason (Reason reason) = reason+formatFailureReason (ExpectedButGot preface expected actual) = intercalate "\n" . maybe id (:) preface $ ["expected: " ++ expected, " but got: " ++ actual]++data Result = Success | Failure (Maybe SrcLoc) String | Error (Maybe SrcLoc) String+  deriving (Eq, Show)++-- | Performs a single test case.+performTestCase :: Assertion -- ^ an assertion to be made during the test case run+                -> IO Result+performTestCase action =+  (action >> return Success)+     `E.catches`+      [E.Handler (\(HUnitFailure loc reason) -> return $ Failure loc (formatFailureReason reason)),++       -- Re-throw AsyncException, otherwise execution will not terminate on+       -- SIGINT (ctrl-c).  Currently, all AsyncExceptions are being thrown+       -- because it's thought that none of them will be encountered during+       -- normal HUnit operation.  If you encounter an example where this+       -- is not the case, please email the maintainer.+       E.Handler (\e -> throw (e :: E.AsyncException)),++       E.Handler (\e -> return $ Error Nothing $ show (e :: E.SomeException))]
+ src/Test/HUnit/Terminal.hs view
@@ -0,0 +1,42 @@+-- | This module handles the complexities of writing information to the+-- terminal, including modifying text in place.++module Test.HUnit.Terminal (+        terminalAppearance+    ) where++import Data.Char (isPrint)+++-- | Simplifies the input string by interpreting @\\r@ and @\\b@ characters+-- specially so that the result string has the same final (or /terminal/,+-- pun intended) appearance as would the input string when written to a+-- terminal that overwrites character positions following carriage+-- returns and backspaces.++terminalAppearance :: String -> String+terminalAppearance str = ta id "" "" str++-- | The helper function @ta@ takes an accumulating @ShowS@-style function+-- that holds /committed/ lines of text, a (reversed) list of characters+-- on the current line /before/ the cursor, a (normal) list of characters+-- on the current line /after/ the cursor, and the remaining input.++ta+    :: ([Char] -> t) -- ^ An accumulating @ShowS@-style function+                     -- that holds /committed/ lines of text+    -> [Char] -- ^ A (reversed) list of characters+              -- on the current line /before/ the cursor+    -> [Char] -- ^ A (normal) list of characters+              -- on the current line /after/ the cursor+    -> [Char] -- ^ The remaining input+    -> t+ta f    bs  as ('\n':cs) = ta (\t -> f (reverse bs ++ as ++ '\n' : t)) "" "" cs+ta f    bs  as ('\r':cs) = ta f "" (reverse bs ++ as) cs+ta f (b:bs) as ('\b':cs) = ta f bs (b:as) cs+ta _    ""   _ ('\b': _) = error "'\\b' at beginning of line"+ta f    bs  as (c:cs)+    | not (isPrint c)    = error "invalid nonprinting character"+    | null as            = ta f (c:bs) ""        cs+    | otherwise          = ta f (c:bs) (tail as) cs+ta f    bs  as       ""  = f (reverse bs ++ as)
+ src/Test/HUnit/Text.hs view
@@ -0,0 +1,152 @@+-- | Text-based test controller for running HUnit tests and reporting+--   results as text, usually to a terminal.++module Test.HUnit.Text+(+  PutText(..),+  putTextToHandle, putTextToShowS,+  runTestText,+  showPath, showCounts,+  runTestTT,+  runTestTTAndExit+)+where++import Test.HUnit.Base++import Data.CallStack+import Control.Monad (when)+import System.IO (Handle, stderr, hPutStr, hPutStrLn)+import System.Exit (exitSuccess, exitFailure)+++-- | As the general text-based test controller ('runTestText') executes a+--   test, it reports each test case start, error, and failure by+--   constructing a string and passing it to the function embodied in a+--   'PutText'.  A report string is known as a \"line\", although it includes+--   no line terminator; the function in a 'PutText' is responsible for+--   terminating lines appropriately.  Besides the line, the function+--   receives a flag indicating the intended \"persistence\" of the line:+--   'True' indicates that the line should be part of the final overall+--   report; 'False' indicates that the line merely indicates progress of+--   the test execution.  Each progress line shows the current values of+--   the cumulative test execution counts; a final, persistent line shows+--   the final count values.+--+--   The 'PutText' function is also passed, and returns, an arbitrary state+--   value (called 'st' here).  The initial state value is given in the+--   'PutText'; the final value is returned by 'runTestText'.++data PutText st = PutText (String -> Bool -> st -> IO st) st+++-- | Two reporting schemes are defined here.  @putTextToHandle@ writes+-- report lines to a given handle.  'putTextToShowS' accumulates+-- persistent lines for return as a whole by 'runTestText'.+--+-- @putTextToHandle@ writes persistent lines to the given handle,+-- following each by a newline character.  In addition, if the given flag+-- is @True@, it writes progress lines to the handle as well.  A progress+-- line is written with no line termination, so that it can be+-- overwritten by the next report line.  As overwriting involves writing+-- carriage return and blank characters, its proper effect is usually+-- only obtained on terminal devices.++putTextToHandle+    :: Handle+    -> Bool -- ^ Write progress lines to handle?+    -> PutText Int+putTextToHandle handle showProgress = PutText put initCnt+ where+  initCnt = if showProgress then 0 else -1+  put line pers (-1) = do when pers (hPutStrLn handle line); return (-1)+  put line True  cnt = do hPutStrLn handle (erase cnt ++ line); return 0+  put line False _   = do hPutStr handle ('\r' : line); return (length line)+    -- The "erasing" strategy with a single '\r' relies on the fact that the+    -- lengths of successive summary lines are monotonically nondecreasing.+  erase cnt = if cnt == 0 then "" else "\r" ++ replicate cnt ' ' ++ "\r"+++-- | Accumulates persistent lines (dropping progess lines) for return by+--   'runTestText'.  The accumulated lines are represented by a+--   @'ShowS' ('String' -> 'String')@ function whose first argument is the+--   string to be appended to the accumulated report lines.++putTextToShowS :: PutText ShowS+putTextToShowS = PutText put id+ where put line pers f = return (if pers then acc f line else f)+       acc f line rest = f (line ++ '\n' : rest)+++-- | Executes a test, processing each report line according to the given+--   reporting scheme.  The reporting scheme's state is threaded through calls+--   to the reporting scheme's function and finally returned, along with final+--   count values.++runTestText :: PutText st -> Test -> IO (Counts, st)+runTestText (PutText put us0) t = do+  (counts', us1) <- performTest reportStart reportError reportFailure us0 t+  us2 <- put (showCounts counts') True us1+  return (counts', us2)+ where+  reportStart ss us = put (showCounts (counts ss)) False us+  reportError   = reportProblem "Error:"   "Error in:   "+  reportFailure = reportProblem "Failure:" "Failure in: "+  reportProblem p0 p1 loc msg ss us = put line True us+   where line  = "### " ++ kind ++ path' ++ "\n" ++ formatLocation loc ++ msg+         kind  = if null path' then p0 else p1+         path' = showPath (path ss)++formatLocation :: Maybe SrcLoc -> String+formatLocation Nothing = ""+formatLocation (Just loc) = srcLocFile loc ++ ":" ++ show (srcLocStartLine loc) ++ "\n"++-- | Converts test execution counts to a string.++showCounts :: Counts -> String+showCounts Counts{ cases = cases', tried = tried',+                   errors = errors', failures = failures' } =+  "Cases: " ++ show cases' ++ "  Tried: " ++ show tried' +++  "  Errors: " ++ show errors' ++ "  Failures: " ++ show failures'+++-- | Converts a test case path to a string, separating adjacent elements by+--   the colon (\':\'). An element of the path is quoted (as with 'show') when+--   there is potential ambiguity.++showPath :: Path -> String+showPath [] = ""+showPath nodes = foldl1 f (map showNode nodes)+ where f b a = a ++ ":" ++ b+       showNode (ListItem n) = show n+       showNode (Label label) = safe label (show label)+       safe s ss = if ':' `elem` s || "\"" ++ s ++ "\"" /= ss then ss else s+++-- | Provides the \"standard\" text-based test controller. Reporting is made to+--   standard error, and progress reports are included. For possible+--   programmatic use, the final counts are returned.+--+--   The \"TT\" in the name suggests \"Text-based reporting to the Terminal\".++runTestTT :: Test -> IO Counts+runTestTT t = do (counts', 0) <- runTestText (putTextToHandle stderr True) t+                 return counts'++-- | Convenience wrapper for 'runTestTT'.+--   Simply runs 'runTestTT' and then exits back to the OS,+--   using 'exitSuccess' if there were no errors or failures,+--   or 'exitFailure' if there were. For example:+--+--   > tests :: Test+--   > tests = ...+--   >+--   > main :: IO ()+--   > main = runTestTTAndExit tests++runTestTTAndExit :: Test -> IO ()+runTestTTAndExit tests = do+  c <- runTestTT tests+  if (errors c == 0) && (failures c == 0)+    then exitSuccess+    else exitFailure
tests/HUnitTestBase.lhs view
@@ -1,7 +1,9 @@ HUnitTestBase.lhs  --  test support and basic tests (Haskell 98 compliant) +> {-# LANGUAGE CPP #-} > module HUnitTestBase where +> import Data.List > import Test.HUnit > import Test.HUnit.Terminal (terminalAppearance) > import System.IO (IOMode(..), openFile, hClose)@@ -26,8 +28,8 @@ > expectReports :: [Report] -> Counts -> Test -> Test > expectReports reports1 counts1 t = TestCase $ do >   (counts2, reports2) <- performTest (\  ss us -> return (Start     ss : us))->                                      (\m ss us -> return (Error   m ss : us))->                                      (\m ss us -> return (Failure m ss : us))+>                                      (\_loc m ss us -> return (Error   m ss : us))+>                                      (\_loc m ss us -> return (Failure m ss : us)) >                                      [] t >   assertEqual "for the reports from a test," reports1 (reverse reports2) >   assertEqual "for the counts from a test," counts1 counts2@@ -79,7 +81,7 @@ > ok :: Test > ok = test (assert ()) > bad :: String -> Test-> bad m = test (assertFailure m)+> bad m = test (assertFailure m :: Assertion)   > assertTests :: Test@@ -88,11 +90,7 @@ >   "null" ~: expectSuccess ok,  >   "userError" ~:-#if defined(__GLASGOW_HASKELL__) >     expectError "user error (error)" (TestCase (ioError (userError "error"))),-#else->     expectError "error" (TestCase (ioError (userError "error"))),-#endif  >   "IO error (file missing)" ~: >     expectUnspecifiedError@@ -110,7 +108,7 @@  >   "assertFailure" ~: >     let msg = "simple assertFailure"->     in expectFailure msg (test (assertFailure msg)),+>     in expectFailure msg (test (assertFailure msg :: Assertion)),  >   "assertString null" ~: expectSuccess (TestCase (assertString "")), @@ -257,12 +255,14 @@ > reportTests :: Test > reportTests = "reports" ~: expectReports suiteReports suiteCounts suite +> removeLocation :: String -> String+> removeLocation = unlines . filter (not . isInfixOf __FILE__) . lines  > expectText :: Counts -> String -> Test -> Test > expectText counts1 text1 t = TestCase $ do >   (counts2, text2) <- runTestText putTextToShowS t >   assertEqual "for the final counts," counts1 counts2->   assertEqual "for the failure text output," text1 (text2 "")+>   assertEqual "for the failure text output," text1 (removeLocation $ text2 "")   > textTests :: Test@@ -270,11 +270,7 @@  >   "lone error" ~: >     expectText (Counts 1 1 1 0)-#if defined(__GLASGOW_HASKELL__) >         "### Error:\nuser error (xyz)\nCases: 1  Tried: 1  Errors: 1  Failures: 0\n"-#else->         "### Error:\nxyz\nCases: 1  Tried: 1  Errors: 1  Failures: 0\n"-#endif >         (test (do _ <- ioError (userError "xyz"); return ())),  >   "lone failure" ~:@@ -295,7 +291,7 @@ >           hClose handle >           assertEqual "for the final counts," suiteCounts counts' >           text <- readFile filename->           let text' = if flag then trim (terminalAppearance text) else text+>           let text' = removeLocation $ if flag then trim (terminalAppearance text) else text >           assertEqual "for the failure text output," suiteOutput text' >       | flag <- [False, True] ] 
tests/HUnitTestExtended.hs view
@@ -1,63 +1,19 @@-module HUnitTestExtended (-    extendedTests-    ) where+module HUnitTestExtended (extendedTests) where  import Test.HUnit import HUnitTestBase --- Notes:--- * Assertion testing is only performed on GHC.  If you want this to be enabled---   for other compilers, email the HUnit maintainer.--#ifdef __GLASGOW_HASKELL__-import qualified Control.Exception (assert)--#ifdef O0-import System.FilePath--assertionMessage :: String-assertionMessage = concat [-    "tests",-    [pathSeparator],-    "HUnitTestExtended.hs:27:13-36: Assertion failed\n"-    ]-#endif--assertion :: IO ()-assertion = Control.Exception.assert False (return ())-#endif-- extendedTests :: Test extendedTests = test [--    -- Hugs doesn't currently catch arithmetic exceptions.-         "div by 0" ~:-        expectUnspecifiedError (TestCase ((3 `div` 0 :: Integer) `seq` return ())),+        expectError "divide by zero" (TestCase ((3 `div` 0 :: Integer) `seq` return ())),      "list ref out of bounds" ~:         expectUnspecifiedError (TestCase ([1 .. 4 :: Integer] !! 10 `seq` return ())), -    "error" ~:-        expectError "error" (TestCase (error "error")),+     "error" ~:+        expectUnspecifiedError (TestCase (error "error")),      "tail []" ~:         expectUnspecifiedError (TestCase (tail [] `seq` return ()))--#ifdef __GLASGOW_HASKELL__-#ifdef O0--- Run with no optimization (-O0)-    ,-    "assert" ~:-         expectError assertionMessage (TestCase assertion)-#else--- #ifdef O0--- Run with optimization (-O1 or -O2)-    ,-    "assert" ~: TestCase assertion--- #ifdef O0-#endif--- #ifdef __GLASGOW_HASKELL__-#endif     ]
− tests/HUnitTestOptimize.hs
@@ -1,113 +0,0 @@--- HUnitTestOptimize.hs------ The purpose of this file is to test whether certain issues occur with optimization.--- It should be built and run with each level of optimization.  I.e., -O0, -O1, -O2------ With some versions and optimization levels of HUnit and GHC, tests were getting--- optimized out.  This is a very bad thing and needs to be tested for.--module HUnitTestOptimize (-    optimizationTests,-    undefinedSwallowsTests-    ) where--import Control.Applicative-import Control.Monad-import Test.HUnit---- Used to include the optimization level in the test results-optimizationLevel :: String-#if defined(O0)-optimizationLevel = "-O0"-#elif defined(O1)-optimizationLevel = "-O1"-#elif defined(O2)-optimizationLevel = "-O2"-#else-optimizationLevel = "unknown optimization level"-#endif---- A test runner that doesn't print the results of the tests; it only tabulates--- the results of the tests.  In this context, it's used to verify that no--- tests were optimized away or otherwise lost.-simpleTestRunner :: Test -> IO Counts-simpleTestRunner t = do -    (counts', _) <- runTestText nullAccum t-    return counts'-    where-        nullAccum = PutText (\ _ _ _ -> return ()) ()---- Some combinations of HUnit, GHC, and optimization levels cause tests to be--- optimized away.  This section verifies that all tests of a type are--- performed.-optimizationTests :: IO Bool-optimizationTests = do-    counts2 <- simpleTestRunner $ TestLabel "Basic Optimization Tests" $ TestList [ -        True ~=? True,-        False ~=? True,-        TestCase $ assertEqual "both true" True True,-        TestCase $ assertEqual "false true" False True,-        TestCase $ assertEqual "fa" False True,-        TestCase $ assertEqual "f" False True,-        TestCase $ (False @?= True),-        TestCase $ unless (False == True) (assertFailure "f")-        ]-    -- Verify results of counts2-    -- We can't use HUnit because it's possible that some tests have been-    -- optimized away, so we'll just do it manually.-    foldr (&&) True <$> sequence [-        caseCount counts2 optimizationLevel, -        tryCount counts2 optimizationLevel, -        errorCount counts2 optimizationLevel, -        failureCount counts2 optimizationLevel-        ]-    where-        caseCount cs ol = if (cases cs == 8) -                    then return True-                    else do-                        putStrLn $ "Failure: Basic Optimization (" ++ -                                ol ++ "): expected 8 test cases; only " ++ -                                (show $ cases cs) ++ -                                " found.  Some may have been optimized out."-                        return False-        tryCount cs ol = if (tried cs == 8) -                    then return True-                    else do-                        putStrLn $ "Failure: Basic Optimization (" ++ -                            ol ++ "): expected to try 8 test cases; only " ++ -                            (show $ tried cs) ++ -                            " tried.  Some may have been optimized out."-                        return False-        errorCount cs ol = if (errors cs == 0) -                    then return True-                    else do-                        putStrLn $ "Failure: Basic Optimization (" ++ -                            ol ++ "): expected 0 errors; " ++ -                            (show $ errors cs) ++ " found."-                        return False-        failureCount cs ol = if (failures cs == 6) -                    then return True-                    else do-                        putStrLn $ "Failure: Basic Optimization (" ++ -                            ol ++ "): expected 6 failed cases; only " ++ -                            (show $ failures cs) ++ -                            " failed.  Some may have been optimized out."-                        return False---- Added in 1.4.2.3--- When certain errors occur in a list of tests, the subsequent tests in the--- list weren't being run.  This test verifies that this does not happen.-undefinedSwallowsTests :: Test-undefinedSwallowsTests = TestLabel ("Undefined Swallows Tests (" ++ optimizationLevel ++ ")") $ TestList [-        TestCase $ do-            rs <- simpleTestRunner . TestList $ [-                -- Added in 1.2.4.3 because the second test case will never be run-                -- (in prior versions)-                TestCase $ ('f' : undefined) @?= "bar",-                TestCase $ "foo" @?= "bar"-                ]-            assertEqual -                "(cases,tried,errors,failures)" -                (cases rs, tried rs, errors rs, failures rs)-                (2, 2, 1, 1)-        ]
tests/HUnitTests.hs view
@@ -9,18 +9,16 @@ import Test.HUnit import HUnitTestBase import HUnitTestExtended-import HUnitTestOptimize import TerminalTest+import Example ()  main :: IO () main = do     counts2 <- runTestTT (test [-            baseTests, +            baseTests,             extendedTests,-            undefinedSwallowsTests,             terminalTests             ])-    oPassed <- optimizationTests-    if (errors counts2 + failures counts2 == 0 && oPassed) +    if (errors counts2 + failures counts2 == 0)         then exitSuccess         else exitFailure