packages feed

HUnit 1.2.0.0 → 1.2.0.1

raw patch · 12 files changed

+1295/−28 lines, 12 filesdep ~basenew-uploaderPVP: minor bump suggested

API additions: PVP suggests at least a minor version bump

Dependency ranges changed: base

API changes (from Hackage documentation)

+ Test.HUnit.Lang: instance Exception HUnitFailure
+ Test.HUnit.Lang: instance Show HUnitFailure

Files

HUnit.cabal view
@@ -1,20 +1,25 @@-name:		HUnit-version:	1.2.0.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+name:           HUnit+version:        1.2.0.1+license:        BSD3+license-file:   LICENSE+author:         Dean Herington+homepage:       http://hunit.sourceforge.net/+category:       Testing+synopsis:       A unit testing framework for Haskell+maintainer:     libraries@haskell.org+cabal-version:  >= 1.2 description:-	HUnit is a unit testing framework for Haskell, inspired by the-	JUnit tool for Java, see: <http://www.junit.org>.+        HUnit is a unit testing framework for Haskell, inspired by the+        JUnit tool for Java, see: <http://www.junit.org>. build-type:     Simple-exposed-modules:-	Test.HUnit.Base,-	Test.HUnit.Lang,-	Test.HUnit.Terminal,-	Test.HUnit.Text,-	Test.HUnit-extensions:	CPP++Library+    build-depends: base+    exposed-modules:+        Test.HUnit.Base,+        Test.HUnit.Lang,+        Test.HUnit.Terminal,+        Test.HUnit.Text,+        Test.HUnit+    extensions: CPP+
+ README view
@@ -0,0 +1,12 @@+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.
Test/HUnit/Lang.lhs view
@@ -20,7 +20,7 @@ > import Data.List (isPrefixOf) #if defined(__GLASGOW_HASKELL__) || defined(__HUGS__) > import Data.Dynamic-> import Control.Exception as E         ( throwDyn, try, Exception(..) )+> import Control.Exception as E #else > import System.IO.Error (ioeGetErrorString, try) #endif@@ -52,7 +52,10 @@  #if defined(__GLASGOW_HASKELL__) || defined(__HUGS__) > data HUnitFailure = HUnitFailure String+>     deriving Show >+> instance Exception HUnitFailure+> > hunitFailureTc :: TyCon > hunitFailureTc = mkTyCon "HUnitFailure" > {-# NOINLINE hunitFailureTc #-}@@ -60,17 +63,14 @@ > instance Typeable HUnitFailure where >     typeOf _ = mkTyConApp hunitFailureTc [] -> assertFailure msg = E.throwDyn (HUnitFailure msg)+> assertFailure msg = E.throw (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)+>     do action+>        return Nothing+>      `E.catches`+>        [E.Handler (\(HUnitFailure msg) -> return $ Just (True, msg)),+>         E.Handler (\e -> return $ Just (False, show (e :: E.SomeException)))] #else > hunitPrefix = "HUnit:" 
+ doc/Guide.html view
@@ -0,0 +1,711 @@+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">+<html>+<head>+  <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">+  <meta name="Author" content="Dean Herington">+  <meta name="KeyWords" content="HUnit, unit testing, test-first development, Haskell, JUnit">+  <meta name="Content-Type" content="text/html; charset=iso-8859-1">+  <title>HUnit 1.0 User's Guide</title>+</head>+<body>++<h1>HUnit 1.0 User's Guide</h1>++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://hunit.sourceforge.net">http://hunit.sourceforge.net</a>.++<h2>Introduction</h2>++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>+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>+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.++<h2><a name="GettingStarted">Getting Started</a></h2>++In the Haskell module where your tests will reside, import module+<tt>Test.HUnit</tt>:+<pre>+    import Test.HUnit+</pre>+Define test cases as appropriate:+<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>+Name the test cases and group them together:+<pre>+    tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]+</pre>+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 <b><u>t</u></b>ext orientation with output to+the <b><u>t</u></b>erminal.)+<pre>+    > runTestTT tests+    Cases: 2  Tried: 2  Errors: 0  Failures: 0+    >+</pre>+If the tests are proving their worth, you might see:+<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>+Isn't that easy?+<p>+You can specify tests even more succinctly using operators and+overloaded functions that HUnit provides:+<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>+Assuming the same test failures as before, you would see:+<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><a name="WritingTests"></a>Writing Tests</h2>++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.++<h3><a name="Assertions"></a>Assertions</h3>++The basic building block of a test is an <b>assertion</b>.+<pre>+    type Assertion = IO ()+</pre>+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>.+<pre>+    assertFailure :: String -> Assertion+    assertFailure msg = ioError (userError ("HUnit:" ++ msg))+</pre>+<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>+<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.+<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>+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>+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.++<h3><a name="TestCase"></a>Test Case</h3>++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>+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>+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>.++<h3><a name="Tests"></a>Tests</h3>++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>+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.+<pre>+    data Test = TestCase Assertion+              | TestList [Test]+              | TestLabel String Test+</pre>+There are three important features of this definition to note:+<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>.+<pre>+    testCaseCount :: Test -> Int+</pre>+<p>+As mentioned above, a test is identified by its <b>path</b> in the+test hierarchy.+<pre>+    data Node  = ListItem Int | Label String+      deriving (Eq, Show, Read)++    type Path = [Node]    -- Node order is from test case to root.+</pre>+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>+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>+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.+<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>+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:+<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><a name="AdvancedFeatures"></a>Advanced Features</h3>++HUnit provides additional features for specifying assertions and tests+more conveniently and concisely. These facilities make use of+Haskell type classes.+<p>+The following operators can be used to construct assertions.+<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>+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>+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>.+<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>+The overloaded <tt>assert</tt> function in the <tt>Assertable</tt>+type class constructs an assertion.+<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>+The <tt>ListAssertable</tt> class allows <tt>assert</tt> to be applied+to <tt>[Char]</tt> (that is, <tt>String</tt>).+<pre>+    class ListAssertable t+     where listAssert :: [t] -> Assertion++    instance ListAssertable Char+     where listAssert = assertString+</pre>+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>+The overloaded <tt>test</tt> function in the <tt>Testable</tt> type+class constructs a test.+<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>+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>+The following operators can be used to construct tests.+<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>+<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.++<h2><a name="RunningTests"></a>Running Tests</h2>++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.++<h3><a name="TestExecution">Test Execution</a></h3>++All test controllers share a common test execution model. They+differ only in how the results of test execution are shown.+<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:+<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>+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>+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.)+<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>+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.++<h3><a name="Text-BasedController">Text-Based Controller</a></h3>++A text-based test controller is included with HUnit.+<pre>+    runTestText :: PutText st -> Test -> IO (Counts, st)+</pre>+<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>+The strings for the three kinds of reporting event are as follows.+<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.+<pre>+    showCounts :: Counts -> String+</pre>+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>+The function <tt>showPath</tt> shows a test case path.+<pre>+    showPath :: Path -> String+</pre>+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>+HUnit includes two reporting schemes for the text-based test+controller. You may define others if you wish.+<pre>+    putTextToHandle :: Handle -> Bool -> PutText Int+</pre>+<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.+<pre>+    putTextToShowS :: PutText ShowS+</pre>+<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>+HUnit provides a shorthand for the most common use of the text-based+test controller.+<pre>+    runTestTT :: Test -> IO Counts+</pre>+<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.++<h2><a name="ConstituentFiles">Constituent Files</a></h2>++HUnit 1.0 consists of the following files.+<dl>++<dt> doc/Guide.html+<dd>+This document.+<dt> examples/Example.hs+<dd>+Haskell module that includes the examples given in the <a+href="#GettingStarted">Getting Started</a> section. Run this+program to make sure you understand how to use HUnit.+<dt> Test/HUnit.lhs+<dd>+Haskell module that you import to use HUnit.+<dt> Test/HUnit/Base.lhs+<dd>+Haskell module that defines HUnit's basic facilities.+<dt> Test/HUnit/Lang.lhs+<dd>+Haskell module that defines how assertion failure is signaled and+caught. By default, it is a copy of+<tt>Test/HUnit/Lang98.lhs</tt>. Replace it by a copy of+<tt>Test/HUnit/LangExtended.lhs</tt> for more robust exception behavior.+<dt> Test/HUnit/Lang98.lhs+<dd>+Haskell module that defines generic assertion failure handling.&nbsp;+It is compliant to Haskell 98 but catches only <tt>IO</tt> errors.+<dt> Test/HUnit/LangExtended.lhs+<dd>+Haskell module that defines more robust assertion failure+handling. It catches more (though unfortunately not all) kinds+of exceptions. However, it works only with Hugs (Dec. 2001 or+later) and GHC (5.00 and later).+<dt> examples/test/HUnitTest98.lhs+<dd>+Haskell module that tests HUnit, assuming the generic assertion+failure handling of <tt>HUnitLang98.lhs</tt>.+<dt> examples/test/HUnitTestBase.lhs+<dd>+Haskell module that defines testing support and basic (Haskell 98+compliant) tests of HUnit (using HUnit, of course!). Contains+more extensive and advanced examples of testing with HUnit.+<dt> examples/test/HUnitTestExtended.lhs+<dd>+Haskell module that tests HUnit, assuming the extended assertion+failure handling of <tt>HUnitLangExc.lhs</tt>.+<dt> Test/HUnit/Text.lhs+<dd>+Haskell module that defines HUnit's text-based test controller.+<dt> LICENSE+<dd>+The license for use of HUnit.+<dt> Test/HUnit/Terminal.lhs+<dd>+Haskell module that assists in checking the output of HUnit tests+performed by the text-based test controller.+<dt> examples/test/TerminalTest.lhs+<dd>+Haskell module that tests <tt>Test/HUnit/Terminal.lhs</tt> (using HUnit, of+course!).+</dl>++<h2><a name="References">References</a></h2>++<dl>++<dt>+<a name="DesignPatterns"></a>[1] Gamma, E., et al. Design Patterns:+Elements of Reusable Object-Oriented Software, Addison-Wesley,+Reading, MA, 1995.+<dd>+The classic book describing design patterns in an object-oriented+context.++<dt>+<a href="http://www.junit.org">http://www.junit.org</a>+<dd>+Web page for JUnit, the tool after which HUnit is modeled.++<dt>+<a href="http://junit.sourceforge.net/doc/testinfected/testing.htm">+http://junit.sourceforge.net/doc/testinfected/testing.htm</a>+<dd>+A good introduction to test-first development and the use of JUnit.++<dt>+<a href="http://junit.sourceforge.net/doc/cookstour/cookstour.htm">+http://junit.sourceforge.net/doc/cookstour/cookstour.htm</a>+<dd>+A description of the internal structure of JUnit. Makes for an+interesting comparison between JUnit and HUnit.++</dl>++<p>+<hr>++The HUnit software and this guide were written by Dean Herington+(<a href="mailto:heringto@cs.unc.edu">heringto@cs.unc.edu</a>).++<p>+HUnit development is supported by+<a href="http://sourceforge.net">+<img src="http://sourceforge.net/sflogo.php?group_id=46796&amp;type=1"+     width="88" height="31" border="0" alt="SourceForge.net Logo">+</a>+</body>+</html>
+ examples/Example.hs view
@@ -0,0 +1,37 @@+-- Example.hs  --  Examples from HUnit user's guide++module Main where++import Test.HUnit+++foo :: Int -> (Int, Int)+foo x = (1, x)++partA :: Int -> IO (Int, Int)+partA v = return (v+2, v+3)++partB :: Int -> IO Bool+partB v = return (v > 5)++test1 :: Test+test1 = TestCase (assertEqual "for (foo 3)," (1,2) (foo 3))++test2 :: Test+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)++tests :: Test+tests = TestList [TestLabel "test1" test1, TestLabel "test2" test2]++tests' :: Test+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" ]++main :: IO Counts+main = do runTestTT tests+          runTestTT tests'
+ examples/Makefile view
@@ -0,0 +1,27 @@+# -----------------------------------------------------------------------------++TOP = ../..+include $(TOP)/mk/boilerplate.mk++# -----------------------------------------------------------------------------++ifeq "$(way)" ""+SUBDIRS = test++EXAMPLES    := $(wildcard *.hs)+BINS        := $(addsuffix $(exeext),$(EXAMPLES:.hs=))+CLEAN_FILES += $(BINS)++HC           = $(GHC_INPLACE)+MKDEPENDHS   = $(GHC_INPLACE)+SRC_HC_OPTS += -Wall -package HUnit++all:: $(BINS)++$(BINS): %$(exeext): %.o+	$(HC) -o $@ $(HC_OPTS) $(LD_OPTS) $<+endif++# -----------------------------------------------------------------------------++include $(TOP)/mk/target.mk
+ examples/test/HUnitTest98.lhs view
@@ -0,0 +1,9 @@+HUnitTest98.lhs  --  test for HUnit, using Haskell language system "98"++> module Main (main) where++> import Test.HUnit+> import HUnitTestBase++> main :: IO Counts+> main = runTestTT (test [baseTests])
+ examples/test/HUnitTestBase.lhs view
@@ -0,0 +1,373 @@+HUnitTestBase.lhs  --  test support and basic tests (Haskell 98 compliant)++> module HUnitTestBase where++> import Test.HUnit+> import Test.HUnit.Terminal (terminalAppearance)+> import System.IO (IOMode(..), openFile, hClose)+++> data Report = Start State+>             | Error String State+>             | UnspecifiedError State+>             | Failure String State+>   deriving (Show, Read)++> instance Eq Report where+>   Start s1            == Start s2             =  s1 == s2+>   Error m1 s1         == Error m2 s2          =  m1 == m2 && s1 == s2+>   Error m1 s1         == UnspecifiedError s2  =  s1 == s2+>   UnspecifiedError s1 == Error m2 s2          =  s1 == s2+>   UnspecifiedError s1 == UnspecifiedError s2  =  s1 == s2+>   Failure m1 s1       == Failure m2 s2        =  m1 == m2 && s1 == s2+>   _                   == _                    =  False+++> expectReports :: [Report] -> Counts -> Test -> Test+> expectReports reports counts test = TestCase $ do+>   (counts', reports') <- performTest (\  ss us -> return (Start     ss : us))+>                                      (\m ss us -> return (Error   m ss : us))+>                                      (\m ss us -> return (Failure m ss : us))+>                                      [] test+>   assertEqual "for the reports from a test," reports (reverse reports')+>   assertEqual "for the counts from a test," counts counts'+++> simpleStart = Start (State [] (Counts 1 0 0 0))++> expectSuccess :: Test -> Test+> expectSuccess = expectReports [simpleStart] (Counts 1 1 0 0)++> expectProblem :: (String -> State -> Report) -> Int -> String -> Test -> Test+> expectProblem kind err msg =+>   expectReports [simpleStart, kind msg (State [] counts)] counts+>  where counts = Counts 1 1 err (1-err)++> expectError, expectFailure :: String -> Test -> Test+> expectError   = expectProblem Error   1+> expectFailure = expectProblem Failure 0++> expectUnspecifiedError :: Test -> Test+> expectUnspecifiedError = expectProblem (\ msg st -> UnspecifiedError st) 1 undefined+++> data Expect = Succ | Err String | UErr | Fail String++> expect :: Expect -> Test -> Test+> expect Succ     test = expectSuccess test+> expect (Err m)  test = expectError m test+> expect UErr     test = expectUnspecifiedError test+> expect (Fail m) test = expectFailure m test++++> baseTests = test [ assertTests,+>                    testCaseCountTests,+>                    testCasePathsTests,+>                    reportTests,+>                    textTests,+>                    showPathTests,+>                    showCountsTests,+>                    assertableTests,+>                    predicableTests,+>                    compareTests,+>                    extendedTestTests ]+++> ok = test (assert ())+> bad m = test (assertFailure m)+++> assertTests = test [++>   "null" ~: expectSuccess ok,++>   "userError" ~:+>     expectError "error" (TestCase (ioError (userError "error"))),++>   "IO error (file missing)" ~:+>     expectUnspecifiedError+>       (test (do openFile "3g9djs" ReadMode; return ())),++   "error" ~:+     expectError "error" (TestCase (error "error")),++   "tail []" ~:+     expectUnspecifiedError (TestCase (tail [] `seq` return ())),++    -- GHC doesn't currently catch arithmetic exceptions.+   "div by 0" ~:+     expectUnspecifiedError (TestCase ((3 `div` 0) `seq` return ())),++>   "assertFailure" ~:+>     let msg = "simple assertFailure"+>     in expectFailure msg (test (assertFailure msg)),++>   "assertString null" ~: expectSuccess (TestCase (assertString "")),++>   "assertString nonnull" ~:+>     let msg = "assertString nonnull"+>     in expectFailure msg (TestCase (assertString msg)),++>   let exp v non =+>         show v ++ " with " ++ non ++ "null message" ~:+>           expect (if v then Succ else Fail non) $ test $ assertBool non v+>   in "assertBool" ~: [ exp v non | v <- [True, False], non <- ["non", ""] ],++>   let msg = "assertBool True"+>   in msg ~: expectSuccess (test (assertBool msg True)),++>   let msg = "assertBool False"+>   in msg ~: expectFailure msg (test (assertBool msg False)),++>   "assertEqual equal" ~:+>     expectSuccess (test (assertEqual "" 3 3)),++>   "assertEqual unequal no msg" ~:+>     expectFailure "expected: 3\n but got: 4"+>       (test (assertEqual "" 3 4)),++>   "assertEqual unequal with msg" ~:+>     expectFailure "for x,\nexpected: 3\n but got: 4"+>       (test (assertEqual "for x," 3 4))++>  ]+++> emptyTest0 = TestList []+> emptyTest1 = TestLabel "empty" emptyTest0+> emptyTest2 = TestList [ emptyTest0, emptyTest1, emptyTest0 ]+> emptyTests = [emptyTest0, emptyTest1, emptyTest2]++> testCountEmpty test = TestCase (assertEqual "" 0 (testCaseCount test))++> suite0 = (0, ok)+> suite1 = (1, TestList [])+> suite2 = (2, TestLabel "3" ok)+> suite3 = (3, suite)++> suite =+>   TestLabel "0"+>     (TestList [ TestLabel "1" (bad "1"),+>                 TestLabel "2" (TestList [ TestLabel "2.1" ok,+>                                           ok,+>                                           TestLabel "2.3" (bad "2") ]),+>                 TestLabel "3" (TestLabel "4" (TestLabel "5" (bad "3"))),+>                 TestList [ TestList [ TestLabel "6" (bad "4") ] ] ])++> suiteCount = (6 :: Int)++> suitePaths = [+>   [Label "0", ListItem 0, Label "1"],+>   [Label "0", ListItem 1, Label "2", ListItem 0, Label "2.1"],+>   [Label "0", ListItem 1, Label "2", ListItem 1],+>   [Label "0", ListItem 1, Label "2", ListItem 2, Label "2.3"],+>   [Label "0", ListItem 2, Label "3", Label "4", Label "5"],+>   [Label "0", ListItem 3, ListItem 0, ListItem 0, Label "6"]]++> suiteReports = [ Start       (State (p 0) (Counts 6 0 0 0)),+>                  Failure "1" (State (p 0) (Counts 6 1 0 1)),+>                  Start       (State (p 1) (Counts 6 1 0 1)),+>                  Start       (State (p 2) (Counts 6 2 0 1)),+>                  Start       (State (p 3) (Counts 6 3 0 1)),+>                  Failure "2" (State (p 3) (Counts 6 4 0 2)),+>                  Start       (State (p 4) (Counts 6 4 0 2)),+>                  Failure "3" (State (p 4) (Counts 6 5 0 3)),+>                  Start       (State (p 5) (Counts 6 5 0 3)),+>                  Failure "4" (State (p 5) (Counts 6 6 0 4))]+>  where p n = reverse (suitePaths !! n)++> suiteCounts = Counts 6 6 0 4++> suiteOutput = "### Failure in: 0:0:1\n\+>               \1\n\+>               \### Failure in: 0:1:2:2:2.3\n\+>               \2\n\+>               \### Failure in: 0:2:3:4:5\n\+>               \3\n\+>               \### Failure in: 0:3:0:0:6\n\+>               \4\n\+>               \Cases: 6  Tried: 6  Errors: 0  Failures: 4\n"+++> suites = [suite0, suite1, suite2, suite3]+++> testCount (num, test) count =+>   "testCaseCount suite" ++ show num ~:+>     TestCase $ assertEqual "for test count," count (testCaseCount test)++> testCaseCountTests = TestList [++>   "testCaseCount empty" ~: test (map testCountEmpty emptyTests),++>   testCount suite0 1,+>   testCount suite1 0,+>   testCount suite2 1,+>   testCount suite3 suiteCount++>  ]+++> testPaths (num, test) paths =+>   "testCasePaths suite" ++ show num ~:+>     TestCase $ assertEqual "for test paths,"+>                             (map reverse paths) (testCasePaths test)++> testPathsEmpty test = TestCase $ assertEqual "" [] (testCasePaths test)++> testCasePathsTests = TestList [++>   "testCasePaths empty" ~: test (map testPathsEmpty emptyTests),++>   testPaths suite0 [[]],+>   testPaths suite1 [],+>   testPaths suite2 [[Label "3"]],+>   testPaths suite3 suitePaths++>  ]+++> reportTests = "reports" ~: expectReports suiteReports suiteCounts suite+++> expectText counts text test = TestCase $ do+>   (counts', text') <- runTestText putTextToShowS test+>   assertEqual "for the final counts," counts counts'+>   assertEqual "for the failure text output," text (text' "")+++> textTests = test [++>   "lone error" ~:+>     expectText (Counts 1 1 1 0)+>         "### Error:\nxyz\nCases: 1  Tried: 1  Errors: 1  Failures: 0\n"+>         (test (do ioError (userError "xyz"); return ())),++>   "lone failure" ~:+>     expectText (Counts 1 1 0 1)+>         "### Failure:\nxyz\nCases: 1  Tried: 1  Errors: 0  Failures: 1\n"+>         (test (assert "xyz")),++>   "putTextToShowS" ~:+>     expectText suiteCounts suiteOutput suite,++>   "putTextToHandle (file)" ~:+>     let filename = "HUnitTest.tmp"+>         trim = unlines . map (reverse . dropWhile (== ' ') . reverse) . lines+>     in map test+>       [ "show progress = " ++ show flag ~: do+>           handle <- openFile filename WriteMode+>           (counts, _) <- runTestText (putTextToHandle handle flag) suite+>           hClose handle+>           assertEqual "for the final counts," suiteCounts counts+>           text <- readFile filename+>           let text' = if flag then trim (terminalAppearance text) else text+>           assertEqual "for the failure text output," suiteOutput text'+>       | flag <- [False, True] ]++>  ]+++> showPathTests = "showPath" ~: [++>   "empty"  ~: showPath [] ~?= "",+>   ":"      ~: showPath [Label ":", Label "::"] ~?= "\"::\":\":\"",+>   "\"\\\n" ~: showPath [Label "\"\\n\n\""] ~?= "\"\\\"\\\\n\\n\\\"\"",+>   "misc"   ~: showPath [Label "b", ListItem 2, ListItem 3, Label "foo"] ~?=+>                        "foo:3:2:b"++>  ]+++> showCountsTests = "showCounts" ~: showCounts (Counts 4 3 2 1) ~?=+>                             "Cases: 4  Tried: 3  Errors: 2  Failures: 1"++++> lift :: a -> IO a+> lift a = return a+++> assertableTests =+>   let assertables x = [+>         (       "", assert             x  , test             (lift x))  ,+>         (    "IO ", assert       (lift x) , test       (lift (lift x))) ,+>         ( "IO IO ", assert (lift (lift x)), test (lift (lift (lift x))))]+>       assertabled l e x =+>         test [ test [ "assert" ~: pre ++ l          ~: expect e $ test $ a,+>                       "test"   ~: pre ++ "IO " ++ l ~: expect e $ t ]+>                | (pre, a, t) <- assertables x ]+>   in "assertable" ~: [+>     assertabled "()"    Succ       (),+>     assertabled "True"  Succ       True,+>     assertabled "False" (Fail "")  False,+>     assertabled "\"\""  Succ       "",+>     assertabled "\"x\"" (Fail "x") "x"+>    ]+++> predicableTests =+>   let predicables x m = [+>         (       "", assertionPredicate      x  ,     x  @? m,     x  ~? m ),+>         (    "IO ", assertionPredicate   (l x) ,   l x  @? m,   l x  ~? m ),+>         ( "IO IO ", assertionPredicate (l(l x)), l(l x) @? m, l(l x) ~? m )]+>       l x = lift x+>       predicabled l e m x =+>         test [ test [ "pred" ~: pre ++ l ~: m ~: expect e $ test $ tst p,+>                       "(@?)" ~: pre ++ l ~: m ~: expect e $ test $ a,+>                       "(~?)" ~: pre ++ l ~: m ~: expect e $ t ]+>                                    | (pre, p, a, t) <- predicables x m ]+>        where tst p = p >>= assertBool m+>   in "predicable" ~: [+>     predicabled "True"  Succ           "error" True,+>     predicabled "False" (Fail "error") "error" False,+>     predicabled "True"  Succ           ""      True,+>     predicabled "False" (Fail ""     ) ""      False+>    ]+++> compareTests = test [++>   let succ = const Succ+>       compare f exp act = test [ "(@=?)" ~: expect e $ test (exp @=? act),+>                                  "(@?=)" ~: expect e $ test (act @?= exp),+>                                  "(~=?)" ~: expect e $       exp ~=? act,+>                                  "(~?=)" ~: expect e $       act ~?= exp ]+>        where e = f $ "expected: " ++ show exp ++ "\n but got: " ++ show act+>   in test [+>     compare succ 1 1,+>     compare Fail 1 2,+>     compare succ (1,'b',3.0) (1,'b',3.0),+>     compare Fail (1,'b',3.0) (1,'b',3.1)+>    ]++>  ]+++> expectList1 :: Int -> Test -> Test+> expectList1 c =+>   expectReports+>     [ Start (State [ListItem n] (Counts c n 0 0)) | n <- [0..c-1] ]+>                                 (Counts c c 0 0)++> expectList2 :: [Int] -> Test -> Test+> expectList2 cs test =+>   expectReports+>     [ Start (State [ListItem j, ListItem i] (Counts c n 0 0))+>         | ((i,j),n) <- zip coords [0..] ]+>                                             (Counts c c 0 0)+>                    test+>  where coords = [ (i,j) | i <- [0 .. length cs - 1], j <- [0 .. cs!!i - 1] ]+>        c = testCaseCount test+++> extendedTestTests = test [++>   "test idempotent" ~: expect Succ $ test $ test $ test $ ok,++>   "test list 1" ~: expectList1 3 $ test [assert (), assert "", assert True],++>   "test list 2" ~: expectList2 [0, 1, 2] $ test [[], [ok], [ok, ok]]++>  ]
+ examples/test/HUnitTestExtended.lhs view
@@ -0,0 +1,38 @@+HUnitTestExc.lhs  --  test for HUnit, using Haskell language system "Exc"++> module Main (main) where++> import Test.HUnit+> import HUnitTestBase++ import qualified Control.Exception (assert)++ assertionMessage = "HUnitTestExc.lhs:13: Assertion failed\n"+ assertion = Control.Exception.assert False (return ())+++> main :: IO Counts+> main = runTestTT (test [baseTests, excTests])++> excTests :: Test+> excTests = test [++    -- Hugs and GHC don't currently catch arithmetic exceptions.+   "div by 0" ~:+     expectUnspecifiedError (TestCase ((3 `div` 0) `seq` return ())),++    -- GHC doesn't currently catch array-related exceptions.+   "array ref out of bounds" ~:+     expectUnspecifiedError (TestCase (... `seq` return ())),++>   "error" ~:+>     expectError "error" (TestCase (error "error")),++>   "tail []" ~:+>     expectUnspecifiedError (TestCase (tail [] `seq` return ()))++   -- Hugs doesn't provide `assert`.+   "assert" ~:+     expectError assertionMessage (TestCase assertion)++>  ]
+ examples/test/Makefile view
@@ -0,0 +1,29 @@+# -----------------------------------------------------------------------------++TOP = ../../..+include $(TOP)/mk/boilerplate.mk++# -----------------------------------------------------------------------------++EXAMPLES    := $(filter-out HUnitTestBase.lhs,$(wildcard *.lhs))+BINS        := $(addsuffix $(exeext),$(EXAMPLES:.lhs=))+CLEAN_FILES += $(BINS)++HC           = $(GHC_INPLACE)+MKDEPENDHS   = $(GHC_INPLACE)+SRC_HC_OPTS += -Wall -package HUnit++all:: $(BINS)++USES_HUNITTESTBASE := $(EXAMPLES:.lhs=)++.PRECIOUS: HUnitTestBase.o+$(addsuffix .o,$(USES_HUNITTESTBASE)): HUnitTestBase.hi+$(addsuffix $(exeext),$(USES_HUNITTESTBASE)): HUnitTestBase.o++$(BINS): %$(exeext): %.o+	$(HC) -o $@ $(HC_OPTS) $(LD_OPTS) $< $(patsubst %,HUnitTestBase.o,$(filter $(<:.o=),$(USES_HUNITTESTBASE)))++# -----------------------------------------------------------------------------++include $(TOP)/mk/target.mk
+ examples/test/TerminalTest.lhs view
@@ -0,0 +1,24 @@+TerminalTest.lhs++> import Test.HUnit.Terminal+> import Test.HUnit++> main :: IO Counts+> main = runTestTT tests++> try :: String -> String -> String -> Test+> try lab inp exp' = lab ~: terminalAppearance inp ~?= exp'++> tests :: Test+> tests = test [+>   try "empty" "" "",+>   try "end in \\n" "abc\ndef\n" "abc\ndef\n",+>   try "not end in \\n" "abc\ndef" "abc\ndef",+>   try "return 1" "abc\ndefgh\rxyz" "abc\nxyzgh",+>   try "return 2" "\nabcdefgh\rijklm\rxy\n" "\nxyklmfgh\n",+>   try "return 3" "\r\rabc\r\rdef\r\r\r\nghi\r\r\n" "def\nghi\n",+>   try "back 1" "abc\bdef\b\bgh\b" "abdgh",+>   try "back 2" "abc\b\b\bdef\b\bxy\b\b\n" "dxy\n"+>   -- \b at beginning of line+>   -- nonprinting char+>  ]
+ prologue.txt view
@@ -0,0 +1,2 @@+HUnit is a unit testing framework for Haskell, inspired by the JUnit+tool for Java, see: <http://www.junit.org>.