packages feed

HTF 0.10.0.7 → 0.11.0.0

raw patch · 29 files changed

+1123/−524 lines, 29 filesdep +arraydep +xmlgendep ~Diffdep ~basedep ~containersPVP ok

version bump matches the API change (PVP)

Dependencies added: array, xmlgen

Dependency ranges changed: Diff, base, containers, haskell-src-exts, mtl, random

API changes (from Hackage documentation)

+ Test.Framework.CmdlineOptions: opts_machineOutputXml :: CmdlineOptions -> Maybe FilePath
+ Test.Framework.CmdlineOptions: opts_shuffle :: CmdlineOptions -> Bool
+ Test.Framework.CmdlineOptions: opts_threads :: CmdlineOptions -> Maybe Int
+ Test.Framework.HUnitWrapper: assertThrowsMVerbose_ :: (MonadBaseControl IO m, MonadIO m, Exception e) => Location -> String -> m a -> (e -> Bool) -> m ()
+ Test.Framework.HUnitWrapper: assertThrowsM_ :: (MonadBaseControl IO m, MonadIO m, Exception e) => Location -> m a -> (e -> Bool) -> m ()
+ Test.Framework.HUnitWrapper: assertThrowsSomeMVerbose_ :: (MonadBaseControl IO m, MonadIO m) => Location -> String -> m a -> m ()
+ Test.Framework.HUnitWrapper: assertThrowsSomeM_ :: (MonadBaseControl IO m, MonadIO m) => Location -> m a -> m ()
+ Test.Framework.TestReporter: JsonOutput :: IsJsonOutput
+ Test.Framework.TestReporter: NoJsonOutput :: IsJsonOutput
+ Test.Framework.TestReporter: NoXmlOutput :: IsXmlOutput
+ Test.Framework.TestReporter: NonParallel :: IsParallel
+ Test.Framework.TestReporter: Parallel :: IsParallel
+ Test.Framework.TestReporter: XmlOutput :: IsXmlOutput
+ Test.Framework.TestReporter: data IsJsonOutput
+ Test.Framework.TestReporter: data IsParallel
+ Test.Framework.TestReporter: data IsXmlOutput
+ Test.Framework.TestReporter: isParallelFromBool :: Bool -> IsParallel
+ Test.Framework.TestTypes: TestOptions :: Bool -> TestOptions
+ Test.Framework.TestTypes: WithTestOptions :: TestOptions -> a -> WithTestOptions a
+ Test.Framework.TestTypes: assertion :: AssertionWithTestOptions a => a -> Assertion
+ Test.Framework.TestTypes: attachCallStack :: ColorString -> CallStack -> ColorString
+ Test.Framework.TestTypes: class AssertionWithTestOptions a
+ Test.Framework.TestTypes: data TestOptions
+ Test.Framework.TestTypes: data WithTestOptions a
+ Test.Framework.TestTypes: defaultTestOptions :: TestOptions
+ Test.Framework.TestTypes: emptyTestReporter :: String -> TestReporter
+ Test.Framework.TestTypes: finalName :: TestPath -> String
+ Test.Framework.TestTypes: instance AssertionWithTestOptions (IO a)
+ Test.Framework.TestTypes: instance AssertionWithTestOptions (WithTestOptions (IO a))
+ Test.Framework.TestTypes: instance Eq TestOptions
+ Test.Framework.TestTypes: instance Eq a => Eq (WithTestOptions a)
+ Test.Framework.TestTypes: instance Read TestOptions
+ Test.Framework.TestTypes: instance Read a => Read (WithTestOptions a)
+ Test.Framework.TestTypes: instance Show TestOptions
+ Test.Framework.TestTypes: instance Show a => Show (WithTestOptions a)
+ Test.Framework.TestTypes: prefixName :: TestPath -> String
+ Test.Framework.TestTypes: tc_outputXml :: TestConfig -> Maybe FilePath
+ Test.Framework.TestTypes: tc_shuffle :: TestConfig -> Bool
+ Test.Framework.TestTypes: tc_threads :: TestConfig -> Maybe Int
+ Test.Framework.TestTypes: tc_useColors :: TestConfig -> Bool
+ Test.Framework.TestTypes: testOptions :: AssertionWithTestOptions a => a -> TestOptions
+ Test.Framework.TestTypes: to_parallel :: TestOptions -> Bool
+ Test.Framework.TestTypes: type CallStack = [(Maybe String, Location)]
+ Test.Framework.TestTypes: withOptions :: (TestOptions -> TestOptions) -> a -> WithTestOptions a
+ Test.Framework.TestTypes: wto_options :: WithTestOptions a -> TestOptions
+ Test.Framework.TestTypes: wto_payload :: WithTestOptions a -> a
+ Test.Framework.ThreadPool: ThreadPool :: ([ThreadPoolEntry m a b] -> m ()) -> ThreadPool m a b
+ Test.Framework.ThreadPool: data ThreadPool m a b
+ Test.Framework.ThreadPool: instance Show (WorkItem m b)
+ Test.Framework.ThreadPool: instance Show (WorkResult m b)
+ Test.Framework.ThreadPool: parallelThreadPool :: MonadIO m => Int -> m (ThreadPool m a b)
+ Test.Framework.ThreadPool: sequentialThreadPool :: MonadIO m => ThreadPool m a b
+ Test.Framework.ThreadPool: threadPoolTest :: (Int, Int) -> Int -> IO [()]
+ Test.Framework.ThreadPool: tp_run :: ThreadPool m a b -> [ThreadPoolEntry m a b] -> m ()
+ Test.Framework.ThreadPool: type ThreadPoolEntry m a b = (m a, a -> IO b, Either SomeException b -> m ())
+ Test.Framework.XmlOutput: mkGlobalResultsXml :: Milliseconds -> [FlatTestResult] -> [FlatTestResult] -> [FlatTestResult] -> [FlatTestResult] -> ByteString
- Test.Framework: makeUnitTest :: TestID -> Location -> IO a -> Test
+ Test.Framework: makeUnitTest :: AssertionWithTestOptions a => TestID -> Location -> a -> Test
- Test.Framework.CmdlineOptions: CmdlineOptions :: Bool -> TestFilter -> Bool -> [String] -> Bool -> Maybe Bool -> Maybe FilePath -> Bool -> Bool -> CmdlineOptions
+ Test.Framework.CmdlineOptions: CmdlineOptions :: Bool -> TestFilter -> Bool -> [String] -> Maybe Int -> Bool -> Bool -> Maybe FilePath -> Maybe Bool -> Maybe FilePath -> Bool -> Bool -> CmdlineOptions
- Test.Framework.TestManager: makeUnitTest :: TestID -> Location -> IO a -> Test
+ Test.Framework.TestManager: makeUnitTest :: AssertionWithTestOptions a => TestID -> Location -> a -> Test
- Test.Framework.TestReporter: defaultTestReporters :: Bool -> Bool -> [TestReporter]
+ Test.Framework.TestReporter: defaultTestReporters :: IsParallel -> IsJsonOutput -> IsXmlOutput -> [TestReporter]
- Test.Framework.TestTypes: BaseTest :: TestSort -> TestID -> (Maybe Location) -> Assertion -> Test
+ Test.Framework.TestTypes: BaseTest :: TestSort -> TestID -> (Maybe Location) -> TestOptions -> Assertion -> Test
- Test.Framework.TestTypes: RunResult :: TestResult -> Maybe Location -> [(Maybe String, Location)] -> String -> Milliseconds -> RunResult
+ Test.Framework.TestTypes: RunResult :: TestResult -> Maybe Location -> CallStack -> ColorString -> Milliseconds -> RunResult
- Test.Framework.TestTypes: TestConfig :: Bool -> TestOutput -> TestFilter -> [TestReporter] -> TestConfig
+ Test.Framework.TestTypes: TestConfig :: Bool -> Maybe Int -> Bool -> TestOutput -> Maybe FilePath -> TestFilter -> [TestReporter] -> Bool -> TestConfig
- Test.Framework.TestTypes: rr_callers :: RunResult -> [(Maybe String, Location)]
+ Test.Framework.TestTypes: rr_callers :: RunResult -> CallStack
- Test.Framework.TestTypes: rr_message :: RunResult -> String
+ Test.Framework.TestTypes: rr_message :: RunResult -> ColorString
- Test.Framework.TestTypes: type FlatTest = GenFlatTest Assertion
+ Test.Framework.TestTypes: type FlatTest = GenFlatTest (WithTestOptions Assertion)

Files

ChangeLog view
@@ -1,3 +1,14 @@+* 0.11.0.0 (2013-08-31)+  - fallback to pure Haskell diff if no diff binary found (thanks JP Moresmau)+  - use cabal test feature (thanks Tom Brow)+  - parallel execution of tests+  - bugfix: enable DoAndIfThenElse+  - bugfix: use default fixities for all operators+  - two new assertions for dealing with exceptions: assertThrowsM and assertThrowsSomeM++* 0.10.0.7 (2012-12-07)+  - fixed problem building against Diff 0.1.3+ * 0.10.0.6 (2012-12-06)   - support for Diff 0.2. 
HTF.cabal view
@@ -1,5 +1,5 @@ Name:             HTF-Version:          0.10.0.7+Version:          0.11.0.0 License:          LGPL License-File:     LICENSE Copyright:        (c) 2005-2012 Stefan Wehr@@ -81,10 +81,8 @@       the imported tests. See "Test.Framework.Tutorial" for details.  Tested-With:-                  GHC==7.0.4,-                  GHC==7.2.1,                   GHC==7.4.2,-                  GHC==7.6.1+                  GHC==7.6.2 Build-Type:       Simple Cabal-Version:    >= 1.10 Extra-Source-Files:@@ -138,9 +136,11 @@ Executable htfpp   Main-Is:          HTFPP.hs   Build-Depends:    cpphs >= 1.12,-                    haskell-src-exts >= 1.11,+                    haskell-src-exts >= 1.14,                     old-time >= 1.0,                     directory >= 1.0,+                    array,+                    random >= 1.0,                     base == 4.*   Other-Modules:     Test.Framework.Preprocessor@@ -148,24 +148,27 @@   Default-language:  Haskell2010  Library-  Build-Depends:    HUnit >= 1.2.5,+  Build-Depends:+                    HUnit >= 1.2.5,                     QuickCheck >= 2.3,                     base == 4.*,                     random >= 1.0,-                    containers >= 0.3,+                    containers >= 0.5,                     process >= 1.0,                     directory >= 1.0,                     mtl >= 1.1 && < 2.2,                     monad-control >= 0.3,                     lifted-base >= 0.1,                     pretty >= 1.0,-                    Diff >= 0.1.3,+                    Diff >= 0.3,                     bytestring >= 0.9,                     regex-compat >= 0.92,                     haskell-src-exts >= 1.11,                     aeson >= 0.6,                     text >= 0.11,-                    old-time >= 1.0+                    old-time >= 1.0,+                    array,+                    xmlgen >= 0.6   if !os(windows)     Build-Depends:   unix >= 2.4   Exposed-Modules:@@ -181,6 +184,8 @@     Test.Framework.Tutorial     Test.Framework.Pretty     Test.Framework.JsonOutput+    Test.Framework.XmlOutput+    Test.Framework.ThreadPool   Other-Modules:     Test.Framework.TestManagerInternal     Test.Framework.Utils@@ -188,7 +193,7 @@     Test.Framework.Diff     Test.Framework.Process   Default-language:  Haskell2010-  -- Ghc-Options: -fwarn-unused-imports -fwarn-unused-binds+  Ghc-Options: -W -fwarn-unused-imports -fwarn-unused-binds -fwarn-unused-matches -fwarn-unused-do-bind -fwarn-wrong-do-bind  Test-Suite TestHTF   Main-is:           TestHTF.hs@@ -208,3 +213,20 @@   Default-language:  Haskell2010   Other-Modules:     Foo.A, Foo.B, TestHTFHunitBackwardsCompatible++Test-Suite TestThreadPools+  Main-is:           ThreadPoolTest.hs+  Type:              exitcode-stdio-1.0+  Hs-Source-Dirs:    tests+  Build-depends:     base == 4.*,+                     random,+                     mtl,+                     HTF+  Default-language:  Haskell2010++Test-Suite Tutorial+  Type:              exitcode-stdio-1.0+  Main-is:           Tutorial.hs+  Hs-Source-Dirs:    tests+  Build-depends:     base >= 4, HTF+  Default-language:  Haskell2010
TODO.org view
@@ -1,11 +1,11 @@-* Relase 0.11-** Fallback parser-*** Use the lexer of haskell-src-exts-** Parallel test execution-*** Options --threads=[N] or -j [N]-*** Some test cannot be run in parallel-+* Relase 0.12+** New parser+*** Add support for MultiWayIf and LambdaCase to haskell-src-exts+*** Allow default Fixities for haskell-src-exts ** Support for timeouts (--timeout=SECS) ** External interface for integrating other test frameworks *** Support for Smallcheck and/or Lazy Smallcheck *** Configuration file for htfpp+**** Expansion templates (extensible!)+** Support for benchmarks+** Collect testmodules with a new preprocessor (new preprocessor runs on the main module)
Test/Framework/BlackBoxTest.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE CPP #-} ----- Copyright (c) 2005,2009,2012   Stefan Wehr - http://www.stefanwehr.de+-- Copyright (c) 2005,2009,2012,2013   Stefan Wehr - http://www.stefanwehr.de -- -- This library is free software; you can redistribute it and/or -- modify it under the terms of the GNU Lesser General Public@@ -45,13 +45,9 @@ import Prelude hiding ( catch ) #endif -import System.IO import System.Exit-import Control.Exception import System.Directory-import Data.List ( mapAccumL ) import qualified Data.Map as Map-import Control.Monad  import Test.Framework.Process import Test.Framework.TestManager@@ -74,8 +70,8 @@                        { bbtCfg_shouldFail  :: Bool                        , bbtCfg_cmd         :: String                        , bbtCfg_stdinFile   :: Maybe FilePath-                       , bbtCfg_stdoutFile  :: Maybe FilePath-                       , bbtCfg_stderrFile  :: Maybe FilePath+                       , bbtCfg_stdoutFile  :: Maybe FilePath -- ^ path to file holding expected output on stdout+                       , bbtCfg_stderrFile  :: Maybe FilePath -- ^ path to file holding expected output on stderr                        , bbtCfg_verbose     :: Bool                        -- functions for comparing output on stdout and stderr.                        , bbtCfg_stdoutCmp   :: Diff@@ -145,7 +141,7 @@ defaultBBTArgs = BBTArgs { bbtArgs_stdinSuffix    = \".in\"                          , bbtArgs_stdoutSuffix   = \".out\"                          , bbtArgs_stderrSuffix   = \".err\"-                         , bbtArgs_dynArgsName    = "BBTArgs"+                         , bbtArgs_dynArgsName    = \"BBTArgs\"                          , bbtArgs_stdoutDiff     = defaultDiff                          , bbtArgs_stderrDiff     = defaultDiff                          , bbtArgs_verbose        = False }@@ -198,17 +194,21 @@  Suppose that one of the @.num@ files is @bbt-dir\/should-pass\/x.num@. Running the corresponding 'Test' invokes-@dist\/build\/sample\/sample@ (the program under test) with @bbt-dir\/should-pass\/x.num@-as input file. If @bbt-dir\/should-pass\/x.num@ existed, its content+@dist\/build\/sample\/sample@ (the program under test)+with @bbt-dir\/should-pass\/x.num@ as the last commandline argument.+The other commandline arguments are taken from the flags specification given in the+file whose name is stored in the 'bbtArgs_dynArgsName' field of the 'BBTArgs' record+(see below).++If @bbt-dir\/should-pass\/x.in@ existed, its content would be used as stdin. The tests succeeds if the exit code of the program is zero and the output on stdout and stderr matches the contents of @bbt-dir\/should-pass\/x.out@ and @bbt-dir\/should-pass\/x.err@, respectively. -The directory @bbt-dir\/should-fail@ contains the file @BBTArgs@. If this file-exists, then its content specifies various-arguments for the test run. The file defines the arguments separated by newlines.-Supported arguments:+The 'bbtArgs_dynArgsName' field of the 'BBTArgs' record specifies a filename+that contains some more configuration flags for the tests. The following+flags (separated by newlines) are supported:   [@Skip@] Skips all tests in the same directory as the argument file. 
Test/Framework/CmdlineOptions.hs view
@@ -31,7 +31,6 @@  import Test.Framework.TestReporter import Test.Framework.TestTypes-import Test.Framework.Colors import Test.Framework.Utils  import Data.Char (toLower)@@ -48,7 +47,7 @@ import System.Posix.Env (getEnv) #endif -#ifdef COMPILER_GHC+#ifdef __GLASGOW_HASKELL__ import GHC.Conc ( numCapabilities ) #endif @@ -62,8 +61,10 @@     , opts_filter :: TestFilter         -- ^ Run only tests matching this filter.     , opts_help :: Bool                 -- ^ If 'True', display a help message and exit.     , opts_negated :: [String]          -- ^ Regular expressions matching test names which should /not/ run.---    , opts_threads :: Maybe Int         -- ^ Use @Just i@ for parallel execution with @i@ threads, @Nothing@ for sequential execution (currently unused).+    , opts_threads :: Maybe Int         -- ^ Use @Just i@ for parallel execution with @i@ threads, @Nothing@ for sequential execution.+    , opts_shuffle :: Bool              -- ^ If 'True' (the default), shuffle tests when running them in parallel.     , opts_machineOutput :: Bool        -- ^ Format output for machines (JSON format) or humans. See 'Test.Framework.JsonOutput' for a definition of the JSON format.+    , opts_machineOutputXml :: Maybe FilePath -- ^ Output file for junit-style XML output. See 'Test.Framework.XmlOutput' for a definition of the XML format.     , opts_useColors :: Maybe Bool      -- ^ Use @Just b@ to enable/disable use of colors, @Nothing@ infers the use of colors.     , opts_outputFile :: Maybe FilePath -- ^ The output file, defaults to stdout     , opts_listTests :: Bool            -- ^ If 'True', lists all tests available and exits.@@ -79,8 +80,10 @@     , opts_filter = const True     , opts_help = False     , opts_negated = []---    , opts_threads = Nothing+    , opts_threads = Nothing+    , opts_shuffle = True     , opts_machineOutput = False+    , opts_machineOutputXml = Nothing     , opts_useColors = Nothing     , opts_outputFile = Nothing     , opts_listTests = False@@ -88,7 +91,7 @@     }  processorCount :: Int-#ifdef COMPILER_GHC+#ifdef __GLASGOW_HASKELL__ processorCount = numCapabilities #else processorCount = 1@@ -100,12 +103,15 @@     , Option ['n']     ["not"]     (ReqArg (\s o -> o { opts_negated = s : (opts_negated o) })                                            "PATTERN") "tests to exclude"     , Option ['l']     ["list"]   (NoArg (\o -> o { opts_listTests = True })) "list all matching tests"---     , Option ['j']     ["threads"]   (OptArg (\ms o -> o { opts_threads = Just (parseThreads ms) }) "N")---                                      ("run N tests in parallel, default N=" ++ show processorCount)+    , Option ['j']     ["threads"]   (OptArg (\ms o -> o { opts_threads = Just (parseThreads ms) }) "N")+                                      ("run N tests in parallel, default N=" ++ show processorCount)+    , Option []        ["deterministic"] (NoArg (\o -> o { opts_shuffle = False })) "do not shuffle tests when executing them in parallel."     , Option ['o']     ["output-file"] (ReqArg (\s o -> o { opts_outputFile = Just s })                                                "FILE") "name of output file"     , Option []        ["json"] (NoArg (\o -> o { opts_machineOutput = True }))-                               "output results in machine-readable JSON format"+                               "output results in machine-readable JSON format (incremental)"+    , Option []        ["xml"] (ReqArg (\s o -> o { opts_machineOutputXml = Just s }) "FILE")+                               "output results in junit-style XML format"     , Option []        ["split"] (NoArg (\o -> o { opts_split = True }))                                "splits results in separate files to avoid file locking (requires -o/--output-file)"     , Option []        ["colors"]  (ReqArg (\s o -> o { opts_useColors = Just (parseBool s) })@@ -134,11 +140,15 @@ >   -q          --quiet             only display errors >   -n PATTERN  --not=PATTERN       tests to exclude >   -l          --list              list all matching tests+>   -j[N]       --threads[=N]       run N tests in parallel, default N=4+>               --deterministic     do not shuffle tests when executing them in parallel. >   -o FILE     --output-file=FILE  name of output file->               --json              output results in machine-readable JSON format+>               --json              output results in machine-readable JSON format (incremental)+>               --xml=FILE          output results in junit-style XML format >               --split             splits results in separate files to avoid file locking (requires -o/--output-file) >               --colors=BOOL       use colors or not >   -h          --help              display this message+ -}  parseTestArgs :: [String] -> Either String CmdlineOptions@@ -156,7 +166,7 @@                         else null pos || any (\s -> s `matches` flat) pos               opts = (foldr ($) defaultCmdlineOptions optTrans) { opts_filter = pred }           in case (opts_outputFile opts, opts_split opts) of-               (Nothing, True) -> Left ("Option --split requires -o or --output-file\n\n" ++ +               (Nothing, True) -> Left ("Option --split requires -o or --output-file\n\n" ++                                         usageInfo usageHeader optionDescriptions)                _ -> Right opts       (_,_,errs) ->@@ -187,14 +197,18 @@              _ -> do (outputHandle, closeOutput, mOutputFd) <- openOutputFile                      colors <- checkColors mOutputFd                      return (TestOutputHandle outputHandle closeOutput, colors)-       setUseColors colors---       let threads = opts_threads opts-       let reporters = defaultTestReporters False {-(isJust threads)-} (opts_machineOutput opts)+       let threads = opts_threads opts+           reporters = defaultTestReporters (isParallelFromBool $ isJust threads)+                                            (if opts_machineOutput opts then JsonOutput else NoJsonOutput)+                                            (if isJust (opts_machineOutputXml opts) then XmlOutput else NoXmlOutput)        return $ TestConfig { tc_quiet = opts_quiet opts---                           , tc_threads = threads+                           , tc_threads = threads+                           , tc_shuffle = opts_shuffle opts                            , tc_output = output+                           , tc_outputXml = opts_machineOutputXml opts                            , tc_reporters = reporters-                           , tc_filter = opts_filter opts }+                           , tc_filter = opts_filter opts+                           , tc_useColors = colors }     where #ifdef mingw32_HOST_OS       openOutputFile =
Test/Framework/Colors.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BangPatterns #-} -- -- Copyright (c) 2011, 2012   Stefan Wehr - http://www.stefanwehr.de --@@ -18,18 +20,19 @@  module Test.Framework.Colors ( -    Color(..), PrimColor(..), startColor, withColor, colorize-  , reset+    Color(..), PrimColor(..), ColorString(..), PrimColorString(..)   , firstDiffColor, secondDiffColor, skipDiffColor, diffColor   , warningColor, testStartColor, testOkColor, pendingColor+  , emptyColorString, (+++), unlinesColorString, colorStringFind, ensureNewlineColorString+  , colorize, colorizeText, colorize', colorizeText'+  , noColor, noColorText, noColor', noColorText'+  , renderColorString -  , setUseColors, useColors ) where -import System.IO.Unsafe (unsafePerformIO)-import Data.IORef---- REVERSE            = "\033[2m"+import qualified Data.Text as T+import Data.String+import Control.Monad  firstDiffColor = Color Magenta False secondDiffColor = Color Blue False@@ -41,14 +44,15 @@ pendingColor = Color Cyan True  data Color = Color PrimColor Bool+           deriving (Eq, Show, Read)  data PrimColor = Black | Blue | Green | Cyan | Red | Magenta                | Brown | Gray | DarkGray | LightBlue                | LightGreen | LightCyan | LightRed | LightMagenta                | Yellow | White | NoColor-             deriving (Eq, Show)+             deriving (Eq, Show, Read) -startColor :: Color -> String+startColor :: Color -> T.Text startColor (Color c isBold) =     (case c of        Black -> "\ESC[0;30m"@@ -67,28 +71,113 @@        LightMagenta -> "\ESC[1;35m"        Yellow -> "\ESC[1;33m"        White -> "\ESC[1;37m"-       NoColor -> "") +++       NoColor -> "") `T.append`     (if isBold then "\ESC[1m" else "") -reset :: String+reset :: T.Text reset = "\ESC[0;0m" -withColor :: Color -> String -> String-withColor c s = startColor c ++ s ++ reset+data PrimColorString = PrimColorString Color T.Text (Maybe T.Text) {- no-color fallback -}+                    deriving (Eq, Show, Read) -colorize :: Color -> String -> IO String-colorize c s =-    do b <- useColors-       return $ if b then withColor c s else s+newtype ColorString = ColorString { unColorString :: [PrimColorString] }+                    deriving (Eq, Show, Read) --- We store the info about the use of colors in a global variable instead of the TestConfigs because--- hunit tests must known whether to colorize diffs.-_useColors :: IORef Bool-_useColors = unsafePerformIO (newIORef False)-{-# NOINLINE _useColors #-}+instance IsString ColorString where+    fromString = noColor -useColors :: IO Bool-useColors = readIORef _useColors+emptyColorString :: ColorString+emptyColorString = noColor "" -setUseColors :: Bool -> IO ()-setUseColors b = atomicModifyIORef _useColors (\_ -> (b, ()))+unlinesColorString :: [ColorString] -> ColorString+unlinesColorString l =+    concatColorString $+    map (\x -> appendPrimColorString x (PrimColorString (Color NoColor False) (T.pack "\n") Nothing)) l+    where+      appendPrimColorString (ColorString l) x =+          ColorString (l ++ [x])++concatColorString :: [ColorString] -> ColorString+concatColorString l =+    ColorString $ concatMap (\(ColorString l) -> l) l++colorStringFind :: (Char -> Bool) -> ColorString -> Bool -> Maybe Char+colorStringFind pred (ColorString l) c =+    let f = if c then pcolorStringFindColor else pcolorStringFindNoColor+    in msum (map f l)+    where+      pcolorStringFindColor (PrimColorString _ t _) = tfind t+      pcolorStringFindNoColor (PrimColorString _ t Nothing) = tfind t+      pcolorStringFindNoColor (PrimColorString _ _ (Just t)) = tfind t+      tfind t = T.find pred t++ensureNewlineColorString :: ColorString -> ColorString+ensureNewlineColorString cs@(ColorString l) =+    let (colors, noColors) = unzip $ map colorsAndNoColors (reverse l)+        nlColor = needsNl colors+        nlNoColor = needsNl noColors+    in if not nlColor && not nlNoColor+       then cs+       else ColorString (l +++                         [PrimColorString (Color NoColor False) (mkNl nlColor)+                                              (Just (mkNl nlNoColor))])+    where+      mkNl True = "\n"+      mkNl False = ""+      colorsAndNoColors (PrimColorString _ t1 (Just t2)) = (t1, t2)+      colorsAndNoColors (PrimColorString _ t1 Nothing) = (t1, t1)+      needsNl [] = False+      needsNl (t:ts) =+          let t' = T.dropWhileEnd (\c -> c == ' ') t+          in if T.null t'+             then needsNl ts+             else T.last t' /= '\n'++colorize :: Color -> String -> ColorString+colorize c s = colorizeText c (T.pack s)++colorizeText :: Color -> T.Text -> ColorString+colorizeText !c !t = ColorString [PrimColorString c t Nothing]++colorize' :: Color -> String -> String -> ColorString+colorize' c s x = colorizeText' c (T.pack s) (T.pack x)++colorizeText' :: Color -> T.Text -> T.Text -> ColorString+colorizeText' !c !t !x = ColorString [PrimColorString c t (Just x)]++noColor :: String -> ColorString+noColor = colorize (Color NoColor False)++noColorText :: T.Text -> ColorString+noColorText = colorizeText (Color NoColor False)++noColor' :: String -> String -> ColorString+noColor' s1 s2 = colorize' (Color NoColor False) s1 s2++noColorText' :: T.Text -> T.Text -> ColorString+noColorText' t1 t2 = colorizeText' (Color NoColor False) t1 t2++infixr 5  +++++(+++) :: ColorString -> ColorString -> ColorString+cs1 +++ cs2 =+    case (cs1, cs2) of+      (ColorString [PrimColorString c1 t1 m1], ColorString (PrimColorString c2 t2 m2 : rest))+          | c1 == c2 ->+              let m3 = case (m1, m2) of+                         (Nothing, Nothing) -> Nothing+                         (Just x1, Just x2) -> Just (x1 `T.append` x2)+                         (Just x1, Nothing) -> Just (x1 `T.append` t2)+                         (Nothing, Just x2) -> Just (t1 `T.append` x2)+              in ColorString (PrimColorString c1 (t1 `T.append` t2) m3 : rest)+      (ColorString ps1, ColorString ps2) -> ColorString (ps1 ++ ps2)++renderColorString :: ColorString -> Bool -> T.Text+renderColorString (ColorString l) useColor =+    T.concat (map render l)+    where+      render = if useColor then renderColors else renderNoColors+      renderNoColors (PrimColorString _ _ (Just t)) = t+      renderNoColors (PrimColorString _ t Nothing) = t+      renderColors (PrimColorString c t _) =+          T.concat [startColor c, t, reset]
Test/Framework/Diff.hs view
@@ -1,4 +1,4 @@-{-# LANGUAGE CPP #-}+{-# LANGUAGE CPP, ScopedTypeVariables #-} -- -- Copyright (c) 2011, 2012   Stefan Wehr - http://www.stefanwehr.de --@@ -19,9 +19,7 @@  module Test.Framework.Diff ( -    DiffConfig(..), noColorsDiffConfig, coloredDiffConfig-  , defaultTerminalDiffConfig, defaultNoColorsDiffConfig-  , diffWithSensibleConfig, diff+    DiffConfig(..), diffWithSensibleConfig, diff, main  ) where @@ -33,6 +31,7 @@ import qualified Data.List as List import Data.Char import qualified Data.Algorithm.Diff as D+import Data.Algorithm.DiffOutput import Test.Framework.Colors  -- for testing@@ -40,6 +39,8 @@ import System.Directory import System.Exit import System.Process+import System.Environment (getArgs)+import qualified Data.Text as T  data Pos = First | Middle | Last | FirstLast          deriving (Eq)@@ -54,89 +55,53 @@ isFirst FirstLast = True isFirst _ = False -isMiddle :: Pos -> Bool-isMiddle Middle = True-isMiddle _ = False- data DiffConfig = DiffConfig {     -- for single line diffs-      dc_fromFirstPrefix :: String-    , dc_fromFirstSuffix :: String-    , dc_fromSecondPrefix :: String-    , dc_fromSecondSuffix :: String-    , dc_fromBothPrefix :: String-    , dc_fromBothSuffix :: String-    , dc_sep :: String-    , dc_skipPrefix :: String-    , dc_skipSuffix :: String+      dc_fromFirst :: String -> ColorString+    , dc_fromSecond :: String -> ColorString+    , dc_fromBoth :: String -> ColorString+    , dc_sep :: ColorString+    , dc_skip :: String -> ColorString     -- for multi-line diffs-    , dc_lineFromFirstPrefix :: String-    , dc_lineFromSecondPrefix :: String-    , dc_lineFromFirstSuffix :: String-    , dc_lineFromSecondSuffix :: String-    }--noColorsDiffConfig :: Char -> Char -> DiffConfig-noColorsDiffConfig f s = DiffConfig {-      dc_fromFirstPrefix = f : " "-    , dc_fromFirstSuffix = ""-    , dc_fromSecondPrefix = s : " "-    , dc_fromSecondSuffix = ""-    , dc_fromBothPrefix = "C "-    , dc_fromBothSuffix = ""-    , dc_skipPrefix = "<..."-    , dc_skipSuffix = "...>"-    , dc_sep = "\n"-    , dc_lineFromFirstPrefix = ""-    , dc_lineFromSecondPrefix = ""-    , dc_lineFromFirstSuffix = ""-    , dc_lineFromSecondSuffix = ""+    , dc_lineFromFirst :: String -> ColorString+    , dc_lineFromSecond :: String -> ColorString     } -coloredDiffConfig :: Color -> Color -> Color -> DiffConfig-coloredDiffConfig c1 c2 c3 = DiffConfig {-      dc_fromFirstPrefix = startColor c1-    , dc_fromFirstSuffix = reset-    , dc_fromSecondPrefix = startColor c2-    , dc_fromSecondSuffix = reset-    , dc_fromBothPrefix = ""-    , dc_fromBothSuffix = ""-    , dc_skipPrefix = startColor c3 ++ "..."-    , dc_skipSuffix = "..." ++ reset-    , dc_sep = ""-    , dc_lineFromFirstPrefix = startColor c1-    , dc_lineFromSecondPrefix = startColor c2-    , dc_lineFromFirstSuffix = reset-    , dc_lineFromSecondSuffix = reset+mkDefaultDiffConfig :: Color -> Color -> Color -> Char -> Char -> DiffConfig+mkDefaultDiffConfig c1 c2 c3 f s = DiffConfig {+      dc_fromFirst = \x -> colorize' c1 x (f : " " ++ x)+    , dc_fromSecond = \x -> colorize' c2 x (s : " " ++ x)+    , dc_fromBoth = \x -> noColor' x ("C " ++ x)+    , dc_skip = \x -> colorize' c3 ("..." ++ x ++ "...") ("<..." ++ x ++ "...>")+    , dc_sep = noColor' "" "\n"+    , dc_lineFromFirst = colorize c1+    , dc_lineFromSecond = colorize c2     } -defaultTerminalDiffConfig :: DiffConfig-defaultTerminalDiffConfig = coloredDiffConfig firstDiffColor secondDiffColor skipDiffColor--defaultNoColorsDiffConfig :: DiffConfig-defaultNoColorsDiffConfig = noColorsDiffConfig 'F' 'S'+defaultDiffConfig :: DiffConfig+defaultDiffConfig = mkDefaultDiffConfig firstDiffColor secondDiffColor skipDiffColor 'F' 'S'  contextSize :: Int contextSize = 10 -singleLineDiff :: DiffConfig -> String -> String -> String+singleLineDiff :: DiffConfig -> String -> String -> ColorString singleLineDiff dc s1 s2-    | s1 == s2 = ""+    | s1 == s2 = emptyColorString     | otherwise =         let groups = D.getGroupedDiff s1 s2         in foldr (\(group, pos) string ->-                      (showDiffGroup pos group) ++-                      (if not (isLast pos) then dc_sep dc else "") +++                      (showDiffGroup pos group) ++++                      (if not (isLast pos) then dc_sep dc else emptyColorString) +++                       string)-                 "" (addPositions groups)+                 emptyColorString (addPositions groups)     where #if MIN_VERSION_Diff(0,2,0)-      showDiffGroup _ (D.First s) = dc_fromFirstPrefix dc ++ s ++ dc_fromFirstSuffix dc-      showDiffGroup _ (D.Second s) = dc_fromSecondPrefix dc ++ s ++ dc_fromSecondSuffix dc+      showDiffGroup _ (D.First s) = dc_fromFirst dc s+      showDiffGroup _ (D.Second s) = dc_fromSecond dc s       showDiffGroup pos (D.Both inBoth _) = #else-      showDiffGroup _ (D.F, s) = dc_fromFirstPrefix dc ++ s ++ dc_fromFirstSuffix dc-      showDiffGroup _ (D.S, s) = dc_fromSecondPrefix dc ++ s ++ dc_fromSecondSuffix dc+      showDiffGroup _ (D.F, s) = dc_fromFirst dc s+      showDiffGroup _ (D.S, s) = dc_fromSecond dc s       showDiffGroup pos (D.B, inBoth) = #endif           let showStart = not $ isFirst pos@@ -150,11 +115,11 @@                             (if showEnd then "" else e)                   in (start, ign, end)               middle = let n = length ignored-                           replText = dc_skipPrefix dc ++ "skipped " ++ show n ++ " chars" ++-                                      dc_skipSuffix dc-                       in if n <= length replText then ignored else replText-          in dc_fromBothPrefix dc ++ contextStart ++ middle ++ contextEnd ++-             dc_fromBothSuffix dc+                           replText = "skipped " ++ show n ++ " chars"+                       in if n <= length replText+                          then dc_skip dc ignored+                          else dc_skip dc replText+          in dc_fromBoth dc contextStart +++ middle +++ dc_fromBoth dc contextEnd       addPositions [] = []       addPositions (x:[]) = (x, FirstLast) : []       addPositions (x:xs) = (x, First) : addPositions' xs@@ -162,7 +127,7 @@       addPositions' (x:[]) = (x, Last) : []       addPositions' (x:xs) = (x, Middle) : addPositions' xs -multiLineDiff :: DiffConfig -> String -> String -> IO String+multiLineDiff :: DiffConfig -> String -> String -> IO ColorString multiLineDiff cfg left right =     withTempFiles $ \(fpLeft, hLeft) (fpRight, hRight) ->         do write hLeft left@@ -170,14 +135,14 @@            doDiff fpLeft fpRight     where       doDiff leftFile rightFile =-          do (ecode, out, err) <- readProcessWithExitCode "diff" [leftFile, rightFile] ""-             case ecode of-               ExitSuccess -> return (format out)-               ExitFailure 1 -> return (format out)-               ExitFailure i ->-                   return ("'diff " ++ leftFile ++ " " ++ rightFile ++-                           "' failed with exit code " ++ show i ++-                           ": " ++ show err)+          (do (ecode, out, _err) <- readProcessWithExitCode "diff" [leftFile, rightFile] ""+              case ecode of+                ExitSuccess -> return (format out)+                ExitFailure 1 -> return (format out)+                ExitFailure _i -> return $ multiLineDiffHaskell left right)+             -- if we can't launch diff, use the Haskell code.+             -- We don't write the exception anywhere to not pollute test results.+            `catch` (\(_::IOException) -> return $ multiLineDiffHaskell left right)       saveRemove fp =           removeFile fp `catch` (\e -> hPutStrLn stderr (show (e::IOException)))       withTempFiles action =@@ -189,199 +154,42 @@       write h s =           do hPutStr h s              hClose h-      format out = unlines $ map formatLine (lines out)+      format out = unlinesColorString $ map formatLine (lines out)       formatLine l =           case l of             ('<' : _) -> fromFirst l             ('>' : _) -> fromSecond l             (c : _)                  | isDigit c -> case List.span (\c -> c /= 'a' && c /= 'c' && c /= 'd') l of-                                  (left, c:right) -> fromFirst left ++ [c] ++ fromSecond right-                                  (left, []) -> left-                 | otherwise -> l+                                  (left, c:right) -> fromFirst left ++++                                                     noColor [c] ++++                                                     fromSecond right+                                  (left, []) -> noColor left+                 | otherwise -> noColor l+            [] -> noColor l           where-            fromFirst s = dc_fromFirstPrefix cfg ++ s ++ dc_fromFirstSuffix cfg-            fromSecond s = dc_fromSecondPrefix cfg ++ s ++ dc_fromSecondSuffix cfg+            fromFirst s = dc_fromFirst cfg s+            fromSecond s = dc_fromSecond cfg s -diff :: DiffConfig -> String -> String -> IO String+diff :: DiffConfig -> String -> String -> IO ColorString diff cfg left right =     case (lines left, lines right) of-      ([], []) -> return ""+      ([], []) -> return emptyColorString       ([], [_]) -> return $ singleLineDiff cfg left right       ([_], []) -> return $ singleLineDiff cfg left right       ([_], [_]) -> return $ singleLineDiff cfg left right       _ -> multiLineDiff cfg left right -diffWithSensibleConfig :: String -> String -> IO String+diffWithSensibleConfig :: String -> String -> IO ColorString diffWithSensibleConfig s1 s2 =-    do b <- useColors-       let dc = if b then defaultTerminalDiffConfig else defaultNoColorsDiffConfig-       diff dc s1 s2+    diff defaultDiffConfig s1 s2  {--NOTE: This is *nearly* working. Originally, I wanted to implemented a pure Haskell-diff solution. At some point, however, I decided that it would be better to implement-a solution based on the diff tool. For now, I leave the code as it is.--type PrimDiff = [(DI, Char)]-type LineNo = Int--data Line = Line { line_number :: LineNo-                 , line_content :: String {- without trailing \n -}-                 }-            deriving (Show)--data LineRange = LineRange { lr_numbers :: (LineNo, LineNo)-                           , lr_contents :: [String] {- without trailing \n -}-                           }-            deriving (Show)--data Diff a = OnlyInLeft a LineNo-            | OnlyInRight a LineNo-            | InBoth a a-            deriving (Show)--instance Functor Diff where-    fmap f d = case d of-                 OnlyInLeft x n -> OnlyInLeft (f x) n-                 OnlyInRight x n -> OnlyInRight (f x) n-                 InBoth x y -> InBoth (f x) (f y)--multiLineDiff :: DiffConfig -> String -> String -> String-multiLineDiff cfg left right =-    let diff = getDiff left right :: PrimDiff-        diffByLine = List.unfoldr nextLine diff-        diffLines = let (_, _, l) = foldl diffLine (1, 1, []) diffByLine-                    in reverse l-        diffLineRanges = maximize diffLines-    in debug ("diff: " ++ show diff ++-              "\ndiffByLine: " ++ show diffByLine ++-              "\ndiffLines: " ++ show diffLines ++-              "\ndiffLineRanges: " ++ show diffLineRanges) $-       render $ prettyDiffs diffLineRanges-    where-      nextLine :: PrimDiff -> Maybe (PrimDiff, PrimDiff)-      nextLine [] = Nothing-      nextLine diff =-          -- FIXME: add support for \r\n-          case List.span (\d -> d /= (B, '\n')) diff of-            ([], _ : rest) -> nextLine rest-            (l, _ : rest) -> Just (l, rest)-            (l, []) -> Just (l, [])-      diffLine :: (Int, Int, [Diff Line]) -> PrimDiff -> (Int, Int, [Diff Line])-      diffLine (leftLineNo, rightLineNo, l) diff =-          case (\(x, y) -> (reverse x, reverse y)) $-               foldl (\(l, r) d -> case d of-                                     (F, c) -> (c : l, r)-                                     (S, c) -> (l, c : r)-                                     (B, c) -> (c : l, c : r))-                     ([], []) diff-          of ([], rightLine) -> (leftLineNo, rightLineNo + 1,-                                 OnlyInRight (Line rightLineNo rightLine) leftLineNo : l)-             (leftLine, []) -> (leftLineNo + 1, rightLineNo,-                                OnlyInLeft (Line leftLineNo leftLine) rightLineNo : l)-             (leftLine, rightLine)-                 | leftLine /= rightLine ->-                     (leftLineNo + 1, rightLineNo + 1,-                      InBoth (Line leftLineNo leftLine) (Line rightLineNo rightLine) : l)-                 | otherwise ->-                     (leftLineNo + 1, rightLineNo + 1, l)-      maximize :: [Diff Line] -> [Diff LineRange]-      maximize [] = []-      maximize (x : l) = maximize' (fmap (\a -> [a]) x) l-          where-            maximize' (OnlyInLeft xs rightLineNo) (OnlyInLeft y _ : rest) =-                maximize' (OnlyInLeft (y : xs) rightLineNo) rest-            maximize' (OnlyInRight xs leftLineNo) (OnlyInRight y _ : rest) =-                maximize' (OnlyInRight (y : xs) leftLineNo) rest-            maximize' (InBoth xs ys) (InBoth x y : rest) =-                maximize' (InBoth (x:xs) (y:ys)) rest-            maximize' acc rest = fmap mkLineRange acc : maximize rest-            mkLineRange :: [Line] -> LineRange-            mkLineRange [] = error ("multilineDiff: cannot convert an empty list of lines " ++-                                    "into a LineRange")-            mkLineRange r@(Line lastLineNo _ : _) =-                case reverse r of-                  l@(Line firstLineNo _ : _) -> LineRange (firstLineNo, lastLineNo)-                                                          (map line_content l)--prettyDiffs :: [Diff LineRange] -> Doc-prettyDiffs [] = empty-prettyDiffs (d : rest) = prettyDiff d $$ prettyDiffs rest-    where-      prettyDiff (OnlyInLeft inLeft lineNoRight) =-          prettyRange (lr_numbers inLeft) <> char 'd' <> int lineNoRight $$-          prettyLines '<' (lr_contents inLeft)-      prettyDiff (OnlyInRight inRight lineNoLeft) =-          int lineNoLeft <> char 'a' <> prettyRange (lr_numbers inRight) $$-          prettyLines '>' (lr_contents inRight)-      prettyDiff (InBoth inLeft inRight) =-          prettyRange (lr_numbers inLeft) <> char 'c' <> prettyRange (lr_numbers inRight) $$-          prettyLines '<' (lr_contents inLeft) $$-          text "---" $$-          prettyLines '>' (lr_contents inRight)-      prettyRange (start, end) =-          if start == end then int start else int start <> comma <> int end-      prettyLines start lines =-          vcat (map (\l -> char start <+> text l) lines)------- Tests for diff-----prop_diffOk :: DiffInput -> Bool-prop_diffOk inp =-    multiLineDiff cfg (di_left inp) (di_right inp) ==-    unsafePerformIO (runDiff (di_left inp) (di_right inp))-    where-      cfg = noColorsDiffConfig 'l' 'r'-      runDiff left right =-          do leftFile <- writeTemp left-             rightFile <- writeTemp right-             (ecode, out, err) <--                 readProcessWithExitCode "diff" [leftFile, rightFile] ""-             -- putStrLn ("OUT:\n" ++ out)-             -- putStrLn ("ERR:\n" ++ err)-             -- putStrLn ("ECODE:\n" ++ show ecode)-             case ecode of-               ExitSuccess -> return out-               ExitFailure 1 -> return out-               ExitFailure i -> error ("'diff " ++ leftFile ++ " " ++ rightFile ++-                                       "' failed with exit code " ++ show i ++-                                       ": " ++ show err)-      writeTemp s =-          do dir <- getTemporaryDirectory-             (fp, h) <- openTempFile dir "HTF-diff.txt"-             hPutStr h s-             hClose h-             return fp--data DiffInput = DiffInput { di_left :: String, di_right :: String }-               deriving (Show)--leftDiffInput = unlines ["1", "2", "3", "4", "", "5", "6", "7"]--instance Arbitrary DiffInput where-    arbitrary =-        do let leftLines = lines leftDiffInput-           rightLinesLines <- mapM modifyLine (leftLines ++ [""])-           return $ DiffInput (unlines leftLines)-                              (unlines (concat rightLinesLines))-      where-        randomString =-            do c <- (elements (['a'..'z']))-               return [c]-        modifyLine :: String -> Gen [String]-        modifyLine str =-            do prefixLen <- frequency [(20-i, return i) | i <- [0..5]]-               prefix <- mapM (\_ -> randomString) [1..prefixLen]-               frequency [ (5, return (prefix ++ [str]))-                         , (3, return (prefix ++ ["XXX" ++ str]))-                         , (2, return prefix)-                         , (2, return [str])]--debug = trace--- debug _ x = x+Haskell diff, in case the diff tool is not present+-}+multiLineDiffHaskell :: String -> String -> ColorString+multiLineDiffHaskell left right =+    noColor $ ppDiff $ D.getGroupedDiff (lines left) (lines right) -- this code is now part of the Diff library (hence the >0.3 in Cabal)  main =     do args <- getArgs@@ -392,8 +200,8 @@              _ -> fail ("USAGE: diff FILE1 FILE2")        left <- readFile leftFp        right <- readFile rightFp-       diff <- return $ multiLineDiff defaultTerminalDiffConfig left right-       putStr diff+       diff <- return $ multiLineDiffHaskell left right+       putStr $ T.unpack $ renderColorString diff True  -- Testcases: --@@ -401,4 +209,3 @@ -- vs. -- > 1 -- > 2--}
Test/Framework/HUnitWrapper.hs view
@@ -1,6 +1,7 @@ {-# OPTIONS_GHC -cpp -pgmPcpphs -optP --layout -optP --hashes -optP --cpp #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE OverloadedStrings #-}  -- -- Copyright (c) 2005, 2009, 2012  Stefan Wehr - http://www.stefanwehr.de@@ -59,6 +60,8 @@   assertThrowsSome_, assertThrowsSomeVerbose_,   assertThrowsIO_, assertThrowsIOVerbose_,   assertThrowsSomeIO_, assertThrowsSomeIOVerbose_,+  assertThrowsM_, assertThrowsMVerbose_,+  assertThrowsSomeM_, assertThrowsSomeMVerbose_,    -- * Assertions on Either values   assertLeft_, assertLeftVerbose_,@@ -82,8 +85,9 @@ ) where  import Control.Exception-import Control.Monad.Trans+import qualified Control.Exception.Lifted as ExL import Control.Monad.Trans.Control+import Control.Monad.Trans import qualified Test.HUnit as HU hiding ( assertFailure ) import qualified Language.Haskell.Exts.Pretty as HE import qualified Language.Haskell.Exts.Parser as HE@@ -98,7 +102,7 @@  -- WARNING: do not forget to add a preprocessor macro for new assertions!! -assertFailure__ :: Location -> String -> IO a+assertFailure__ :: Location -> ColorString -> IO a assertFailure__ loc s = unitTestFail (Just loc) s  {- |@@ -106,7 +110,8 @@ -} assertFailure_ :: Location -> String -> IO a assertFailure_ loc s =-    assertFailure__ loc (mkMsg "assertFailure" "" ("failed at " ++ showLoc loc) ++ ": " ++ s)+    assertFailure__ loc (mkMsg "assertFailure" ""+                                   ("failed at " ++ showLoc loc ++ ": " ++ s))  {- | Use @unitTestPending' msg test@ to mark the given test as pending@@ -115,12 +120,16 @@ unitTestPending' :: String -> IO a -> IO a unitTestPending' msg _ = unitTestPending msg -mkMsg :: String -> String -> String -> String-mkMsg fun extraInfo s =-    if null extraInfo-       then fun ++ (' ':s)-       else fun ++ " (" ++ extraInfo ++ ") " ++ s+mkMsg :: String -> String -> String -> ColorString+mkMsg s1 s2 s3 = mkColorMsg s1 s2 (noColor s3) +mkColorMsg :: String -> String -> ColorString -> ColorString+mkColorMsg fun extraInfo s =+    let pref = if null extraInfo+               then fun ++ " "+               else fun ++ " (" ++ extraInfo ++ ") "+    in noColor pref +++ s+ -- -- Dirty macro hackery (I'm too lazy ...) --@@ -165,15 +174,15 @@ -- Equality Assertions -- -equalityFailedMessage :: String -> String -> IO String+equalityFailedMessage :: String -> String -> IO ColorString equalityFailedMessage exp act =     do d <- diffWithSensibleConfig expP actP-       expected_ <- colorize firstDiffColor "* expected:"-       but_got_ <- colorize secondDiffColor "* but got:"-       diff_ <- colorize diffColor "* diff:"-       return ("\n" ++ expected_ ++ " " ++ withNewline expP ++-               "\n" ++ but_got_ ++ "  " ++ withNewline actP ++-               "\n" ++ diff_ ++ "     " ++ withNewline d +++       let expected_ = colorize firstDiffColor "* expected:"+       let but_got_ = colorize secondDiffColor "* but got:"+       let diff_ = colorize diffColor "* diff:"+       return ("\n" +++ expected_ +++ " " +++ noColor (withNewline expP) ++++               "\n" +++ but_got_ +++ "  " +++ noColor (withNewline actP) ++++               "\n" +++ diff_ +++ "     " +++ newlineBeforeDiff d +++ d +++                (if stringEq                    then "\nWARNING: strings are equal but actual values differ!"                    else ""))@@ -183,6 +192,11 @@             [] -> s             [_] -> s             _ -> '\n':s+      newlineBeforeDiff d =+          let f b = case colorStringFind (\c -> c == '\n') d b of+                      Just _ -> "\n"+                      Nothing -> ""+          in noColor' (f True) (f False)       (expP, actP, stringEq) =           case (pp exp, pp act) of             (Nothing, _) -> (exp, act, exp == act)@@ -212,7 +226,8 @@ _assertEqual_ name loc s expected actual =     if expected /= actual        then do x <- equalityFailedMessage (show expected) (show actual)-               assertFailure__ loc (mkMsg name s $ "failed at " ++ showLoc loc ++ x)+               assertFailure__ loc (mkColorMsg name s $+                                    noColor ("failed at " ++ showLoc loc) +++ x)        else return ()  DocAssertion(assertEqual, Fail if the two values of type @a@ are not equal.@@ -238,7 +253,8 @@ _assertEqualPretty_ name loc s expected actual =     if expected /= actual        then do x <- equalityFailedMessage (showPretty expected) (showPretty actual)-               assertFailure__ loc (mkMsg name s $ "failed at " ++ showLoc loc ++ x)+               assertFailure__ loc (mkColorMsg name s+                                       (noColor ("failed at " ++ showLoc loc) +++ x))        else return ()  DocAssertion(assertEqualPretty, Fail if the two values of type @a@ are not equal.@@ -299,8 +315,8 @@                                  ++ "\n actual length: " ++ show na))              | not (unorderedEq expected actual) ->                  do x <- equalityFailedMessage (show expected) (show actual)-                    assertFailure__ loc (mkMsg "assertSetEqual" s-                                   ("failed at " ++ showLoc loc ++ x))+                    assertFailure__ loc (mkColorMsg "assertSetEqual" s+                                         (noColor ("failed at " ++ showLoc loc) +++ x))              | otherwise -> return ()     where unorderedEq l1 l2 =               null (l1 \\ l2) && null (l2 \\ l1)@@ -330,26 +346,44 @@ _assertThrowsIO_ :: Exception e                  => String -> Location -> String -> IO a -> (e -> Bool) -> HU.Assertion _assertThrowsIO_ name loc s x f =-    do res <- try x+    _assertThrowsM_ name loc s x f+DocAssertion(assertThrowsIO, Fail if executing the 'IO' action does not+             throw an exception satisfying the given predicate @(e -> Bool)@.)+CreateAssertionsCtx(assertThrowsIO, Exception e, IO a -> (e -> Bool))++_assertThrowsSomeIO_ :: String -> Location -> String -> IO a -> HU.Assertion+_assertThrowsSomeIO_ name loc s x = _assertThrowsIO_ name loc s x (\ (_e::SomeException) -> True)+DocAssertion(assertThrowsSomeIO, Fail if executing the 'IO' action does not+             throw an exception.)+CreateAssertions(assertThrowsSomeIO, IO a)++_assertThrowsM_ :: (MonadBaseControl IO m, MonadIO m, Exception e)+                => String -> Location -> String -> m a -> (e -> Bool) -> m ()+_assertThrowsM_ name loc s x f =+    do res <- ExL.try x        case res of-         Right _ -> assertFailure__ loc (mkMsg name s+         Right _ -> liftIO $+                    assertFailure__ loc (mkMsg name s                                    ("failed at " ++ showLoc loc ++                                     ": no exception was thrown"))          Left e -> if f e then return ()-                   else assertFailure__ loc (mkMsg name s+                   else liftIO $+                        assertFailure__ loc (mkMsg name s                                        ("failed at " ++                                         showLoc loc ++                                         ": wrong exception was thrown: " ++                                         show e))-DocAssertion(assertThrowsIO, Fail if executing the 'IO' action does not+DocAssertion(assertThrowsM, Fail if executing the 'm' action does not              throw an exception satisfying the given predicate @(e -> Bool)@.)-CreateAssertionsCtx(assertThrowsIO, Exception e, IO a -> (e -> Bool))+CreateAssertionsGeneric(assertThrowsM, (MonadBaseControl IO m, MonadIO m, Exception e) =>,+                        m a -> (e -> Bool), m ()) -_assertThrowsSomeIO_ :: String -> Location -> String -> IO a -> HU.Assertion-_assertThrowsSomeIO_ name loc s x = _assertThrowsIO_ name loc s x (\ (e::SomeException) -> True)-DocAssertion(assertThrowsSomeIO, Fail if executing the 'IO' action does not+_assertThrowsSomeM_ :: (MonadBaseControl IO m, MonadIO m)+                    => String -> Location -> String -> m a -> m ()+_assertThrowsSomeM_ name loc s x = _assertThrowsM_ name loc s x (\ (_e::SomeException) -> True)+DocAssertion(assertThrowsSomeM, Fail if executing the 'm' action does not              throw an exception.)-CreateAssertions(assertThrowsSomeIO, IO a)+CreateAssertionsGeneric(assertThrowsSomeM, (MonadBaseControl IO m, MonadIO m) =>, m a, m ())  _assertThrows_ :: Exception e                => String -> Location -> String -> a -> (e -> Bool) -> HU.Assertion@@ -360,7 +394,7 @@  _assertThrowsSome_ :: String -> Location -> String -> a -> HU.Assertion _assertThrowsSome_ name loc s x =-    _assertThrows_ name loc s x (\ (e::SomeException) -> True)+    _assertThrows_ name loc s x (\ (_e::SomeException) -> True) DocAssertion(assertThrowsSome, Fail if evaluating the expression of type @a@ does not              throw an exception.) CreateAssertions(assertThrowsSome, a)
Test/Framework/HaskellParser.hs view
@@ -105,15 +105,51 @@          of the symbols _:"'>!#$%&*+./<=>?@\^|-~ with at most length 8 -}       parseMode :: Parser.ParseMode       parseMode = Parser.ParseMode { Parser.parseFilename = originalFileName+                                   , Parser.baseLanguage = Ext.Haskell2010                                    , Parser.ignoreLanguagePragmas = False                                    , Parser.ignoreLinePragmas = False-                                   , Parser.extensions =-                                       Ext.glasgowExts ++-                                       [Ext.BangPatterns, Ext.TemplateHaskell]-                                   , Parser.fixities =-                                       Just (Fix.baseFixities ++-                                             Fix.infixr_ 0 ["==>"])+                                   , Parser.extensions = (map Ext.EnableExtension+                                                              extensions)+                                   , Parser.fixities = Nothing                                    }+      extensions =+       [ Ext.ForeignFunctionInterface+       , Ext.UnliftedFFITypes+       , Ext.GADTs+       , Ext.ScopedTypeVariables+       , Ext.UnboxedTuples+       , Ext.TypeSynonymInstances+       , Ext.StandaloneDeriving+       , Ext.DeriveDataTypeable+       , Ext.FlexibleContexts+       , Ext.FlexibleInstances+       , Ext.ConstrainedClassMethods+       , Ext.MultiParamTypeClasses+       , Ext.FunctionalDependencies+       , Ext.MagicHash+       , Ext.PolymorphicComponents+       , Ext.ExistentialQuantification+       , Ext.UnicodeSyntax+       , Ext.PostfixOperators+       , Ext.PatternGuards+       , Ext.LiberalTypeSynonyms+       , Ext.RankNTypes+       , Ext.ImpredicativeTypes+       , Ext.TypeOperators+       , Ext.RecursiveDo+       , Ext.ParallelListComp+       , Ext.EmptyDataDecls+       , Ext.KindSignatures+       , Ext.GeneralizedNewtypeDeriving+       , Ext.TypeFamilies+       , Ext.NamedFieldPuns+       , Ext.RecordWildCards+       , Ext.PackageImports+       , Ext.ViewPatterns+       , Ext.TupleSections+       , Ext.NondecreasingIndentation+       , Ext.DoAndIfThenElse+       ]       unknownLoc :: Syn.SrcLoc       unknownLoc = Syn.SrcLoc originalFileName 0 0       transformModule (Syn.Module _ (Syn.ModuleName moduleName) _ _ _ imports decls)
Test/Framework/JsonOutput.hs view
@@ -61,12 +61,14 @@  import Test.Framework.TestTypes import Test.Framework.Location+import Test.Framework.Colors  import qualified Data.Aeson as J import Data.Aeson ((.=))  import qualified Data.ByteString.Lazy as BSL import qualified Data.ByteString.Lazy.Char8 as BSLC+import qualified Data.Text as T  class J.ToJSON a => HTFJsonObj a @@ -89,7 +91,7 @@       , te_result :: TestResult       , te_location :: Maybe Location       , te_callers :: [(Maybe String, Location)]-      , te_message :: String+      , te_message :: T.Text       , te_wallTimeMs :: Int       } @@ -191,8 +193,9 @@ mkTestEndEventObj :: FlatTestResult -> String -> TestEndEventObj mkTestEndEventObj ftr flatName =     let r = ft_payload ftr+        msg = renderColorString (rr_message r) False     in TestEndEventObj (mkTestObj ftr flatName) (rr_result r) (rr_location r) (rr_callers r)-                       (rr_message r) (rr_wallTimeMs r)+                       msg (rr_wallTimeMs r)  mkTestListObj :: [(FlatTest, String)] -> TestListObj mkTestListObj l =
Test/Framework/Preprocessor.hs view
@@ -77,6 +77,8 @@              ,"assertThrowsSome"              ,"assertThrowsIO"              ,"assertThrowsSomeIO"+             ,"assertThrowsM"+             ,"assertThrowsSomeM"              ,"assertLeft"              ,"assertLeftNoShow"              ,"assertRight"
Test/Framework/Process.hs view
@@ -67,8 +67,8 @@     --  data gets pulled as it becomes available. you have to force the     --  output strings before waiting for the process to terminate.     ---    forkIO (Control.Exception.evaluate (length output) >> return ())-    forkIO (Control.Exception.evaluate (length errput) >> return ())+    _ <- forkIO (Control.Exception.evaluate (length output) >> return ())+    _ <- forkIO (Control.Exception.evaluate (length errput) >> return ())      -- And now we wait. We must wait after we read, unsurprisingly.     ecode <- waitForProcess pid -- blocks without -threaded, you're warned.
Test/Framework/QuickCheckWrapper.hs view
@@ -72,7 +72,7 @@ -- | Change the default 'Args' used to evaluate quick check properties. setDefaultArgs :: Args -> IO () setDefaultArgs args =-    do withMVar qcState $ \state -> return (state { qc_args = args })+    do _ <- withMVar qcState $ \state -> return (state { qc_args = args })        return ()  -- | Retrieve the 'Args' currently used per default when evaluating quick check properties.@@ -92,7 +92,7 @@     withMVar qcState $ \state ->         do eitherArgs <-                (let a = (argsModifier t) (qc_args state)-                in do evaluate (length (show a))+                in do _ <- evaluate (length (show a))                       return (Right a))                `catch`                (\e -> return $ Left (show (e :: SomeException)))
Test/Framework/TestManager.hs view
@@ -46,6 +46,7 @@ import System.Exit (ExitCode(..), exitWith) import System.Environment (getArgs) import Control.Exception (finally)+import qualified Data.List as List  import System.IO @@ -57,21 +58,24 @@ import Test.Framework.CmdlineOptions import Test.Framework.TestReporter import Test.Framework.Location+import Test.Framework.Colors+import Test.Framework.ThreadPool  -- | Construct a test where the given 'Assertion' checks a quick check property. -- Mainly used internally by the htfpp preprocessor. makeQuickCheckTest :: TestID -> Location -> Assertion -> Test-makeQuickCheckTest id loc ass = BaseTest QuickCheckTest id (Just loc) ass+makeQuickCheckTest id loc ass = BaseTest QuickCheckTest id (Just loc) defaultTestOptions ass  -- | Construct a unit test from the given 'IO' action. -- Mainly used internally by the htfpp preprocessor.-makeUnitTest :: TestID -> Location -> IO a -> Test-makeUnitTest id loc ass = BaseTest UnitTest id (Just loc) (ass >> return ())+makeUnitTest :: AssertionWithTestOptions a => TestID -> Location -> a -> Test+makeUnitTest id loc ass =+    BaseTest UnitTest id (Just loc) (testOptions ass) (assertion ass)  -- | Construct a black box test from the given 'Assertion'. -- Mainly used internally. makeBlackBoxTest :: TestID -> Assertion -> Test-makeBlackBoxTest id ass = BaseTest BlackBoxTest id Nothing ass+makeBlackBoxTest id ass = BaseTest BlackBoxTest id Nothing defaultTestOptions ass  -- | Create a named 'TestSuite' from a list of 'Test' values. makeTestSuite :: TestID -> [Test] -> TestSuite@@ -108,8 +112,8 @@     flatten action = flatten (makeUnitTest "unnamed test" unknownLocation action)  flattenTest :: Test -> [FlatTest]-flattenTest (BaseTest sort id mloc x) =-    [FlatTest sort (TestPathBase id) mloc x]+flattenTest (BaseTest sort id mloc opts x) =+    [FlatTest sort (TestPathBase id) mloc (WithTestOptions opts x)] flattenTest (CompoundTest ts) =     flattenTestSuite ts @@ -121,39 +125,57 @@     let fts = concatMap flattenTest ts     in map (\ft -> ft { ft_path = TestPathCompound Nothing (ft_path ft) }) fts -runFlatTest :: FlatTest -> TR FlatTestResult-runFlatTest ft =-    do reportTestStart ft-       (res, time) <- liftIO $ measure $ HU.performTestCase (ft_payload ft)-       let (testResult, (mLoc, callers, msg)) =-             case res of-               Nothing -> (Pass, (Nothing, [], ""))-               Just (isFailure, msg') ->-                   if ft_sort ft /= QuickCheckTest-                      then let utr = deserializeHUnitMsg msg'-                               r = case () of-                                     _| utr_pending utr -> Pending-                                      | isFailure -> Fail-                                      | otherwise -> Error-                           in (r, (utr_location utr, utr_callingLocations utr, utr_message utr))-                      else let (r, s) = deserializeQuickCheckMsg msg'-                           in (r, (Nothing, [], s))-           rr = FlatTest-                  { ft_sort = ft_sort ft-                  , ft_path = ft_path ft-                  , ft_location = ft_location ft-                  , ft_payload = RunResult testResult mLoc callers msg time }-       return rr--handleRunResult :: FlatTestResult -> TR ()-handleRunResult r =-    do modify (\s -> s { ts_results = r : ts_results s })-       reportTestResult r+mkFlatTestRunner :: FlatTest -> ThreadPoolEntry TR () (Maybe (Bool, String), Int)+mkFlatTestRunner ft = (pre, action, post)+    where+      pre = reportTestStart ft+      action _ = measure $ HU.performTestCase (wto_payload (ft_payload ft))+      post excOrResult =+          let (testResult, (mLoc, callers, msg, time)) =+                 case excOrResult of+                   Left exc -> (Error, (Nothing,+                                        [],+                                        noColor ("Running test unexpectedly failed: " ++ show exc),+                                        (-1)))+                   Right (res, time) ->+                       case res of+                         Nothing -> (Pass, (Nothing, [], emptyColorString, time))+                         Just (isFailure, msg') ->+                           if ft_sort ft /= QuickCheckTest+                              then let utr = deserializeHUnitMsg msg'+                                       r = case () of+                                             _| utr_pending utr -> Pending+                                              | isFailure -> Fail+                                              | otherwise -> Error+                                   in (r, (utr_location utr, utr_callingLocations utr, utr_message utr, time))+                              else let (r, s) = deserializeQuickCheckMsg msg'+                                   in (r, (Nothing, [], noColor s, time))+              rr = FlatTest+                     { ft_sort = ft_sort ft+                     , ft_path = ft_path ft+                     , ft_location = ft_location ft+                     , ft_payload = RunResult testResult mLoc callers msg time }+          in do modify (\s -> s { ts_results = rr : ts_results s })+                reportTestResult rr  runAllFlatTests :: [FlatTest] -> TR () runAllFlatTests tests =     do reportGlobalStart tests-       mapM_ (\ft -> runFlatTest ft >>= handleRunResult) tests+       tc <- ask+       case tc_threads tc of+         Nothing ->+             let entries = map mkFlatTestRunner tests+             in tp_run sequentialThreadPool entries+         Just i ->+             let (ptests, stests) = List.partition (\t -> to_parallel (wto_options (ft_payload t))) tests+                 pentries' = map mkFlatTestRunner ptests+                 sentries = map mkFlatTestRunner stests+             in do tp <- parallelThreadPool i+                   pentries <- if tc_shuffle tc+                               then liftIO (shuffleIO pentries')+                               else return pentries'+                   tp_run tp pentries+                   tp_run sequentialThreadPool sentries  -- | Run something testable using the 'Test.Framework.TestConfig.defaultCmdlineOptions'. runTest :: TestableHTF t => t              -- ^ Testable thing
Test/Framework/TestManagerInternal.hs view
@@ -31,9 +31,9 @@ import Test.Framework.TestTypes import Test.Framework.Utils import Test.Framework.Location+import Test.Framework.Colors  import qualified Test.HUnit.Lang as HU-import Control.Monad.Trans import Control.Monad.Trans.Control import qualified Control.Exception.Lifted as Exc @@ -72,11 +72,11 @@     = UnitTestResult       { utr_location :: Maybe Location       , utr_callingLocations :: [(Maybe String, Location)]-      , utr_message :: String+      , utr_message :: ColorString       , utr_pending :: Bool       } deriving (Eq, Show, Read) -unitTestFail :: Maybe Location -> String -> IO a+unitTestFail :: Maybe Location -> ColorString -> IO a unitTestFail loc s =     do assertFailureHTF (show (UnitTestResult loc [] s False))        error "unitTestFail: UNREACHABLE"@@ -90,14 +90,14 @@ -- Mark a unit test as pending without removing it from the test suite. unitTestPending :: String -> IO a unitTestPending s =-    do assertFailureHTF (show (UnitTestResult Nothing [] s True))+    do assertFailureHTF (show (UnitTestResult Nothing [] (noColor s) True))        error "unitTestFail: UNREACHABLE"  deserializeHUnitMsg :: String -> UnitTestResult deserializeHUnitMsg msg =     case readM msg of       Just r -> r-      _ -> UnitTestResult Nothing [] msg False+      _ -> UnitTestResult Nothing [] (noColor msg) False  blackBoxTestFail :: String -> Assertion blackBoxTestFail = assertFailureHTF
Test/Framework/TestReporter.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE OverloadedStrings #-} {-|  This module defines functions for notifying all test reporters registered about@@ -8,6 +9,7 @@ -} module Test.Framework.TestReporter ( +    IsParallel(..), isParallelFromBool, IsJsonOutput(..), IsXmlOutput(..),     reportAllTests, reportGlobalStart, reportTestStart, reportTestResult,     reportGlobalResults, defaultTestReporters @@ -16,14 +18,14 @@ import Test.Framework.TestTypes import Test.Framework.Location import Test.Framework.Colors-import Test.Framework.Utils import Test.Framework.JsonOutput+import Test.Framework.XmlOutput  import System.IO import Control.Monad.RWS import Text.PrettyPrint -import qualified Data.ByteString as BS+import qualified Data.Text.IO as T import qualified Data.ByteString.Lazy as BSL  -- | Invokes 'tr_reportAllTests' on all test reporters registered.@@ -56,13 +58,23 @@     do reps <- asks tc_reporters        mapM_ (\r -> tr_reportGlobalResults r t l1 l2 l3 l4) reps +data IsParallel = Parallel | NonParallel++isParallelFromBool :: Bool -> IsParallel+isParallelFromBool True = Parallel+isParallelFromBool False = NonParallel++data IsJsonOutput = JsonOutput | NoJsonOutput+data IsXmlOutput = XmlOutput | NoXmlOutput+ -- | The default test reporters for HTF.-defaultTestReporters :: Bool -- ^ 'True' if tests are run in parallel-                     -> Bool -- ^ 'True' if machine output should be produced+defaultTestReporters :: IsParallel+                     -> IsJsonOutput+                     -> IsXmlOutput                      -> [TestReporter]-defaultTestReporters inParallel forMachine =+defaultTestReporters inParallel forMachine doXml =     case (inParallel, forMachine) of-      (False, False) ->+      (NonParallel, NoJsonOutput) ->           [TestReporter            { tr_id = "rep_seq_human"            , tr_reportAllTests = reportAllTestsH@@ -70,8 +82,8 @@            , tr_reportTestStart = reportTestStartHS            , tr_reportTestResult = reportTestResultHS            , tr_reportGlobalResults = reportGlobalResultsH-           }]-      (True, False) ->+           }] ++ xmlReporters+      (Parallel, NoJsonOutput) ->           [TestReporter            { tr_id = "rep_par_human"            , tr_reportAllTests = reportAllTestsH@@ -79,8 +91,8 @@            , tr_reportTestStart = reportTestStartHP            , tr_reportTestResult = reportTestResultHP            , tr_reportGlobalResults = reportGlobalResultsH-           }]-      (False, True) ->+           }] ++ xmlReporters+      (NonParallel, JsonOutput) ->           [TestReporter            { tr_id = "rep_seq_machine"            , tr_reportAllTests = reportAllTestsM@@ -88,8 +100,8 @@            , tr_reportTestStart = reportTestStartMS            , tr_reportTestResult = reportTestResultMS            , tr_reportGlobalResults = reportGlobalResultsM-           }]-      (True, True) ->+           }] ++ xmlReporters+      (Parallel, JsonOutput) ->           [TestReporter            { tr_id = "rep_par_machine"            , tr_reportAllTests = reportAllTestsM@@ -97,7 +109,14 @@            , tr_reportTestStart = reportTestStartMP            , tr_reportTestResult = reportTestResultMP            , tr_reportGlobalResults = reportGlobalResultsM-           }]+           }] ++ xmlReporters+    where+      xmlReporters =+          case doXml of+            NoXmlOutput -> []+            XmlOutput -> [(emptyTestReporter "rep_xml") {+                            tr_reportGlobalResults = reportGlobalResultsXml+                          }]  -- -- output for humans@@ -112,8 +131,8 @@  reportHumanTestStartMessage :: ReportLevel -> GenFlatTest a -> TR () reportHumanTestStartMessage level ft =-    do t <- liftIO $ colorize testStartColor "[TEST] "-       reportTR level (t ++ (humanTestName ft))+    do let t = colorize testStartColor "[TEST] "+       reportTR level (t +++ noColor (humanTestName ft))  -- sequential reportGlobalStartHS :: ReportGlobalStart@@ -128,40 +147,27 @@         msg = attachCallStack (rr_message (ft_payload ftr)) (rr_callers (ft_payload ftr))     in case res of          Pass ->-             do suf <- okSuffix-                reportMessage Debug msg suf+             reportMessage Debug msg okSuffix          Pending ->              do reportHumanTestStartMessageIfNeeded-                suf <- pendingSuffix-                reportMessage Info msg suf+                reportMessage Info msg pendingSuffix          Fail ->              do reportHumanTestStartMessageIfNeeded-                suf <- failureSuffix-                reportMessage Info msg suf+                reportMessage Info msg failureSuffix          Error ->              do reportHumanTestStartMessageIfNeeded-                suf <- errorSuffix-                reportMessage Info msg suf+                reportMessage Info msg errorSuffix    where-     attachCallStack msg callStack =-         case reverse callStack of-           [] -> msg-           l -> ensureNewline msg ++-                unlines (map formatCallStackElem l)-     formatCallStackElem (mMsg, loc) =-         "  called from " ++ showLoc loc ++ (case mMsg of-                                               Nothing -> ""-                                               Just s -> " (" ++ s ++ ")")      reportHumanTestStartMessageIfNeeded =          do tc <- ask             when (tc_quiet tc) (reportHumanTestStartMessage Info ftr)      reportMessage level msg suffix =-         reportTR level (ensureNewline msg ++ suffix ++ timeStr)+         reportTR level (ensureNewlineColorString msg +++ suffix +++ noColor timeStr)      timeStr = " (" ++ show (rr_wallTimeMs (ft_payload ftr)) ++ "ms)\n"-     failureSuffix = liftIO $ colorize warningColor "*** Failed!"-     errorSuffix = liftIO $ colorize warningColor "@@@ Error!"-     pendingSuffix = liftIO $ colorize pendingColor "^^^ Pending!"-     okSuffix = liftIO $ colorize testOkColor  "+++ OK"+     failureSuffix = colorize warningColor "*** Failed!"+     errorSuffix = colorize warningColor "@@@ Error!"+     pendingSuffix = colorize pendingColor "^^^ Pending!"+     okSuffix = colorize testOkColor  "+++ OK"  -- parallel reportGlobalStartHP :: ReportGlobalStart@@ -169,7 +175,7 @@  reportTestStartHP :: ReportTestStart reportTestStartHP ft =-     do reportTR Debug ("Starting " ++ (humanTestName ft))+     do reportStringTR Debug ("Starting " ++ (humanTestName ft))  reportTestResultHP :: ReportTestResult reportTestResultHP ftr =@@ -179,7 +185,7 @@ -- results and all tests reportAllTestsH :: ReportAllTests reportAllTestsH l =-    reportDoc Info (renderTestNames l)+    reportStringTR Info (render (renderTestNames l))  reportGlobalResultsH :: ReportGlobalResults reportGlobalResultsH t passedL pendingL failedL errorL =@@ -188,27 +194,28 @@            failed = length failedL            error = length errorL            total = passed + failed + error + pending-       pendings <- liftIO $ colorize pendingColor "* Pending:"-       failures <- liftIO $ colorize warningColor "* Failures:"-       errors <- liftIO $ colorize warningColor "* Errors:"-       reportTR Info ("* Tests:    " ++ show total ++ "\n" ++-                      "* Passed:   " ++ show passed ++ "\n" ++-                      pendings ++ "  " ++ show pending ++ "\n" ++-                      failures ++ " " ++ show failed ++ "\n" ++-                      errors ++ "   " ++ show error)+       let pendings = colorize pendingColor "* Pending:"+           failures = colorize warningColor "* Failures:"+           errors = colorize warningColor "* Errors:"+       reportTR Info ("* Tests:    " +++ showC total +++ "\n" ++++                      "* Passed:   " +++ showC passed +++ "\n" ++++                      pendings +++ "  " +++ showC pending +++ "\n" ++++                      failures +++ " " +++ showC failed +++ "\n" ++++                      errors +++ "   " +++ showC error)        when (pending > 0) $-          reportDoc Info-              (text ('\n' : pendings) $$ renderTestNames' (reverse pendingL))+          reportTR Info+              ("\n" +++ pendings +++ renderTestNames' (reverse pendingL))        when (failed > 0) $-          reportDoc Info-              (text ('\n' : failures) $$ renderTestNames' (reverse failedL))+          reportTR Info+              ("\n" +++ failures +++ renderTestNames' (reverse failedL))        when (error > 0) $-          reportDoc Info-              (text ('\n' : errors) $$ renderTestNames' (reverse errorL))-       reportTR Info ("\nTotal execution time: " ++ show t ++ "ms")+          reportTR Info+              ("\n" +++ errors +++ renderTestNames' (reverse errorL))+       reportStringTR Info ("\nTotal execution time: " ++ show t ++ "ms")     where+      showC x = noColor (show x)       renderTestNames' rrs =-          nest 2 $ renderTestNames rrs+          noColor $ render $ nest 2 $ renderTestNames rrs  renderTestNames :: [GenFlatTest a] -> Doc renderTestNames l =@@ -254,22 +261,28 @@     let json = mkTestResultsObj t (length pass) (length pending) (length failed) (length errors)     in reportJsonTR json +reportGlobalResultsXml :: ReportGlobalResults+reportGlobalResultsXml t pass pending failed errors =+    do let xml = mkGlobalResultsXml t pass pending failed errors+       tc <- ask+       case tc_outputXml tc of+         Just fname -> liftIO $ withFile fname WriteMode $ \h -> BSL.hPut h xml+         Nothing -> liftIO $ BSL.putStr xml+ -- -- General reporting routines -- -reportDoc :: ReportLevel -> Doc -> TR ()-reportDoc level doc = reportTR level (render doc)--reportTR :: ReportLevel -> String -> TR ()+reportTR :: ReportLevel -> ColorString -> TR () reportTR level msg =     do tc <- ask-       reportGen tc level (\h -> hPutStrLn h msg)+       let s = renderColorString msg (tc_useColors tc)+       reportGen tc level (\h -> T.hPutStrLn h s) -reportBytesTR :: ReportLevel -> BS.ByteString -> TR ()-reportBytesTR level msg =+reportStringTR :: ReportLevel -> String -> TR ()+reportStringTR level msg =     do tc <- ask-       reportGen tc level (\h -> BS.hPut h msg)+       reportGen tc level (\h -> hPutStrLn h msg)  reportLazyBytesTR :: ReportLevel -> BSL.ByteString -> TR () reportLazyBytesTR level msg =
Test/Framework/TestTypes.hs view
@@ -1,4 +1,5 @@-{-| +{-# LANGUAGE FlexibleInstances #-}+{-|  This module defines types (and small auxiliary functions) for organizing tests, for configuring the execution of@@ -8,16 +9,17 @@ module Test.Framework.TestTypes (    -- * Organizing tests-  TestID, Assertion, Test(..), TestSuite(..), TestSort(..),+  TestID, Assertion, Test(..), TestOptions(..), AssertionWithTestOptions(..), WithTestOptions(..),+  TestSuite(..), TestSort(..),   TestPath(..), GenFlatTest(..), FlatTest, TestFilter,-  testPathToList, flatName,+  testPathToList, flatName, finalName, prefixName, defaultTestOptions, withOptions,    -- * Executing tests   TR, TestState(..), initTestState, TestConfig(..), TestOutput(..),    -- * Reporting results   ReportAllTests, ReportGlobalStart, ReportTestStart, ReportTestResult, ReportGlobalResults,-  TestReporter(..),+  TestReporter(..), emptyTestReporter, attachCallStack, CallStack,    -- * Specifying results.   TestResult(..), FlatTestResult, Milliseconds, RunResult(..)@@ -25,6 +27,7 @@ ) where  import Test.Framework.Location+import Test.Framework.Colors  import Control.Monad.RWS import System.IO@@ -41,8 +44,44 @@ data TestSort = UnitTest | QuickCheckTest | BlackBoxTest               deriving (Eq,Show,Read) +-- | General options for tests+data TestOptions = TestOptions {+      to_parallel :: Bool+    }+    deriving (Eq,Show,Read)++-- | The default 'TestOptions'+defaultTestOptions :: TestOptions+defaultTestOptions = TestOptions {+                       to_parallel = True+                     }++-- | Something with 'TestOptions'+data WithTestOptions a = WithTestOptions {+      wto_options :: TestOptions+    , wto_payload :: a+    }+    deriving (Eq,Show,Read)++-- | Shortcut for constructing a 'WithTestOptions' value.+withOptions :: (TestOptions -> TestOptions) -> a -> WithTestOptions a+withOptions f x = WithTestOptions (f defaultTestOptions) x++-- | A type class for an assertion with 'TestOptions'.+class AssertionWithTestOptions a where+    testOptions :: a -> TestOptions+    assertion :: a -> Assertion++instance AssertionWithTestOptions (IO a) where+    testOptions _ = defaultTestOptions+    assertion io = io >> return ()++instance AssertionWithTestOptions (WithTestOptions (IO a)) where+    testOptions (WithTestOptions opts _) = opts+    assertion (WithTestOptions _ io) = io >> return ()+ -- | Abstract type for tests and their results.-data Test = BaseTest TestSort TestID (Maybe Location) Assertion+data Test = BaseTest TestSort TestID (Maybe Location) TestOptions Assertion           | CompoundTest TestSuite  -- | Abstract type for test suites and their results.@@ -62,8 +101,26 @@ -- | Creates a string representation from a 'TestPath'. flatName :: TestPath -> String flatName p =-    List.intercalate ":" (map (fromMaybe "") (testPathToList p))+    flatNameFromList (testPathToList p) +flatNameFromList :: [Maybe TestID] -> String+flatNameFromList l =+    List.intercalate ":" (map (fromMaybe "") l)++-- | Returns the final name of a 'TestPath'+finalName :: TestPath -> String+finalName (TestPathBase i) = i+finalName (TestPathCompound _ p) = finalName p++-- | Returns the name of the prefix of a test path. The prefix is everything except the+--   last element.+prefixName :: TestPath -> String+prefixName path =+    let l = case reverse (testPathToList path) of+              [] -> []+              (_:xs) -> reverse xs+    in flatNameFromList l+ -- | Generic type for flattened tests and their results. data GenFlatTest a     = FlatTest@@ -74,7 +131,7 @@       }  -- | Flattened representation of tests.-type FlatTest = GenFlatTest Assertion+type FlatTest = GenFlatTest (WithTestOptions Assertion)  -- | A filter is a predicate on 'FlatTest'. If the predicate is 'True', the flat test is run. type TestFilter = FlatTest -> Bool@@ -86,16 +143,31 @@ -- | A type synonym for time in milliseconds. type Milliseconds = Int +-- | A type for call-stacks+type CallStack = [(Maybe String, Location)]+ -- | The result of a test run. data RunResult     = RunResult       { rr_result :: TestResult       -- ^ The summary result of the test.       , rr_location :: Maybe Location -- ^ The location where the test failed (if applicable).-      , rr_callers :: [(Maybe String, Location)] -- ^ Information about the callers of the location where the test failed-      , rr_message :: String          -- ^ A message describing the result.+      , rr_callers :: CallStack       -- ^ Information about the callers of the location where the test failed+      , rr_message :: ColorString     -- ^ A message describing the result.       , rr_wallTimeMs :: Milliseconds -- ^ Execution time in milliseconds.       } +attachCallStack :: ColorString -> CallStack -> ColorString+attachCallStack msg callStack =+    case reverse callStack of+      [] -> msg+      l -> ensureNewlineColorString msg ++++           noColor (unlines (map formatCallStackElem l))+    where+      formatCallStackElem (mMsg, loc) =+          "  called from " ++ showLoc loc ++ (case mMsg of+                                                Nothing -> ""+                                                Just s -> " (" ++ s ++ ")")+ -- | The result of running a 'FlatTest' type FlatTestResult = GenFlatTest RunResult @@ -120,11 +192,13 @@ data TestConfig     = TestConfig       { tc_quiet :: Bool                -- ^ If set, displays messages only for failed tests.----      , tc_threads :: Maybe Int       Use @Just i@ for parallel execution with @i@ threads, @Nothing@ for sequential execution (currently unused).+      , tc_threads :: Maybe Int         -- ^ Use @Just i@ for parallel execution with @i@ threads, @Nothing@ for sequential execution.+      , tc_shuffle :: Bool              -- ^ Shuffle tests before parallel execution       , tc_output :: TestOutput         -- ^ Output destination of progress and result messages.+      , tc_outputXml :: Maybe FilePath  -- ^ Output destination of XML result summary       , tc_filter :: TestFilter         -- ^ Filter for the tests to run.       , tc_reporters :: [TestReporter]  -- ^ Test reporters to use.+      , tc_useColors :: Bool            -- ^ Whether to use colored output       }  instance Show TestConfig where@@ -147,6 +221,17 @@       , tr_reportTestStart :: ReportTestStart      -- ^ Called to report the start of a single test.       , tr_reportTestResult :: ReportTestResult    -- ^ Called to report the result of a single test.       , tr_reportGlobalResults :: ReportGlobalResults  -- ^ Called to report the overall results of all tests.+      }++emptyTestReporter :: String -> TestReporter+emptyTestReporter id =+    TestReporter+      { tr_id = id+      , tr_reportAllTests = \_ -> return ()+      , tr_reportGlobalStart = \_ -> return ()+      , tr_reportTestStart = \_ -> return ()+      , tr_reportTestResult = \_ -> return ()+      , tr_reportGlobalResults = \_ _ _ _ _ -> return ()       }  instance Show TestReporter where
+ Test/Framework/ThreadPool.hs view
@@ -0,0 +1,215 @@+{-# LANGUAGE ScopedTypeVariables #-}+--+-- Copyright (c) 2013   Stefan Wehr - http://www.stefanwehr.de+--+-- This library is free software; you can redistribute it and/or+-- modify it under the terms of the GNU Lesser General Public+-- License as published by the Free Software Foundation; either+-- version 2.1 of the License, or (at your option) any later version.+--+-- This library is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU+-- Lesser General Public License for more details.+--+-- You should have received a copy of the GNU Lesser General Public+-- License along with this library; if not, write to the Free Software+-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA+--++module Test.Framework.ThreadPool (++    ThreadPoolEntry, ThreadPool(..), sequentialThreadPool, parallelThreadPool+  , threadPoolTest++) where++import qualified Control.Exception as Ex+import Control.Monad+import Control.Monad.Trans+import Control.Concurrent++-- for tests+import System.Random++type ThreadPoolEntry m a b = ( m a        -- pre-action, must not throw exceptions+                             , a -> IO b  -- action+                             , Either Ex.SomeException b -> m ()  -- post-action, must not throw exceptions+                             )++data ThreadPool m a b+    = ThreadPool+      { tp_run :: [ThreadPoolEntry m a b] -> m () }++sequentialThreadPool :: MonadIO m => ThreadPool m a b+sequentialThreadPool = ThreadPool runSequentially++parallelThreadPool :: MonadIO m => Int -> m (ThreadPool m a b)+parallelThreadPool n =+    do when (n < 1) $ fail ("invalid number of workers: " ++ show n)+       return (ThreadPool (runParallel n))++runSequentially :: MonadIO m => [ThreadPoolEntry m a b] -> m ()+runSequentially entries =+    mapM_ run entries+    where+      run (pre, action, post) =+          do a <- pre+             b <- liftIO $ Ex.try (action a)+             post b++data WorkItem m b = Work (IO b) (Either Ex.SomeException b -> m ()) | Done++instance Show (WorkItem m b) where+    show (Work _ _) = "Work"+    show Done = "Done"++type NamedMVar a = (String, MVar a)+type NamedChan a = (String, Chan a)++type ToWorker m b = NamedMVar (WorkItem m b)++data WorkResult m b = WorkResult (m ()) (ToWorker m b)++instance Show (WorkResult m b) where+    show _ = "WorkResult"++type FromWorker m b = NamedChan (WorkResult m b)++runParallel :: forall m a b . MonadIO m => Int -> [ThreadPoolEntry m a b] -> m ()+runParallel _ [] = return ()+runParallel n entries =+    do when (n < 1) $ fail ("invalid number of workers: " ++ show n)+       fromWorker <- liftIO $ newNamedChan "fromWorker"+       let nWorkers = min n (length entries)+       toWorkers <- mapM (\i -> liftIO $ mkWorker i fromWorker) [1..nWorkers]+       let (initEntries, restEntries) = splitAt nWorkers entries+       mapM_ (\(mvar, entry) -> runEntry entry mvar) (zip toWorkers initEntries)+       loop fromWorker nWorkers restEntries+    where+      loop :: FromWorker m b -> Int -> [ThreadPoolEntry m a b] -> m ()+      loop fromWorker nWorkers [] =+          cleanup fromWorker nWorkers+      loop fromWorker nWorkers (x:xs) =+          do toWorker <- waitForWorkerResult fromWorker+             runEntry x toWorker+             loop fromWorker nWorkers xs+      cleanup :: FromWorker m b -> Int -> m ()+      -- n is the number of workers that will still write to fromWorker+      cleanup fromWorker n =+          do debug ("cleanup, n=" ++ show n)+             toWorker <- waitForWorkerResult fromWorker+             liftIO $ putNamedMVar toWorker Done+             when (n > 1) $ cleanup fromWorker (n - 1)+      waitForWorkerResult :: FromWorker m b -> m (ToWorker m b)+      waitForWorkerResult fromWorker =+          do WorkResult postAction toWorker <- liftIO $ readNamedChan fromWorker+             postAction+             return toWorker+      runEntry :: ThreadPoolEntry m a b -> ToWorker m b -> m ()+      runEntry (pre, action, post) toWorker =+          do a <- pre+             liftIO $ putNamedMVar toWorker (Work (action a) post)+      mkWorker :: Int -> FromWorker m b -> IO (ToWorker m b)+      mkWorker i fromWorker =+          do toWorker <- newEmptyNamedMVar ("worker" ++ show i)+             let loop = do workItem <- takeNamedMVar toWorker+                           case workItem of+                             Done ->+                                 do debug ("worker" ++ show i ++ " exiting!")+                                    return ()+                             Work action post ->+                                 do res <- Ex.try action+                                    _ <- Ex.evaluate res+                                    writeNamedChan fromWorker (WorkResult (post res) toWorker)+                                    loop+             _ <- forkIO (loop `Ex.catch` (\(e::Ex.BlockedIndefinitelyOnMVar) ->+                                          fail ("worker " ++ show i ++ ": " ++ show e)))+             return toWorker++--+-- Debugging and testing+--++_DEBUG_ = False++newNamedChan :: String -> IO (NamedChan a)+newNamedChan name =+    do chan <- newChan+       return (name, chan)++readNamedChan :: Show a => NamedChan a -> IO a+readNamedChan (name, chan) =+    do debug ("readChan[" ++ name ++ "]...")+       x <- readChan chan+       debug ("DONE readChan[" ++ name ++"]=" ++ show x)+       return x++writeNamedChan :: Show a => NamedChan a -> a -> IO ()+writeNamedChan (name, chan) x =+    do debug ("writeChan[" ++ name ++ "]=" ++ show x)+       writeChan chan x++newEmptyNamedMVar :: String -> IO (NamedMVar a)+newEmptyNamedMVar name =+    do mvar <- newEmptyMVar+       return (name, mvar)++putNamedMVar :: Show a => NamedMVar a -> a -> IO ()+putNamedMVar (name, mvar) x =+    do debug ("putMVar[" ++ name ++ "]=" ++ show x ++ "...")+       putMVar mvar x+       debug ("DONE putMVar[" ++ name ++ "]=" ++ show x)++takeNamedMVar :: Show a => NamedMVar a -> IO a+takeNamedMVar (name, mvar) =+    do debug ("takeMVar[" ++ name ++ "]...")+       x <- takeMVar mvar+       debug ("DONE takeMVar[" ++ name ++ "]=" ++ show x)+       return x++debug :: MonadIO m => String -> m ()+debug s = if _DEBUG_ then liftIO $ putStrLn s else return ()++runTestParallel :: Int -> Int -> IO ()+runTestParallel nEntries n =+    do putStrLn ("Running test " ++ show n)+       boxes <- mapM (\i -> do mvar <- newEmptyNamedMVar ("testbox" ++ show i)+                               return (mvar, i))+                      [1..nEntries]+       let entries = map mkEntry boxes+       runParallel n entries+       debug ("Checking boxes in test " ++ show n)+       --runSequentially entries+       mapM_ assertBox boxes+       putStrLn ("Test " ++ show n ++ " successful")+    where+      mkEntry (mvar, i) =+          let pre = myThreadId+              post x = case x of+                         Left err -> fail ("Exception in worker thread: " ++ show err)+                         Right y -> do tid <- myThreadId+                                       putNamedMVar mvar (y, tid)+              action x = do tid <- myThreadId+                            j <- randomIO+                            let micros = (j `mod` 50)+                            threadDelay micros+                            return (x, tid, i)+          in (pre, action, post)+      assertBox (mvar, i) =+         do ((preTid, actionTid, i'), postTid) <- takeNamedMVar mvar+            tid <- myThreadId+            assertEq "pre-tid" tid preTid+            assertEq "post-tid" tid postTid+            assertNeq "action-tid" tid actionTid+            assertEq "i" i i'+      assertEq what exp act =+          when (exp /= act) $ fail (what ++ " wrong, expected=" ++ show exp ++ ", actual=" +++                                    show act)+      assertNeq what exp act =+          when (exp == act) $ fail (what ++ " wrong, did not expected " ++ show exp)++threadPoolTest (i, j) nEntries =+    mapM (runTestParallel nEntries) [i..j] `Ex.catch`+             (\(e::Ex.BlockedIndefinitelyOnMVar) ->+                  fail ("main-thread blocked " ++ show e))
Test/Framework/Tutorial.hs view
@@ -36,11 +36,9 @@ This pragma instructs GHC to run the source file through @htfpp@, the custom preprocessor of the HTF. -The following @import@ statements are also needed:+The following @import@ statement is also needed:  @-import System.Environment ( getArgs )-import System.Exit ( exitWith ) import Test.Framework @ @@ -89,7 +87,7 @@ Cabal-Version: >= 1.10 Build-type:    Simple -Executable tutorial+Test-Suite tutorial   Type:              exitcode-stdio-1.0   Main-is:           Tutorial.hs   Build-depends:     base == 4.*, HTF == 0.10.*@@ -370,6 +368,3 @@ -}  ) where--import Test.Framework-import qualified Test.HUnit.Base
Test/Framework/Utils.hs view
@@ -22,6 +22,9 @@ import System.Directory import Data.Char import System.Time hiding (diffClockTimes)+import System.Random+import Data.Array.IO+import Control.Monad  infixr 6 </> @@ -120,7 +123,7 @@ readM s | [x] <- parse = return x         | otherwise    = fail $ "Failed parse: " ++ show s     where-      parse = [x | (x,t) <- reads s]+      parse = [x | (x, []) <- reads s]  ensureNewline :: String -> String ensureNewline s =@@ -149,3 +152,19 @@       -- bring all into microseconds       picoseconds i = i `div` (1000 * 1000)       seconds i = i * 1000000++-- | Randomly shuffle a list+--   /O(N)/+shuffleIO :: [a] -> IO [a]+shuffleIO xs = do+        ar <- newArray n xs+        forM [1..n] $ \i -> do+            j <- randomRIO (i,n)+            vi <- readArray ar i+            vj <- readArray ar j+            writeArray ar j vi+            return vj+  where+    n = length xs+    newArray :: Int -> [a] -> IO (IOArray Int a)+    newArray n xs =  newListArray (1,n) xs
+ Test/Framework/XmlOutput.hs view
@@ -0,0 +1,189 @@+{- |++See <http://pzolee.blogs.balabit.com/2012/11/jenkins-vs-junit-xml-format/>+for a description of the format used.++The source code of this module also contains a rough specification of+the output format in terms of Haskell data types.++-}+{-# LANGUAGE OverloadedStrings #-}+module Test.Framework.XmlOutput (++    mkGlobalResultsXml++) where++import qualified Data.ByteString.Lazy as BSL+import qualified Data.List as List+import qualified Data.Map.Strict as Map+import qualified Data.Text as T+import Text.Printf++import Text.XML.Generator++import Test.Framework.TestTypes+import Test.Framework.Colors++-- A "specification" of the output format in terms of haskell data types+-- * The name of each data type corresponds to the name of an XML element+--   (lowercase first letter)+-- * The name of a field with a primitive corresponds to an attribute with+--   then same name as the field (without the prefix)+data JunitXmlOutput = JunitXmlOutput Testsuites++type Seconds = Double++data Testsuites+    = Testsuites+      { tss_tests :: Int+      , tss_failures :: Int+      , tss_errors :: Int+      , tss_time :: Seconds+      , tss_suites :: [Testsuite] }++data Testsuite+    = Testsuite+      { ts_tests :: Int+      , ts_failures :: Int+      , ts_errors :: Int+      , ts_time :: Seconds+      , ts_id :: Int+      , ts_name :: String+      , ts_package :: String+      , ts_testcases :: [Testcase] }++data Testcase+    = Testcase+      { tc_classname :: String+      , tc_name :: String+      , tc_time :: Seconds+      , tc_result :: Maybe Result }++-- For this datatype, the elemName field specifies the name of the element+data Result+    = Result+      { r_elemName :: String+      , r_message :: T.Text+      , r_type :: String+      , r_textContent :: T.Text }++renderAsXml :: JunitXmlOutput -> BSL.ByteString+renderAsXml (JunitXmlOutput suites) =+    xrender $+    doc defaultDocInfo $+        xelem "testsuites" $+              xattr "tests" (showT (tss_tests suites)) <>+              xattr "failures" (showT (tss_failures suites)) <>+              xattr "errors" (showT (tss_errors suites)) <>+              xattr "time" (showTime (tss_time suites)) <#>+              (map testsuiteXml (tss_suites suites))+    where+      testsuiteXml suite =+          xelem "testsuite" $+                xattr "id" (showT (ts_id suite)) <>+                xattr "tests" (showT (ts_tests suite)) <>+                xattr "failures" (showT (ts_failures suite)) <>+                xattr "errors" (showT (ts_errors suite)) <>+                xattr "time" (showTime (ts_time suite)) <>+                xattr "name" (T.pack (ts_name suite)) <>+                xattr "package" (T.pack (ts_package suite)) <#>+                (map testcaseXml (ts_testcases suite))+      testcaseXml tc =+          xelem "testcase" $+                xattr "classname" (T.pack (tc_classname tc)) <>+                xattr "name" (T.pack (tc_name tc)) <>+                xattr "time" (showTime (tc_time tc)) <#>+                resultXml (tc_result tc)+      resultXml Nothing = xempty+      resultXml (Just res) =+          xelem (T.pack (r_elemName res)) $+                xattr "type" (T.pack (r_type res)) <>+                xattr "message" (r_message res) <#>+                xtext (r_textContent res)+      showT = T.pack . show+      showTime = T.pack . printf "%.3f"++groupByModule :: [FlatTestResult] -> [(String, [FlatTestResult])]+groupByModule l =+    let m = List.foldl' (\m r -> Map.insertWith (++) (prefixName (ft_path r)) [r] m) Map.empty l+    in Map.toList m++mkTestSuite :: (Int, (String, [FlatTestResult])) -> Testsuite+mkTestSuite (id, (modName, results)) =+    Testsuite+    { ts_tests = nTests+    , ts_failures = nFailures+    , ts_errors = nErrors+    , ts_time = millisToSeconds millis+    , ts_id = id+    , ts_name = modName+    , ts_package = modName+    , ts_testcases = map mkTestCase results }+    where+      (nTests, nFailures, nErrors, millis) =+          List.foldl' (\(t, f, e, m) r -> (t + 1, f + failureInc r, e + errorInc r,+                                           m + (rr_wallTimeMs . ft_payload) r))+                      (0, 0, 0, 0) results+      failureInc r = if isFailure r then 1 else 0+      errorInc r = if isError r then 1 else 0++isFailure :: FlatTestResult -> Bool+isFailure r = Fail == (rr_result . ft_payload) r++isError :: FlatTestResult -> Bool+isError r = Error == (rr_result . ft_payload) r++mkTestCase :: FlatTestResult -> Testcase+mkTestCase r =+    Testcase+    { tc_classname = modName+    , tc_name = simpleName+    , tc_time = millisToSeconds (rr_wallTimeMs payload)+    , tc_result = result }+    where+      payload = ft_payload r+      simpleName = prefix ++ finalName (ft_path r)+      modName = prefixName (ft_path r)+      prefix = case ft_sort r of+                 UnitTest -> "test_"+                 QuickCheckTest -> "prop_"+                 BlackBoxTest -> "bbt_"+      result =+          if isFailure r+          then Just (mkResult "failure")+          else if isError r+               then Just (mkResult "error")+               else Nothing+      mkResult elemName =+          Result+          { r_elemName = elemName+          , r_message = T.takeWhile (/= '\n') msg+          , r_type = elemName+          , r_textContent = msg }+      msg = renderColorString (attachCallStack (rr_message payload) (rr_callers payload)) False++millisToSeconds :: Milliseconds -> Seconds+millisToSeconds millis =+    fromInteger (toInteger millis) / 1000.0++mkGlobalResultsXml :: Milliseconds     -- ^ wall time in ms+                   -> [FlatTestResult] -- ^ passed tests+                   -> [FlatTestResult] -- ^ pending tests+                   -> [FlatTestResult] -- ^ failed tests+                   -> [FlatTestResult] -- ^ erroneous tests+                   -> BSL.ByteString+mkGlobalResultsXml t pass pending failed errors =+    let nPassed = length pass+        nPending = length pending+        nFailed = length failed+        nErrors = length errors+        byModules = groupByModule (pass ++ pending ++ failed ++ errors)+        suites = map mkTestSuite (zip [0..] byModules)+        root = Testsuites+               { tss_tests = nPassed + nPending + nFailed + nErrors+               , tss_failures = nFailed+               , tss_errors = nErrors+               , tss_time = millisToSeconds t+               , tss_suites = suites }+    in renderAsXml (JunitXmlOutput root)
run-all-tests-for-all-compilers.sh view
@@ -30,6 +30,10 @@         result="FAIL"     fi     RESULT="$RESULT\nResult for GHC version $ghc_version: $result"+    if [ $ecode -ne 0 ]+    then+        exit 1+    fi done  echo
run-all-tests.sh view
@@ -1,22 +1,14 @@ #!/bin/bash  set -e--cabal configure --enable-tests-cabal build-cabal test-cabal install+ghc-pkg unregister HTF || true -cd tests-mkdir -p dist/build/htfpp-cp ../dist/build/htfpp/htfpp dist/build/htfpp-cabal configure --enable-tests-cabal build-cabal test+cabal configure --enable-tests || exit 1+cabal build || exit 1+cabal test || exit 1+cabal install || exit 1 -cd ../sample-mkdir -p dist/build/htfpp-cp ../dist/build/htfpp/htfpp dist/build/htfpp-cabal configure --enable-tests-cabal build-cabal test+cd sample+cabal configure --enable-tests || exit 1+cabal build || exit 1+cabal test || exit 1
sample/sample-HTF.cabal view
@@ -13,12 +13,12 @@ executable sample   main-is:           Main.hs   other-modules:     MyPkg.A MyPkg.B-  build-depends:     base, HTF == 0.10.*+  build-depends:     base, HTF == 0.11.*   default-language:  Haskell2010  test-suite sample-tests   type:              exitcode-stdio-1.0   main-is:           TestMain.hs   other-modules:     MyPkg.A MyPkg.B-  build-depends:     base, HTF == 0.10.*+  build-depends:     base, HTF == 0.11.*   default-language:  Haskell2010
tests/TestHTF.hs view
@@ -32,6 +32,7 @@ import System.IO import System.IO.Temp import Control.Exception+import Control.Monad import qualified Data.HashMap.Strict as M import qualified Data.Aeson as J import Data.Aeson ( (.=) )@@ -65,7 +66,8 @@  test_assertEqualV = assertEqualVerbose "blub" 1 2 -test_assertEqualNoShow = assertEqualNoShow A B+test_assertEqualNoShow = withOptions (\opts -> opts { to_parallel = False }) $+                         assertEqualNoShow A B  test_assertListsEqualAsSets = assertListsEqualAsSets [1,2] [2] @@ -83,7 +85,7 @@  test_assertThrowsIO2 = assertThrowsIO (fail "ERROR") (handleExc True) -test_someError = error "Bart Simpson!!"+test_someError = error "Bart Simpson!!" :: IO ()  test_pendingTest = unitTestPending "This test is pending" @@ -147,9 +149,9 @@        check jsons (J.object ["type" .= J.String "test-end"                              ,"test" .= J.object ["flatName" .= J.String "Main:diff"]])                    (J.object ["test" .= J.object ["location" .= J.object ["file" .= J.String "TestHTF.hs$"-                                                                         ,"line" .= J.toJSON (101::Int)]]+                                                                         ,"line" .= J.toJSON (103::Int)]]                              ,"location" .= J.object ["file" .= J.String "TestHTF.hs$"-                                                     ,"line" .= J.toJSON (102::Int)]])+                                                     ,"line" .= J.toJSON (104::Int)]])        check jsons (J.object ["type" .= J.String "test-end"                              ,"test" .= J.object ["flatName" .= J.String "Foo.A:a"]])                    (J.object ["test" .= J.object ["location" .= J.object ["file" .= J.String "Foo/A.hs$"@@ -160,10 +162,10 @@                              ,"test" .= J.object ["flatName" .= J.String "Main:subAssert"]])                    (J.object ["callers" .= J.toJSON [J.object ["message" .= J.Null                                                               ,"location" .= J.object ["file" .= J.String "TestHTF.hs$"-                                                                                      ,"line" .= J.toJSON (90::Int)]]+                                                                                      ,"line" .= J.toJSON (92::Int)]]                                                     ,J.object ["message" .= J.String "I'm another sub"                                                               ,"location" .= J.object ["file" .= J.String "TestHTF.hs$"-                                                                                      ,"line" .= J.toJSON (92::Int)]]]])+                                                                                      ,"line" .= J.toJSON (94::Int)]]]])     where       check jsons pred assert =           case filter (\j -> matches j pred) jsons of@@ -210,8 +212,12 @@        bbts <- blackBoxTests (dirPrefix </> "bbt") (dirPrefix </> "./run-bbt.sh") ".x"                  (defaultBBTArgs { bbtArgs_verbose = False })        let tests = [addToTestSuite htf_thisModulesTests bbts] ++ htf_importedTests+       when ("--help" `elem` args || "-h" `elem` args) $+            do hPutStrLn stderr ("USGAGE: dist/build/test/test [--direct]")+               ecode <- runTestWithArgs ["--help"] ([] :: [Test])+               exitWith ecode        case args of-         "--interactive":rest ->+         "--direct":rest ->              do ecode <- runTestWithArgs rest tests                 case ecode of                   ExitFailure _ -> return ()@@ -219,7 +225,8 @@          _ ->              do withSystemTempFile "HTF-out" $ \outFile h ->                   do hClose h-                     ecode <- runTestWithArgs ["--json", "--output-file=" ++ outFile] tests+                     ecode <- runTestWithArgs ["-j4", "--deterministic",+                                               "--json", "--output-file=" ++ outFile] tests                      case ecode of                        ExitFailure _ -> checkOutput outFile                        _ -> fail ("unexpected exit code: " ++ show ecode)
+ tests/ThreadPoolTest.hs view
@@ -0,0 +1,39 @@+--+-- Copyright (c) 2013   Stefan Wehr - http://www.stefanwehr.de+--+-- This program is free software; you can redistribute it and/or+-- modify it under the terms of the GNU General Public License as+-- published by the Free Software Foundation; either version 2 of+-- the License, or (at your option) any later version.+--+-- This program is distributed in the hope that it will be useful,+-- but WITHOUT ANY WARRANTY; without even the implied warranty of+-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU+-- General Public License for more details.+--+-- You should have received a copy of the GNU General Public License+-- along with this program; if not, write to the Free Software+-- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA+-- 02111-1307, USA.+--++import Test.Framework.ThreadPool+import System.Environment+import System.Exit+import Control.Monad++main :: IO ()+main =+    do args <- getArgs+       when ("-h" `elem` args || "--help" `elem` args) usage+       (i, nEntries) <- case args of+                          [] -> return (200, 100)+                          [x] -> return (read x, 100)+                          [x, y] -> return (read x, read y)+                          _ -> usage+       threadPoolTest (1, i) nEntries+       return ()+    where+      usage =+          do putStrLn "USAGE: ThreadPoolTest [N_THREADS [N_ENTRIES]]"+             exitWith (ExitFailure 1)
tests/Tutorial.hs view
@@ -1,4 +1,4 @@-{-# OPTIONS_GHC -F -pgmF htfpp #-}+{-# OPTIONS_GHC -F -pgmF ./dist/build/htfpp/htfpp #-} import System.Environment ( getArgs ) import System.Exit ( exitWith ) import Test.Framework
tests/compile-errors/run-tests.sh view
@@ -2,7 +2,7 @@  cd "$(dirname $0)" -FLAGS="-hide-all-packages -package HTF -package base --make"+FLAGS="-hide-all-packages -package base -package-conf ../../dist/package.conf.inplace -package HTF --make" lineno=7  function check()
tests/test-HTF.cabal view
@@ -6,7 +6,6 @@ Executable test   Main-is: TestHTF.hs   Build-depends:     base >= 4,-                     HTF,                      bytestring >= 0.9,                      aeson >= 0.6,                      unordered-containers >= 0.2,@@ -17,6 +16,8 @@                      process >= 1.0,                      regex-compat >= 0.92   Default-language:  Haskell2010+  Ghc-options: -threaded+  Hs-source-dirs: ., ..  Test-Suite tutorial   Type:              exitcode-stdio-1.0