doctest 0.20.1 → 0.21.0
raw patch · 9 files changed
+94/−76 lines, 9 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- CHANGES.markdown +3/−0
- README.md +27/−3
- doctest.cabal +2/−2
- ghci-wrapper/src/Language/Haskell/GhciWrapper.hs +10/−6
- src/Options.hs +35/−57
- src/Runner.hs +1/−1
- test/MainSpec.hs +2/−3
- test/OptionsSpec.hs +13/−3
- test/RunSpec.hs +1/−1
CHANGES.markdown view
@@ -1,3 +1,6 @@+Changes in 0.21.0+ - Accept `--fast`, `--preserve-it` and `--verbose` via `--repl-options`+ Changes in 0.20.1 - GHC 9.4 compatibility. (#382)
README.md view
@@ -10,7 +10,8 @@ * [Getting started](#getting-started) * [Installation](#installation) * [A basic example](#a-basic-example)- * [Running doctest for a Cabal package](#running-doctest-for-a-cabal-package)+* [Running doctest for a Cabal package](#running-doctest-for-a-cabal-package)+ * [Passing doctest options to cabal repl](#passing-doctest-options-to-cabal-repl) * [Writing examples and properties](#writing-examples-and-properties) * [Example groups](#example-groups) * [A note on performance](#a-note-on-performance)@@ -91,7 +92,7 @@ ``` -## Running `doctest` for a Cabal package+# Running `doctest` for a Cabal package The easiest way to run `doctest` for a Cabal package is via `cabal repl --with-ghc=doctest`. @@ -134,6 +135,7 @@ - `doctest` always uses the version of GHC it was compiled with. Reinstalling `doctest` with `cabal install doctest --overwrite-policy=always` before each invocation ensures that it uses the same version of GHC as is on the `PATH`.+ - Technically, `cabal build` is not necessary. `cabal repl --with-ghc=doctest` will build any dependencies as needed. However, it's more robust to run `cabal build` first (specifically it is not a good idea to build@@ -147,7 +149,29 @@ (This is what you want to use on CI.) +## Passing `doctest` options to `cabal repl` +You can pass `doctest` options like `--fast`, `--preserve-it` and `--verbose` to+`cabal repl` via `--repl-options`.++Example:++```+$ cabal repl --with-ghc=doctest --repl-options=--verbose+### Started execution at src/Fib.hs:7.+### example:+fib 10+### Successful!++### Started execution at src/Fib.hs:10.+### example:+fib 5+### Successful!++# Final summary:+Examples: 2 Tried: 2 Errors: 0 Failures: 0+```+ # Writing examples and properties ## Example groups@@ -185,7 +209,7 @@ (With the caveat that the order in which groups appear now matters!) However, note that due to a-[bug on GHC 8.2.1 or later](https://ghc.haskell.org/trac/ghc/ticket/14052),+[bug on GHC 8.2.1 or later](https://gitlab.haskell.org/ghc/ghc/-/issues/14052), the performance of `--fast` suffers significantly when combined with the `--preserve-it` flag (which keeps the value of GHCi's `it` value between examples).
doctest.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.0.+-- This file has been generated from package.yaml by hpack version 0.35.1. -- -- see: https://github.com/sol/hpack name: doctest-version: 0.20.1+version: 0.21.0 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)
ghci-wrapper/src/Language/Haskell/GhciWrapper.hs view
@@ -43,14 +43,18 @@ itMarker = "d42472243a0e6fc481e7514cbc9eb08812ed48daa29ca815844d86010b1d113a" data Interpreter = Interpreter {- hIn :: Handle- , hOut :: Handle- , process :: ProcessHandle- }+ hIn :: Handle+, hOut :: Handle+, process :: ProcessHandle+} new :: Config -> [String] -> IO Interpreter new Config{..} args_ = do- (Just stdin_, Just stdout_, Nothing, processHandle ) <- createProcess $ (proc configGhci args) {std_in = CreatePipe, std_out = CreatePipe, std_err = Inherit}+ (Just stdin_, Just stdout_, Nothing, processHandle ) <- createProcess (proc configGhci args) {+ std_in = CreatePipe+ , std_out = CreatePipe+ , std_err = Inherit+ } setMode stdin_ setMode stdout_ let interpreter = Interpreter {hIn = stdin_, hOut = stdout_, process = processHandle}@@ -134,7 +138,7 @@ echo :: String -> IO () echo | echoMode = putStr- | otherwise = (const $ return ())+ | otherwise = \ _ -> return () -- | Evaluate an expression eval :: Interpreter -> String -> IO String
src/Options.hs view
@@ -3,12 +3,9 @@ module Options ( Result(..) , Run(..)-, defaultMagic-, defaultFastMode-, defaultPreserveIt-, defaultVerbose , parseOptions #ifdef TEST+, defaultRun , usage , info , versionInfo@@ -72,26 +69,14 @@ , "--abi-hash" ] -defaultMagic :: Bool-defaultMagic = True--defaultFastMode :: Bool-defaultFastMode = False--defaultPreserveIt :: Bool-defaultPreserveIt = False--defaultVerbose :: Bool-defaultVerbose = False- defaultRun :: Run defaultRun = Run { runWarnings = [] , runOptions = []-, runMagicMode = defaultMagic-, runFastMode = defaultFastMode-, runPreserveIt = defaultPreserveIt-, runVerbose = defaultVerbose+, runMagicMode = False+, runFastMode = False+, runPreserveIt = False+, runVerbose = False } modifyWarnings :: ([String] -> [String]) -> Run -> Run@@ -114,51 +99,41 @@ parseOptions :: [String] -> Result Run parseOptions args- | "--info" `elem` args = Output info- | "--interactive" `elem` args = Result Run {- runWarnings = []- , runOptions = filter (/= "--interactive") args- , runMagicMode = False- , runFastMode = False- , runPreserveIt = False- , runVerbose = False- }- | any (`elem` nonInteractiveGhcOptions) args = RunGhc args- | "--help" `elem` args = Output usage- | "--version" `elem` args = Output versionInfo- | otherwise = case execRWS parse () args of- (xs, Endo setter) ->- Result (setOptions xs $ setter defaultRun)- where- parse :: RWS () (Endo Run) [String] ()- parse = do- stripNoMagic- stripFast- stripPreserveIt- stripVerbose- stripOptGhc--stripNoMagic :: RWS () (Endo Run) [String] ()-stripNoMagic = stripFlag (setMagicMode False) "--no-magic"+ | on "--info" = Output info+ | on "--interactive" = runRunOptionsParser (discard "--interactive" args) defaultRun $ do+ commonRunOptions+ | on `any` nonInteractiveGhcOptions = RunGhc args+ | on "--help" = Output usage+ | on "--version" = Output versionInfo+ | otherwise = runRunOptionsParser args defaultRun {runMagicMode = True} $ do+ commonRunOptions+ parseFlag "--no-magic" (setMagicMode False)+ parseOptGhc+ where+ on option = option `elem` args -stripFast :: RWS () (Endo Run) [String] ()-stripFast = stripFlag (setFastMode True) "--fast"+type RunOptionsParser = RWS () (Endo Run) [String] () -stripPreserveIt :: RWS () (Endo Run) [String] ()-stripPreserveIt = stripFlag (setPreserveIt True) "--preserve-it"+runRunOptionsParser :: [String] -> Run -> RunOptionsParser -> Result Run+runRunOptionsParser args def parse = case execRWS parse () args of+ (xs, Endo setter) ->+ Result (setOptions xs $ setter def) -stripVerbose :: RWS () (Endo Run) [String] ()-stripVerbose = stripFlag (setVerbose True) "--verbose"+commonRunOptions :: RunOptionsParser+commonRunOptions = do+ parseFlag "--fast" (setFastMode True)+ parseFlag "--preserve-it" (setPreserveIt True)+ parseFlag "--verbose" (setVerbose True) -stripFlag :: (Run -> Run) -> String -> RWS () (Endo Run) [String] ()-stripFlag setter flag = do+parseFlag :: String -> (Run -> Run) -> RunOptionsParser+parseFlag flag setter = do args <- RWS.get when (flag `elem` args) $ RWS.tell (Endo setter)- RWS.put (filter (/= flag) args)+ RWS.put (discard flag args) -stripOptGhc :: RWS () (Endo Run) [String] ()-stripOptGhc = do+parseOptGhc :: RunOptionsParser+parseOptGhc = do issueWarning <- RWS.state go when issueWarning $ RWS.tell $ Endo $ modifyWarnings (++ [warning])@@ -169,3 +144,6 @@ opt : rest -> maybe (fmap (opt :)) (\x (_, xs) -> (True, x : xs)) (stripPrefix "--optghc=" opt) (go rest) warning = "WARNING: --optghc is deprecated, doctest now accepts arbitrary GHC options\ndirectly."++discard :: String -> [String] -> [String]+discard flag = filter (/= flag)
src/Runner.hs view
@@ -129,7 +129,7 @@ reload = do unless fastMode $ -- NOTE: It is important to do the :reload first! See- -- https://ghc.haskell.org/trac/ghc/ticket/5904, which results in a+ -- https://gitlab.haskell.org/ghc/ghc/-/issues/5904, which results in a -- panic on GHC 7.4.1 if you do the :reload second. void $ Interpreter.safeEval repl ":reload" void $ Interpreter.safeEval repl $ ":m *" ++ module_
test/MainSpec.hs view
@@ -8,7 +8,6 @@ import Control.Exception import System.Directory (getCurrentDirectory, setCurrentDirectory) import System.FilePath-import Options import Runner (Summary(..)) import Run hiding (doctest) import System.IO.Silently@@ -22,11 +21,11 @@ -- | Construct a doctest specific 'Assertion'. doctest :: HasCallStack => FilePath -> [String] -> Summary -> Assertion-doctest = doctestWithPreserveIt defaultPreserveIt+doctest = doctestWithPreserveIt False doctestWithPreserveIt :: HasCallStack => Bool -> FilePath -> [String] -> Summary -> Assertion doctestWithPreserveIt preserveIt workingDir args expected = do- actual <- withCurrentDirectory ("test/integration" </> workingDir) (hSilence [stderr] $ doctestWithOptions defaultFastMode preserveIt defaultVerbose args)+ actual <- withCurrentDirectory ("test/integration" </> workingDir) (hSilence [stderr] $ doctestWithOptions False preserveIt False args) assertEqual label expected actual where label = workingDir ++ " " ++ show args
test/OptionsSpec.hs view
@@ -18,12 +18,19 @@ spec :: Spec spec = do describe "parseOptions" $ do- let warning = ["WARNING: --optghc is deprecated, doctest now accepts arbitrary GHC options\ndirectly."]+ let+ run :: [String] -> Run+ run options = defaultRun {+ runWarnings = ["WARNING: --optghc is deprecated, doctest now accepts arbitrary GHC options\ndirectly."]+ , runOptions = options+ , runMagicMode = True+ }+ it "strips --optghc" $- parseOptions ["--optghc", "foobar"] `shouldBe` Result (Run warning ["foobar"] defaultMagic defaultFastMode defaultPreserveIt defaultVerbose)+ parseOptions ["--optghc", "foobar"] `shouldBe` Result (run ["foobar"]) it "strips --optghc=" $- parseOptions ["--optghc=foobar"] `shouldBe` Result (Run warning ["foobar"] defaultMagic defaultFastMode defaultPreserveIt defaultVerbose)+ parseOptions ["--optghc=foobar"] `shouldBe` Result (run ["foobar"]) context "with ghc options that are not valid with --interactive" $ do it "returns RunGhc" $ do@@ -39,6 +46,9 @@ it "filters out --interactive" $ do runOptions <$> parseOptions options `shouldBe` Result ["--foo", "--bar"]++ it "accepts --fast" $ do+ runFastMode <$> parseOptions ("--fast" : options) `shouldBe` Result True describe "--no-magic" $ do context "without --no-magic" $ do
test/RunSpec.hs view
@@ -20,7 +20,7 @@ import Run doctestWithDefaultOptions :: [String] -> IO Summary-doctestWithDefaultOptions = doctestWithOptions Options.defaultFastMode Options.defaultPreserveIt Options.defaultVerbose+doctestWithDefaultOptions = doctestWithOptions False False False withCurrentDirectory :: FilePath -> IO a -> IO a withCurrentDirectory workingDir action = do