packages feed

doctest-parallel 0.2 → 0.2.1

raw patch · 12 files changed

+260/−53 lines, 12 filesdep +randomPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: random

API changes (from Hackage documentation)

- Test.DocTest: main :: Library -> Config -> IO Summary
+ Test.DocTest: getSeed :: Bool -> Bool -> Maybe Int -> IO (Maybe Int)
+ Test.DocTest: mainFromCabalWithConfig :: String -> Config -> IO ()
+ Test.DocTest: mainFromLibraryWithConfig :: Library -> Config -> IO ()
+ Test.DocTest: run :: Library -> Config -> IO Summary
+ Test.DocTest.Helpers: [libCSourceDirectories] :: Library -> [FilePath]
+ Test.DocTest.Internal.Options: [cfgQuiet] :: Config -> Bool
+ Test.DocTest.Internal.Options: [cfgRandomizeOrder] :: Config -> Bool
+ Test.DocTest.Internal.Options: [cfgSeed] :: Config -> Maybe Int
+ Test.DocTest.Internal.Options: parseSeed :: String -> Maybe Int
+ Test.DocTest.Internal.Options: parseSpecificFlag :: String -> String -> Maybe String
+ Test.DocTest.Internal.Runner: [reportStateQuiet] :: ReportState -> Bool
+ Test.DocTest.Internal.Runner: shuffle :: Int -> [a] -> [a]
- Test.DocTest.Helpers: Library :: [FilePath] -> [ModuleName] -> [Extension] -> Library
+ Test.DocTest.Helpers: Library :: [FilePath] -> [FilePath] -> [ModuleName] -> [Extension] -> Library
- Test.DocTest.Internal.Options: Config :: Bool -> Bool -> [ModuleName] -> Maybe Int -> Config
+ Test.DocTest.Internal.Options: Config :: Bool -> Bool -> [ModuleName] -> Maybe Int -> Bool -> Maybe Int -> Bool -> Config
- Test.DocTest.Internal.Runner: ReportState :: Int -> Bool -> Bool -> Summary -> ReportState
+ Test.DocTest.Internal.Runner: ReportState :: Int -> Bool -> Bool -> Bool -> Summary -> ReportState
- Test.DocTest.Internal.Runner: runModule :: Bool -> Bool -> [String] -> Chan ReportUpdate -> Module [Located DocTest] -> IO ()
+ Test.DocTest.Internal.Runner: runModule :: Bool -> Maybe Int -> Bool -> [String] -> Chan ReportUpdate -> Module [Located DocTest] -> IO ()
- Test.DocTest.Internal.Runner: runModules :: Maybe Int -> Bool -> Bool -> Bool -> [String] -> [Module [Located DocTest]] -> IO Summary
+ Test.DocTest.Internal.Runner: runModules :: Maybe Int -> Bool -> Bool -> Maybe Int -> Bool -> [String] -> Bool -> [Module [Located DocTest]] -> IO Summary

Files

CHANGES.markdown view
@@ -1,8 +1,15 @@+# 0.2.1+  * C include directories (Cabal field: `include-dirs`) are now passed to GHC when parsing source files ([#7](https://github.com/martijnbastiaan/doctest-parallel/issues/7))+  * A migration guide has been added ([#11](https://github.com/martijnbastiaan/doctest-parallel/issues/11))+  * Test order can be randomized using `--randomize-order`. Test order can be made deterministic by adding an optional `--seed=N` argument ([#12](https://github.com/martijnbastiaan/doctest-parallel/pull/12))+  * Any non-error output can now be surpressed by `--quiet` ([#20](https://github.com/martijnbastiaan/doctest-parallel/pull/20))+  * Doctest can now be called using a record for option passing in addition to command line arguments. See `mainFromCabalWithConfig` and `mainFromLibraryWithConfig`.+ # 0.2 Changes:   * Support for GHC 9.2 has been added ([#4](https://github.com/martijnbastiaan/doctest-parallel/pull/4))   * Support for GHC 8.2 has been dropped ([#3](https://github.com/martijnbastiaan/doctest-parallel/pull/3))-  * The dependency `cabal-install-parsers` has been dropped. This trims the dependency tree quite a bit [#3](https://github.com/martijnbastiaan/doctest-parallel/pull/3)+  * The dependency `cabal-install-parsers` has been dropped. This trims the dependency tree quite a bit ([#3](https://github.com/martijnbastiaan/doctest-parallel/pull/3))   * The Hackage distribution now ships all files necessary to run `doctest-parallel`'s tests (Fixes [#1](https://github.com/martijnbastiaan/doctest-parallel/issues/1), PR [#2](https://github.com/martijnbastiaan/doctest-parallel/pull/2))  # 0.1
README.markdown view
@@ -6,6 +6,9 @@ # Installation `doctest-parallel` is available from [Hackage](https://hackage.haskell.org/package/doctest-parallel). It cannot be used as a standalone binary, rather, it expects to be integrated in a Cabal/Stack project. See [examples/](example/README.md) for more information on how to integrate `doctest-parallel` into your project. +# Migrating from `doctest`+See [issue #11](https://github.com/martijnbastiaan/doctest-parallel/issues/11) for more information.+ # Usage Below is a small Haskell module. The module contains a Haddock comment with some examples of interaction. The examples demonstrate how the module is supposed to be used. @@ -329,7 +332,8 @@  * It would be lovely if we could get rid of the needs for `write-ghc-environment-files: always` option for Cabal. To properly do this, I think Cabal should do two things:     1. Deprecate GHC environment files as a way to _implicitly_ setup environments. Instead, environment files should be written to the `dist-newstyle` directory and activated using some subcommand, e.g. `cabal shell`. This avoids the many problems GHC environment files have, while retaining their functionality for people who like them.     2. Any subcommands should be run with `GHC_ENVIRONMENT` set - pointing to the GHC environment file. Like Stack, this would create a hassle free way of using Cabal in combination with projects/executables that use the GHC API (e.g., `clash-ghc`, `doctest-parallel`).- * Cabal needs to expose more information _by default_ in order for `doctest-parallel` to properly work. Specifically, it needs to know the exact `default-extensions`, `ghc-options`, and `CPP` flags the project is compiled with. These options are obtainable by using a custom `Setup.hs`, but this has its own list of problems.+ * It would be nice if Cabal would expose more information _by default_ (probably through auto-generated modules) in order for `doctest-parallel` to properly work. Specifically, it needs to know the exact `default-extensions`, `ghc-options`, and `CPP` flags the project is compiled with. These options are obtainable by using a custom `Setup.hs`, but this has its own list of problems.+   * Alternatively, if comments could be included in and loaded from `.hi` files that'd solve all issues too.  * Hopefully many of the improvements made here can make their way back into `sol/doctest`.   Of course, if you wish to add a feature that's not in this list, please feel free top open a pull request!
doctest-parallel.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.0  name:           doctest-parallel-version:        0.2+version:        0.2.1 synopsis:       Test interactive Haskell examples description:    The doctest program checks examples in source code comments.  It is modeled                 after doctest for Python (<https://docs.python.org/3/library/doctest.html>).@@ -54,6 +54,7 @@     test/parse/setup-empty/*.hs     test/parse/setup-only/*.hs     test/parse/simple/*.hs+    test/integration/WithCInclude/include/WithCInclude.h  source-repository head   type: git@@ -97,6 +98,7 @@     , ghc-paths >=0.1.0.9     , pretty     , process+    , random >= 1.2     , syb >=0.3     , transformers     , unordered-containers@@ -118,6 +120,8 @@   -- ghc-options: -Wall   hs-source-dirs:       test/integration+  include-dirs:+      test/integration/WithCInclude/include   c-sources:       test/integration/WithCbits/foo.c   exposed-modules:@@ -157,6 +161,7 @@     TestSimple.Fib     TrailingWhitespace.Foo     WithCbits.Bar+    WithCInclude.Bar  test-suite spectests   main-is: Spec.hs@@ -185,6 +190,7 @@     , base >=4.5 && <5     , base-compat >=0.7.0     , code-page >=0.1+    , containers     , doctest-parallel     , deepseq     , directory
src/Test/DocTest.hs view
@@ -1,11 +1,21 @@ {-# LANGUAGE CPP #-}-{-# LANGUAGE LambdaCase #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE ViewPatterns #-} -module Test.DocTest where+module Test.DocTest+  ( mainFromCabal+  , mainFromLibrary+  , mainFromCabalWithConfig+  , mainFromLibraryWithConfig +  -- * Internal+  , filterModules+  , isSuccess+  , getSeed+  , run+  ) where+ import           Prelude () import           Prelude.Compat @@ -14,6 +24,7 @@ import           Control.Monad (unless) import           System.Exit (exitFailure) import           System.IO+import           System.Random (randomIO)  import qualified Control.Exception as E @@ -40,13 +51,29 @@ -- -- Example: --+-- @ -- mainFromCabal "my-project" =<< getArgs+-- @ -- mainFromCabal :: String -> [String] -> IO () mainFromCabal libName cmdArgs = do   lib <- extractCabalLibrary =<< findCabalPackage libName   mainFromLibrary lib cmdArgs +-- | Run doctest given config.+--+-- Example:+--+-- @+-- mainFromCabal "my-project" defaultConfig+-- @+--+mainFromCabalWithConfig :: String -> Config -> IO ()+mainFromCabalWithConfig libName config = do+  lib <- extractCabalLibrary =<< findCabalPackage libName+  mainFromLibraryWithConfig lib config++-- | Like 'mainFromCabal', but with a given library. mainFromLibrary :: Library -> [String] -> IO () mainFromLibrary lib (parseOptions -> opts) =   case opts of@@ -56,15 +83,20 @@        hPutStrLn stderr "Try `doctest --help' for more information."        exitFailure     Result config -> do-      r <- main lib config `E.catch` \e -> do-        case fromException e of-          Just (UsageError err) -> do-            hPutStrLn stderr ("doctest: " ++ err)-            hPutStrLn stderr "Try `doctest --help' for more information."-            exitFailure-          _ -> E.throwIO e-      unless (isSuccess r) exitFailure+      mainFromLibraryWithConfig lib config +-- | Run doctests with given library and config.+mainFromLibraryWithConfig :: Library -> Config -> IO ()+mainFromLibraryWithConfig lib config = do+  r <- run lib config `E.catch` \e -> do+    case fromException e of+      Just (UsageError err) -> do+        hPutStrLn stderr ("doctest: " ++ err)+        hPutStrLn stderr "Try `doctest --help' for more information."+        exitFailure+      _ -> E.throwIO e+  unless (isSuccess r) exitFailure+ isSuccess :: Summary -> Bool isSuccess s = sErrors s == 0 && sFailures s == 0 @@ -83,15 +115,36 @@   nonExistingMods = Set.toList (wantedMods1 `Set.difference` allMods1)   isSpecifiedMod Module{moduleName} = moduleName `Set.member` wantedMods1 +getSeed+  :: Bool+  -- ^ Whether quiet mode is enabled+  -> Bool+  -- ^ Enable order randomization. If 'False', this function always returns 'Nothing'+  -> Maybe Int+  -- ^ User supplied seed. If 'Nothing', a fresh seed will be generated.+  -> IO (Maybe Int)+  -- ^ Maybe seed to use for order randomization.+getSeed _quiet False _ = pure Nothing+getSeed _quiet True (Just seed) = pure (Just seed)+getSeed quiet True Nothing = do+  -- Using an abslute number to prevent copy+paste errors+  seed <- abs <$> randomIO+  unless quiet $+    putStrLn ("Using freshly generated seed to randomize test order: " <> show seed)+  pure (Just seed) -main :: Library -> Config -> IO Summary-main lib Config{..} = do+-- | Run doctest for given library and config. Produce a summary of all tests.+run :: Library -> Config -> IO Summary+run lib Config{..} = do   let     implicitPrelude = DisableExtension ImplicitPrelude `notElem` libDefaultExtensions lib     (includeArgs, moduleArgs, otherGhciArgs) = libraryToGhciArgs lib     evalGhciArgs = otherGhciArgs ++ ["-XNoImplicitPrelude"] +  seed <- getSeed cfgQuiet cfgRandomizeOrder cfgSeed+   -- get examples from Haddock comments   allModules <- getDocTests (includeArgs ++ moduleArgs ++ otherGhciArgs)-  let modules = filterModules cfgModules allModules-  runModules cfgThreads cfgPreserveIt cfgVerbose implicitPrelude evalGhciArgs modules+  runModules+    cfgThreads cfgPreserveIt cfgVerbose seed implicitPrelude evalGhciArgs+    cfgQuiet (filterModules cfgModules allModules)
src/Test/DocTest/Helpers.hs view
@@ -1,6 +1,5 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE MultiWayIf #-}-{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE LambdaCase #-} @@ -25,7 +24,7 @@ import Distribution.PackageDescription   ( CondTree(CondNode, condTreeData), GenericPackageDescription (condLibrary)   , exposedModules, libBuildInfo, hsSourceDirs, defaultExtensions, package-  , packageDescription, condSubLibraries )+  , packageDescription, condSubLibraries, includeDirs ) import Distribution.Pretty (prettyShow) import Distribution.Verbosity (silent) @@ -38,16 +37,22 @@  data Library = Library   { libSourceDirectories :: [FilePath]+    -- ^ Haskell source directories+  , libCSourceDirectories :: [FilePath]+    -- ^ C source directories   , libModules :: [ModuleName]+    -- ^ Exposed modules   , libDefaultExtensions :: [Extension]+    -- ^ Extensions enabled by default   }   deriving (Show)  -- | Convert a "Library" to arguments suitable to be passed to GHCi. libraryToGhciArgs :: Library -> ([String], [String], [String])-libraryToGhciArgs Library{..} = (srcArgs, modArgs, extArgs)+libraryToGhciArgs Library{..} = (hsSrcArgs <> cSrcArgs, modArgs, extArgs)  where-  srcArgs = map ("-i" <>) libSourceDirectories+  hsSrcArgs = map ("-i" <>) libSourceDirectories+  cSrcArgs = map ("-I" <>) libCSourceDirectories   modArgs = map prettyShow libModules   extArgs = map showExt libDefaultExtensions @@ -144,10 +149,12 @@     let       buildInfo = libBuildInfo lib       sourceDirs = hsSourceDirs buildInfo+      cSourceDirs = includeDirs buildInfo       root = takeDirectory pkgPath     in       pure Library         { libSourceDirectories = map ((root </>) . compatPrettyShow) sourceDirs+        , libCSourceDirectories = map (root </>) cSourceDirs         , libModules = exposedModules lib         , libDefaultExtensions = defaultExtensions buildInfo         }
src/Test/DocTest/Internal/Options.hs view
@@ -24,15 +24,18 @@ usage :: String usage = unlines [     "Usage:"-  , "  doctest [ --fast | --preserve-it | --verbose | -jN ]..."+  , "  doctest [ options ]... [<module>]..."   , "  doctest --help"   , "  doctest --version"   , "  doctest --info"   , ""   , "Options:"   , "  -jN                      number of threads to use"+  , "  --randomize-order        randomize order in which tests are run"+  , "  --seed                   use a specific seed to randomize test order"   , "  --preserve-it            preserve the `it` variable between examples"   , "  --verbose                print each test as it is run"+  , "  --quiet                  only print errors"   , "  --help                   display this help and exit"   , "  --version                output version information and exit"   , "  --info                   output machine-readable version information and exit"@@ -73,10 +76,18 @@   , cfgVerbose :: Bool   -- ^ Verbose output (default: @False@)   , cfgModules :: [ModuleName]-  -- ^ Module names to test+  -- ^ Module names to test. An empty list means "test all modules".   , cfgThreads :: Maybe Int   -- ^ Number of threads to use. Defaults to autodetection based on the number   -- of cores.+  , cfgRandomizeOrder :: Bool+  -- ^ Randomize the order in which test cases in a module are run (default: @False@)+  , cfgSeed :: Maybe Int+  -- ^ Initialize random number generator used to randomize test cases when+  -- 'cfgRandomizeOrder' is set. If set to 'Nothing', a random seed is picked+  -- from a system RNG source on startup.+  , cfgQuiet :: Bool+  -- ^ Only print error messages, no status or progress messages (default: @False@)   } deriving (Show, Eq)  defaultConfig :: Config@@ -85,6 +96,9 @@   , cfgVerbose = False   , cfgModules = []   , cfgThreads = Nothing+  , cfgRandomizeOrder = False+  , cfgSeed = Nothing+  , cfgQuiet = False   }  parseOptions :: [String] -> Result Config@@ -96,17 +110,57 @@       "--help" -> ResultStdout usage       "--info" -> ResultStdout info       "--version" -> ResultStdout versionInfo+      "--randomize-order" -> go config{cfgRandomizeOrder=True} args       "--preserve-it" -> go config{cfgPreserveIt=True} args       "--verbose" -> go config{cfgVerbose=True} args-      ('-':'j':n0) | Just n1 <- parseThreads n0 -> go config{cfgThreads=Just n1} args+      "--quiet" -> go config{cfgQuiet=True} args+      ('-':_) | Just n <- parseSeed arg -> go config{cfgSeed=Just n} args+      ('-':_) | Just n <- parseThreads arg -> go config{cfgThreads=Just n} args       ('-':_) -> ResultStderr ("Unknown command line argument: " <> arg)       mod_ -> go config{cfgModules=mod_ : cfgModules config} args +-- | Parse seed argument+--+-- >>> parseSeed "--seed=6"+-- Just 6+-- >>> parseSeed "--seeeed=6"+-- Nothing+--+parseSeed :: String -> Maybe Int+parseSeed arg = readMaybe =<< parseSpecificFlag arg "seed"+++-- | Parse number of threads argument+--+-- >>> parseThreads "-j6"+-- Just 6+-- >>> parseThreads "-j-2"+-- Nothing+-- >>> parseThreads "-jA"+-- Nothing+-- parseThreads :: String -> Maybe Int-parseThreads n0 = do+parseThreads ('-':'j':n0) = do   n1 <- readMaybe n0   if n1 > 0 then Just n1 else Nothing+parseThreads _ = Nothing +-- | Parse a specific flag with a value, or return 'Nothing'+--+-- >>> parseSpecificFlag "--foo" "foo"+-- Nothing+-- >>> parseSpecificFlag "--foo=" "foo"+-- Nothing+-- >>> parseSpecificFlag "--foo=5" "foo"+-- Just "5"+-- >>> parseSpecificFlag "--foo=5" "bar"+-- Nothing+parseSpecificFlag :: String -> String -> Maybe String+parseSpecificFlag arg flag = do+  case parseFlag arg of+    ('-':'-':f, value) | f == flag -> value+    _ -> Nothing+ -- | Parse a flag into its flag and argument component. -- -- Example:@@ -121,5 +175,5 @@ parseFlag arg =   case break (== '=') arg of     (flag, ['=']) -> (flag, Nothing)-    (flag, ('=':opt)) -> (flag, Just opt)+    (flag, '=':opt) -> (flag, Just opt)     (flag, _) -> (flag, Nothing)
src/Test/DocTest/Internal/Runner.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE MultiWayIf #-}+{-# LANGUAGE TypeApplications #-}+{-# LANGUAGE NamedFieldPuns #-}  module Test.DocTest.Internal.Runner where @@ -8,11 +10,14 @@ import           Control.Concurrent (Chan, writeChan, readChan, newChan, forkIO) import           Control.Exception (SomeException, catch) import           Control.Monad hiding (forM_)-import           Data.Maybe (fromMaybe)-import           Text.Printf (printf)-import           System.IO (hPutStrLn, hPutStr, stderr, hIsTerminalDevice) import           Data.Foldable (forM_)+import           Data.Function (on)+import           Data.List (sortBy)+import           Data.Maybe (fromMaybe) import           GHC.Conc (numCapabilities)+import           System.IO (hPutStrLn, hPutStr, stderr, hIsTerminalDevice)+import           System.Random (randoms, mkStdGen)+import           Text.Printf (printf)  import           Control.Monad.Trans.State import           Control.Monad.IO.Class@@ -70,35 +75,46 @@   -- ^ Preserve it   -> Bool   -- ^ Verbose+  -> Maybe Int+  -- ^ If 'Just', use seed to randomize test order   -> Bool   -- ^ Implicit Prelude   -> [String]   -- ^ Arguments passed to the GHCi process.+  -> Bool+  -- ^ Quiet mode activated   -> [Module [Located DocTest]]   -- ^ Modules under test   -> IO Summary-runModules nThreads preserveIt verbose implicitPrelude args modules = do+runModules nThreads preserveIt verbose seed implicitPrelude args quiet modules = do   isInteractive <- hIsTerminalDevice stderr    -- Start a thread pool. It sends status updates to this thread through 'output'.   (input, output) <-     makeThreadPool       (fromMaybe numCapabilities nThreads)-      (runModule preserveIt implicitPrelude args)+      (runModule preserveIt seed implicitPrelude args)    -- Send instructions to threads   liftIO (mapM_ (writeChan input) modules)    let     nExamples = (sum . map count) modules-    initState = ReportState 0 isInteractive verbose mempty {sExamples = nExamples}+    initState = ReportState+      { reportStateCount = 0+      , reportStateInteractive = isInteractive+      , reportStateVerbose = verbose+      , reportStateQuiet = quiet+      , reportStateSummary = mempty{sExamples=nExamples}+      } -  ReportState _ _ _ s <- (`execStateT` initState) $ do+  ReportState{reportStateSummary} <- (`execStateT` initState) $ do     consumeUpdates output (length modules)-    verboseReport "# Final summary:"-    gets (show . reportStateSummary) >>= report+    unless quiet $ do+      verboseReport "# Final summary:"+      gets (show . reportStateSummary) >>= report -  return s+  return reportStateSummary  where   consumeUpdates _output 0 = pure ()   consumeUpdates output modsLeft = do@@ -125,6 +141,7 @@   reportStateCount        :: Int     -- ^ characters on the current line , reportStateInteractive  :: Bool    -- ^ should intermediate results be printed? , reportStateVerbose      :: Bool+, reportStateQuiet        :: Bool , reportStateSummary      :: Summary -- ^ test summary } @@ -156,15 +173,27 @@           | otherwise = msg   liftIO (hPutStr stderr str) +-- | Shuffle a list given a seed for an RNG+shuffle :: Int -> [a] -> [a]+shuffle seed xs =+    map snd+  $ sortBy (compare `on` fst)+  $ zip (randoms @Int (mkStdGen seed)) xs+ -- | Run all examples from given module. runModule   :: Bool+  -> Maybe Int   -> Bool   -> [String]   -> Chan ReportUpdate   -> Module [Located DocTest]   -> IO ()-runModule preserveIt implicitPrelude ghciArgs output (Module module_ setup examples) = do+runModule preserveIt (Just seed) implicitPrelude ghciArgs output (Module module_ setup examples) = do+  runModule+    preserveIt Nothing implicitPrelude ghciArgs output+    (Module module_ setup (shuffle seed examples))+runModule preserveIt Nothing implicitPrelude ghciArgs output (Module module_ setup examples) = do   Interpreter.withInterpreter ghciArgs $ \repl -> withCP65001 $ do     -- Try to import this module, if it fails, something is off     importResult <- Interpreter.safeEval repl importModule@@ -242,7 +271,10 @@  reportStart :: Location -> Expression -> String -> Report () reportStart loc expression testType = do-  verboseReport (printf "### Started execution at %s.\n### %s:\n%s" (show loc) testType expression)+  quiet <- gets reportStateQuiet+  unless quiet $+    verboseReport+      (printf "### Started execution at %s.\n### %s:\n%s" (show loc) testType expression)  reportFailure :: FromSetup -> Location -> Expression -> [String] -> Report () reportFailure fromSetup loc expression err = do@@ -275,33 +307,46 @@   report "   * Run with '--write-ghc-environment-files=always'"   report "   * Add 'write-ghc-environment-files: always' to your cabal.project"   report ""-  report " 3. The testsuite executable does not have a dependency on your project library. Please add it to the 'build-depends' section of the testsuite executable."+  report " 3. For Cabal users: Cabal did not generate a GHC environment file in time. This"+  report "    can happen if you use 'cabal test' instead of 'cabal run doctests'. See"+  report "    https://github.com/martijnbastiaan/doctest-parallel/issues/22."   report ""-  report "See the example project at https://github.com/martijnbastiaan/doctest-parallel/tree/master/examples for more information."+  report " 4. The testsuite executable does not have a dependency on your project library. Please"+  report "    add it to the 'build-depends' section of the testsuite executable."+  report ""+  report "See the example project at https://github.com/martijnbastiaan/doctest-parallel/blob/main/example/README.md for more information."   updateSummary FromSetup emptySummary{sErrors=1}  reportSuccess :: FromSetup -> Location -> Report () reportSuccess fromSetup loc = do-  verboseReport (printf "### Successful `%s'!\n" (show loc))+  quiet <- gets reportStateQuiet+  unless quiet $+    verboseReport (printf "### Successful `%s'!\n" (show loc))   updateSummary fromSetup (Summary 0 1 0 0)  verboseReport :: String -> Report () verboseReport xs = do   verbose <- gets reportStateVerbose-  when verbose $ report xs+  quiet <- gets reportStateQuiet+  unless quiet $+    when verbose $+      report xs  updateSummary :: FromSetup -> Summary -> Report () updateSummary FromSetup summary =   -- Suppress counts, except for errors   updateSummary NotFromSetup summary{sExamples=0, sTried=0, sFailures=0} updateSummary NotFromSetup summary = do-  ReportState n f v s <- get-  put (ReportState n f v $ s `mappend` summary)+  ReportState n f v q s <- get+  put (ReportState n f v q $ s `mappend` summary)  reportProgress :: Report () reportProgress = do   verbose <- gets reportStateVerbose-  when (not verbose) $ gets (show . reportStateSummary) >>= report_+  quiet <- gets reportStateQuiet+  unless quiet $+    unless verbose $+      gets (show . reportStateSummary) >>= report_  -- | Run given test group. --
test/MainSpec.hs view
@@ -7,10 +7,12 @@ import           Test.Hspec import           Test.HUnit (assertEqual, Assertion) +import qualified Data.Map as Map import qualified Test.DocTest as DocTest import           Test.DocTest.Helpers (extractSpecificCabalLibrary, findCabalPackage) import           Test.DocTest.Internal.Options import           Test.DocTest.Internal.Runner+import           System.Environment (getEnvironment) import           System.IO.Silently import           System.IO @@ -24,7 +26,7 @@   lib <- extractSpecificCabalLibrary (Just "spectests-modules") pkg   actual <-     hSilence [stderr] $-      DocTest.main lib config{cfgModules=modNames}+      DocTest.run lib config{cfgModules=modNames}   assertEqual (show modNames) expected actual  cases :: Int -> Summary@@ -35,7 +37,17 @@  spec :: Spec spec = do-  describe "doctest" $ do+  env <- runIO getEnvironment+  let+    cDescribe =+      -- Don't run doctests as part of the Stack testsuite yet, pending+      -- https://github.com/commercialhaskell/stack/issues/5662+      if "STACK_EXE" `Map.member` Map.fromList env then+        xdescribe+      else+        describe++  cDescribe "doctest" $ do     it "testSimple" $       doctest ["TestSimple.Fib"]         (cases 1)@@ -109,7 +121,7 @@       doctest ["TrailingWhitespace.Foo"]         (cases 1) -  describe "doctest as a runner for QuickCheck properties" $ do+  cDescribe "doctest as a runner for QuickCheck properties" $ do     it "runs a boolean property" $ do       doctest ["PropertyBool.Foo"]         (cases 1)@@ -134,12 +146,12 @@       doctest ["PropertySetup.Foo"]         (cases 1) -  describe "doctest (module isolation)" $ do+  cDescribe "doctest (module isolation)" $ do     it "should fail due to module isolation" $ do       doctestWithOpts defaultConfig ["ModuleIsolation.TestA", "ModuleIsolation.TestB"]         (cases 2) {sFailures = 1} -  describe "doctest (regression tests)" $ do+  cDescribe "doctest (regression tests)" $ do     it "bugfixOutputToStdErr" $ do       doctest ["BugfixOutputToStdErr.Fib"]         (cases 2)@@ -160,3 +172,7 @@     it "doesn't get confused by doctests using System.IO imports" $ do       doctest ["SystemIoImported.A"]         (cases 2)++    it "correctly handles C import directories" $ do+      doctest ["WithCInclude.Bar"]+        (cases 1)
test/RunnerSpec.hs view
@@ -12,11 +12,11 @@ main = hspec spec  capture :: Report a -> IO String-capture = fmap fst . hCapture [stderr] . (`execStateT` ReportState 0 True False mempty)+capture = fmap fst . hCapture [stderr] . (`execStateT` ReportState 0 True False False mempty)  -- like capture, but with interactivity set to False capture_ :: Report a -> IO String-capture_ = fmap fst . hCapture [stderr] . (`execStateT` ReportState 0 False False mempty)+capture_ = fmap fst . hCapture [stderr] . (`execStateT` ReportState 0 False False False mempty)  spec :: Spec spec = do
test/doctests.hs view
@@ -4,4 +4,6 @@ import System.Environment (getArgs)  main :: IO ()-main = mainFromCabal "doctest-parallel" =<< getArgs+main = do+  args <- getArgs+  mainFromCabal "doctest-parallel" ("--randomize-order":args)
+ test/integration/WithCInclude/Bar.hs view
@@ -0,0 +1,12 @@+{-# LANGUAGE CPP #-}++module WithCInclude.Bar where++#include "WithCInclude.h"+++-- |+-- >>> x+-- 42+x :: Int+x = THE_DEFINE
+ test/integration/WithCInclude/include/WithCInclude.h view
@@ -0,0 +1,1 @@+#define THE_DEFINE 42