packages feed

doctest 0.22.9 → 0.22.10

raw patch · 11 files changed

+117/−131 lines, 11 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

+ Test.DocTest.Internal.Run: formatSummary :: Summary -> String
- Test.DocTest.Internal.Run: Summary :: Int -> Int -> Int -> Int -> Summary
+ Test.DocTest.Internal.Run: Summary :: !Int -> !Int -> !Int -> !Int -> Summary
- Test.DocTest.Internal.Run: [sErrors] :: Summary -> Int
+ Test.DocTest.Internal.Run: [sErrors] :: Summary -> !Int
- Test.DocTest.Internal.Run: [sExamples] :: Summary -> Int
+ Test.DocTest.Internal.Run: [sExamples] :: Summary -> !Int
- Test.DocTest.Internal.Run: [sFailures] :: Summary -> Int
+ Test.DocTest.Internal.Run: [sFailures] :: Summary -> !Int
- Test.DocTest.Internal.Run: [sTried] :: Summary -> Int
+ Test.DocTest.Internal.Run: [sTried] :: Summary -> !Int

Files

CHANGES.markdown view
@@ -1,3 +1,6 @@+Changes in 0.22.10+  - Make progress reporting more robust+ Changes in 0.22.9   - Use `-fprint-error-index-links=never` for GHC `>=9.10` 
doctest.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           doctest-version:        0.22.9+version:        0.22.10 synopsis:       Test interactive Haskell examples description:    `doctest` is a tool that checks [examples](https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810775744)                 and [properties](https://www.haskell.org/haddock/doc/html/ch03s08.html#idm140354810771856)
src/Extract.hs view
@@ -2,7 +2,6 @@ module Extract (Module(..), extract) where  import           Imports hiding (mod, concat)-import           Control.Exception import           Data.List (partition, isSuffixOf)  import           Control.DeepSeq (deepseq, NFData(rnf))
src/Imports.hs view
@@ -1,15 +1,26 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} module Imports (module Imports) where  import           Prelude as Imports import           Data.Monoid as Imports import           Data.Maybe as Imports-import           Control.Monad as Imports+import           Control.Monad as Imports hiding (forM_)+import           Control.Exception as Imports+import           Data.Foldable as Imports (forM_) import           Control.Arrow as Imports  import           Data.Char import           System.Exit import           System.Process++#if __GLASGOW_HASKELL__ >= 804+import           Data.Functor as Imports ((<&>))+#else+infixl 1 <&>+(<&>) :: Functor f => f a -> (a -> b) -> f b+(<&>) = flip fmap+#endif  pass :: Monad m => m () pass = return ()
src/Interpreter.hs view
@@ -17,7 +17,6 @@  import           System.Process import           System.Directory (getPermissions, executable)-import           Control.Exception hiding (handle) import           GHC.Paths (ghc)  import           Language.Haskell.GhciWrapper
src/Language/Haskell/GhciWrapper.hs view
@@ -14,7 +14,6 @@ import           System.IO hiding (stdin, stdout, stderr) import           System.Process import           System.Exit-import           Control.Exception import           Data.List (isSuffixOf)  data Config = Config {
src/Run.hs view
@@ -9,6 +9,7 @@  , Result , Summary(..)+, formatSummary , isSuccess , evaluateResult , doctestWithResult@@ -154,4 +155,6 @@ runDocTests :: Config -> [Module [Located DocTest]] -> IO Result runDocTests Config{..} modules = do   Interpreter.withInterpreter ((<> ghcOptions) <$> repl) $ \ interpreter -> withCP65001 $ do-    runModules fastMode preserveIt verbose interpreter modules+    let+      v = if verbose then Verbose else NonVerbose+    runModules fastMode preserveIt v interpreter modules
src/Runner.hs view
@@ -1,25 +1,29 @@ {-# LANGUAGE CPP #-}+{-# LANGUAGE LambdaCase #-} module Runner (   runModules+, Verbose(..) , Summary(..)+, formatSummary  #ifdef TEST , Report-, ReportState (..)+, ReportState(..)+, Interactive(..) , report-, report_+, reportTransient #endif ) where -import           Prelude hiding (putStr, putStrLn, error)+import           Prelude ()+import           Imports hiding (putStr, putStrLn, error) -import           Control.Monad hiding (forM_) import           Text.Printf (printf)-import           System.IO (hPutStrLn, hPutStr, stderr, hIsTerminalDevice)-import           Data.Foldable (forM_)+import           System.IO hiding (putStr, putStrLn)  import           Control.Monad.Trans.State import           Control.Monad.IO.Class+import           Data.IORef  import           Interpreter (Interpreter) import qualified Interpreter@@ -30,17 +34,18 @@  -- | Summary of a test run. data Summary = Summary {-  sExamples :: Int-, sTried    :: Int-, sErrors   :: Int-, sFailures :: Int+  sExamples :: !Int+, sTried    :: !Int+, sErrors   :: !Int+, sFailures :: !Int } deriving Eq --- | Format a summary. instance Show Summary where-  show (Summary examples tried errors failures) =-    printf "Examples: %d  Tried: %d  Errors: %d  Failures: %d" examples tried errors failures+  show = formatSummary +formatSummary :: Summary -> String+formatSummary (Summary examples tried errors failures) =+  printf "Examples: %d  Tried: %d  Errors: %d  Failures: %d" examples tried errors failures  -- | Sum up summaries. instance Monoid Summary where@@ -53,72 +58,85 @@ #endif     (Summary x1 x2 x3 x4) (Summary y1 y2 y3 y4) = Summary (x1 + y1) (x2 + y2) (x3 + y3) (x4 + y4) +withLineBuffering :: Handle -> IO c -> IO c+withLineBuffering h action = bracket (hGetBuffering h) (hSetBuffering h) $ \ _ -> do+  hSetBuffering h LineBuffering+  action+ -- | Run all examples from a list of modules.-runModules :: Bool -> Bool -> Bool -> Interpreter -> [Module [Located DocTest]] -> IO Summary-runModules fastMode preserveIt verbose repl modules = do-  isInteractive <- hIsTerminalDevice stderr-  ReportState _ _ _ s <- (`execStateT` ReportState 0 isInteractive verbose mempty {sExamples = c}) $ do-    forM_ modules $ runModule fastMode preserveIt repl+runModules :: Bool -> Bool -> Verbose -> Interpreter -> [Module [Located DocTest]] -> IO Summary+runModules fastMode preserveIt verbose repl modules = withLineBuffering stderr $ do -    verboseReport "# Final summary:"-    gets (show . reportStateSummary) >>= report+  interactive <- hIsTerminalDevice stderr <&> \ case+    False -> NonInteractive+    True -> Interactive -  return s+  summary <- newIORef mempty {sExamples = n}++  let+    reportFinalResult :: IO ()+    reportFinalResult = do+      final <- readIORef summary+      hPutStrLn stderr (formatSummary final)++    run :: IO ()+    run = flip evalStateT (ReportState interactive verbose summary) $ do+      reportProgress+      forM_ modules $ runModule fastMode preserveIt repl+      verboseReport "# Final summary:"++  run `finally` reportFinalResult++  readIORef summary   where-    c = (sum . map count) modules+    n :: Int+    n = sum (map countExpressions modules) --- | Count number of expressions in given module.-count :: Module [Located DocTest] -> Int-count (Module _ setup tests) = sum (map length tests) + maybe 0 length setup+countExpressions :: Module [Located DocTest] -> Int+countExpressions (Module _ setup tests) = sum (map length tests) + maybe 0 length setup --- | A monad for generating test reports. type Report = StateT ReportState IO +data Interactive = NonInteractive | Interactive++data Verbose = NonVerbose | Verbose+  deriving (Eq, Show)+ data ReportState = ReportState {-  reportStateCount        :: Int     -- ^ characters on the current line-, reportStateInteractive  :: Bool    -- ^ should intermediate results be printed?-, reportStateVerbose      :: Bool-, reportStateSummary      :: Summary -- ^ test summary+  reportStateInteractive :: Interactive+, reportStateVerbose :: Verbose+, reportStateSummary :: IORef Summary } +getSummary :: Report Summary+getSummary = gets reportStateSummary >>= liftIO . readIORef+ -- | Add output to the report. report :: String -> Report ()-report msg = do-  overwrite msg--  -- add a newline, this makes the output permanent-  liftIO $ hPutStrLn stderr ""-  modify (\st -> st {reportStateCount = 0})+report = liftIO . hPutStrLn stderr  -- | Add intermediate output to the report. -- -- This will be overwritten by subsequent calls to `report`/`report_`. -- Intermediate out may not contain any newlines.-report_ :: String -> Report ()-report_ msg = do-  f <- gets reportStateInteractive-  when f $ do-    overwrite msg-    modify (\st -> st {reportStateCount = length msg})---- | Add output to the report, overwrite any intermediate out.-overwrite :: String -> Report ()-overwrite msg = do-  n <- gets reportStateCount-  let str | 0 < n     = "\r" ++ msg ++ replicate (n - length msg) ' '-          | otherwise = msg-  liftIO (hPutStr stderr str)+reportTransient :: String -> Report ()+reportTransient msg = gets reportStateInteractive >>= \ case+  NonInteractive -> pass+  Interactive -> liftIO $ do+    hPutStr stderr msg+    hFlush stderr+    hPutStr stderr $ '\r' : (replicate (length msg) ' ') ++ "\r"  -- | Run all examples from given module. runModule :: Bool -> Bool -> Interpreter -> Module [Located DocTest] -> Report () runModule fastMode preserveIt repl (Module module_ setup examples) = do -  Summary _ _ e0 f0 <- gets reportStateSummary+  Summary _ _ e0 f0 <- getSummary    forM_ setup $     runTestGroup preserveIt repl reload -  Summary _ _ e1 f1 <- gets reportStateSummary+  Summary _ _ e1 f1 <- getSummary    -- only run tests, if setup does not produce any errors/failures   when (e0 == e1 && f0 == f1) $@@ -171,19 +189,22 @@   updateSummary (Summary 0 1 0 0)  verboseReport :: String -> Report ()-verboseReport xs = do-  verbose <- gets reportStateVerbose-  when verbose $ report xs+verboseReport msg = gets reportStateVerbose >>= \ case+  NonVerbose -> pass+  Verbose -> report msg  updateSummary :: Summary -> Report () updateSummary summary = do-  ReportState n f v s <- get-  put (ReportState n f v $ s `mappend` summary)+  ref <- gets reportStateSummary+  liftIO $ modifyIORef' ref $ mappend summary+  reportProgress  reportProgress :: Report ()-reportProgress = do-  verbose <- gets reportStateVerbose-  when (not verbose) $ gets (show . reportStateSummary) >>= report_+reportProgress = gets reportStateVerbose >>= \ case+  NonVerbose -> do+    summary <- getSummary+    reportTransient (formatSummary summary)+  Verbose -> pass  -- | Run given test group. --@@ -191,9 +212,6 @@ -- can reuse the same 'Interpreter' for several test groups. runTestGroup :: Bool -> Interpreter -> IO () -> [Located DocTest] -> Report () runTestGroup preserveIt repl setup tests = do--  reportProgress-   liftIO setup   runExampleGroup preserveIt repl examples 
test/Language/Haskell/GhciWrapperSpec.hs view
@@ -6,7 +6,6 @@ import           Test.Hspec import           System.IO.Silently -import           Control.Exception import           Data.List  import           Language.Haskell.GhciWrapper (Interpreter, Config(..), defaultConfig)
test/MainSpec.hs view
@@ -7,7 +7,6 @@ import           Test.Hspec import           Test.HUnit (assertEqual, Assertion) -import           Control.Exception import           System.Directory (getCurrentDirectory, setCurrentDirectory) import           System.FilePath import           Run hiding (doctest)@@ -27,7 +26,7 @@ doctestWithPreserveIt :: HasCallStack => Bool -> FilePath -> [String] -> Summary -> Assertion doctestWithPreserveIt preserveIt workingDir ghcOptions expected = do   actual <- withCurrentDirectory ("test/integration" </> workingDir) (hSilence [stderr] $ doctestWithResult defaultConfig {ghcOptions, preserveIt})-  assertEqual label expected actual+  assertEqual label (formatSummary expected) (formatSummary actual)   where     label = workingDir ++ " " ++ show ghcOptions 
test/RunnerSpec.hs view
@@ -1,89 +1,45 @@ {-# LANGUAGE CPP, OverloadedStrings #-}-module RunnerSpec (main, spec) where+module RunnerSpec (spec) where  import           Imports  import           Test.Hspec +import           Data.IORef import           System.IO-import           System.IO.Silently (hCapture)+import           System.IO.Silently (hCapture_) import           Control.Monad.Trans.State import           Runner -main :: IO ()-main = hspec spec--capture :: Report a -> IO String-capture = fmap fst . hCapture [stderr] . (`execStateT` ReportState 0 True 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 :: Interactive -> Report a -> IO String+capture interactive action = do+  ref <- newIORef mempty+  hCapture_ [stderr] (evalStateT action (ReportState interactive NonVerbose ref))  spec :: Spec spec = do-   describe "report" $ do-     context "when mode is interactive" $ do-       it "writes to stderr" $ do-        capture $ do+        capture Interactive $ do           report "foobar"         `shouldReturn` "foobar\n" -      it "overwrites any intermediate output" $ do-        capture $ do-          report_ "foo"-          report  "bar"-        `shouldReturn` "foo\rbar\n"--      it "blank out intermediate output if necessary" $ do-        capture $ do-          report_ "foobar"-          report  "baz"-        `shouldReturn` "foobar\rbaz   \n"-     context "when mode is non-interactive" $ do       it "writes to stderr" $ do-        capture_ $ do+        capture NonInteractive $ do           report "foobar"         `shouldReturn` "foobar\n"    describe "report_" $ do-     context "when mode is interactive" $ do-      it "writes intermediate output to stderr" $ do-        capture $ do-          report_ "foobar"-        `shouldReturn` "foobar"--      it "overwrites any intermediate output" $ do-        capture $ do-          report_ "foo"-          report_ "bar"-        `shouldReturn` "foo\rbar"--      it "blank out intermediate output if necessary" $ do-        capture $ do-          report_ "foobar"-          report_  "baz"-        `shouldReturn` "foobar\rbaz   "+      it "writes transient output to stderr" $ do+        capture Interactive $ do+          reportTransient "foobar"+        `shouldReturn` "foobar\r      \r"      context "when mode is non-interactive" $ do       it "is ignored" $ do-        capture_ $ do-          report_ "foobar"+        capture NonInteractive $ do+          reportTransient "foobar"         `shouldReturn` ""--      it "does not influence a subsequent call to `report`" $ do-        capture_ $ do-          report_ "foo"-          report  "bar"-        `shouldReturn` "bar\n"--      it "does not require `report` to blank out any intermediate output" $ do-        capture_ $ do-          report_ "foobar"-          report  "baz"-        `shouldReturn` "baz\n"