HUnit 1.2.0.2 → 1.2.0.3
raw patch · 12 files changed
+27/−1267 lines, 12 filesdep ~basenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- HUnit.cabal +12/−3
- README +0/−12
- Test/HUnit/Lang.lhs +15/−2
- doc/Guide.html +0/−711
- examples/Example.hs +0/−37
- examples/Makefile +0/−27
- examples/test/HUnitTest98.lhs +0/−9
- examples/test/HUnitTestBase.lhs +0/−373
- examples/test/HUnitTestExtended.lhs +0/−38
- examples/test/Makefile +0/−29
- examples/test/TerminalTest.lhs +0/−24
- prologue.txt +0/−2
HUnit.cabal view
@@ -1,5 +1,5 @@ name: HUnit-version: 1.2.0.2+version: 1.2.0.3 license: BSD3 license-file: LICENSE author: Dean Herington@@ -13,8 +13,17 @@ JUnit tool for Java, see: <http://www.junit.org>. build-type: Simple -Library- build-depends: base >=4 && <5+flag base4++library+ build-depends: base <5+ if flag(base4)+ build-depends: base >=4+ cpp-options: -DBASE4+ else+ build-depends: base <4+ if impl(ghc >= 6.10)+ build-depends: base >=4 exposed-modules: Test.HUnit.Base, Test.HUnit.Lang,
− 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.
Test/HUnit/Lang.lhs view
@@ -54,14 +54,14 @@ > data HUnitFailure = HUnitFailure String > deriving Show >-> instance Exception HUnitFailure-> > hunitFailureTc :: TyCon > hunitFailureTc = mkTyCon "HUnitFailure" > {-# NOINLINE hunitFailureTc #-} > > instance Typeable HUnitFailure where > typeOf _ = mkTyConApp hunitFailureTc []+#ifdef BASE4+> instance Exception HUnitFailure > assertFailure msg = E.throw (HUnitFailure msg) @@ -71,6 +71,19 @@ > `E.catches` > [E.Handler (\(HUnitFailure msg) -> return $ Just (True, msg)), > E.Handler (\e -> return $ Just (False, show (e :: E.SomeException)))]+#else+> assertFailure msg = 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:"
− doc/Guide.html
@@ -1,711 +0,0 @@-<!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) <- partA 3- assertEqual "for the first result of partA," 5 x- b <- 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) <- 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 (return ()))</tt> is a test case that never-fails, and-<tt>(TestCase (assertEqual "for x," 3 x))</tt>-is a test case that checks that the value of <tt>x</tt> is 3. -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 ())</tt>,-<tt>(assert True)</tt>, and <tt>(assert "")</tt> (as well as-<tt>IO</tt> forms of these values, such as <tt>(return ())</tt>)-are all assertions that never fail, while <tt>(assert False)</tt>-and <tt>(assert "some failure 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 - (errors + 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 in: <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 in: <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: <i>cases</i> Tried: <i>tried</i> Errors: <i>errors</i> Failures: <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 -> 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. -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&type=1"- width="88" height="31" border="0" alt="SourceForge.net Logo">-</a>-</body>-</html>
− examples/Example.hs
@@ -1,37 +0,0 @@--- 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
@@ -1,27 +0,0 @@-# -------------------------------------------------------------------------------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
@@ -1,9 +0,0 @@-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
@@ -1,373 +0,0 @@-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
@@ -1,38 +0,0 @@-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
@@ -1,29 +0,0 @@-# -------------------------------------------------------------------------------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
@@ -1,24 +0,0 @@-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
@@ -1,2 +0,0 @@-HUnit is a unit testing framework for Haskell, inspired by the JUnit-tool for Java, see: <http://www.junit.org>.