Skip to content

Commit 06f32cd

Browse files
authored
Add exec variant that allows a negative call (#55)
* Add a exec variant that doesn't fail upon receiving a non-zero error code * exec*: shorten output (to enhance readibility) * Add a execFlex variant that doesn't fail upon receiving a non-zero exit code * New exec variants: add argument-level documentation
1 parent fdbed5a commit 06f32cd

File tree

1 file changed

+46
-27
lines changed

1 file changed

+46
-27
lines changed

src/Hedgehog/Extras/Test/Process.hs

+46-27
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66
module Hedgehog.Extras.Test.Process
77
( createProcess
88
, exec
9+
, execAny
910
, exec_
1011
, execFlex
1112
, execFlex'
13+
, execFlexAny'
1214
, procFlex
1315
, binFlex
1416

@@ -43,7 +45,7 @@ import Hedgehog (MonadTest)
4345
import Hedgehog.Extras.Internal.Cli (argQuote)
4446
import Hedgehog.Extras.Internal.Plan (Component (..), Plan (..))
4547
import Hedgehog.Extras.Stock.IO.Process (TimedOut (..))
46-
import Prelude (error)
48+
import Prelude (error, (++))
4749
import System.Exit (ExitCode)
4850
import System.FilePath (takeDirectory)
4951
import System.FilePath.Posix ((</>))
@@ -164,27 +166,36 @@ execFlex'
164166
-> [String]
165167
-> m String
166168
execFlex' execConfig pkgBin envBin arguments = GHC.withFrozenCallStack $ do
167-
cp <- procFlex' execConfig pkgBin envBin arguments
168-
H.annotate . ("Command: " <>) $ case IO.cmdspec cp of
169-
IO.ShellCommand cmd -> cmd
170-
IO.RawCommand cmd args -> cmd <> " " <> L.unwords args
171-
(exitResult, stdout, stderr) <- H.evalIO $ IO.readCreateProcessWithExitCode cp ""
169+
(exitResult, stdout, stderr) <- execFlexAny' execConfig pkgBin envBin arguments
172170
case exitResult of
173171
IO.ExitFailure exitCode -> do
174172
H.annotate $ L.unlines $
175-
[ "Process exited with non-zero exit-code"
173+
[ "Process exited with non-zero exit-code: " ++ show @Int exitCode
176174
, "━━━━ command ━━━━"
177175
, pkgBin <> " " <> L.unwords (fmap argQuote arguments)
178-
, "━━━━ stdout ━━━━"
179-
, stdout
180-
, "━━━━ stderr ━━━━"
181-
, stderr
182-
, "━━━━ exit code ━━━━"
183-
, show @Int exitCode
184176
]
177+
++ if L.null stdout then [] else ["━━━━ stdout ━━━━" , stdout]
178+
++ if L.null stderr then [] else ["━━━━ stderr ━━━━" , stderr]
185179
H.failMessage GHC.callStack "Execute process failed"
186180
IO.ExitSuccess -> return stdout
187181

182+
-- | Run a process, returning its exit code, its stdout, and its stderr.
183+
-- Contrary to @execFlex'@, this function doesn't fail if the call fails.
184+
-- So, if you want to test something negative, this is the function to use.
185+
execFlexAny'
186+
:: (MonadTest m, MonadCatch m, MonadIO m, HasCallStack)
187+
=> ExecConfig
188+
-> String -- ^ @pkgBin@: name of the binary to launch via 'cabal exec'
189+
-> String -- ^ @envBin@: environment variable defining the binary to launch the process, when in Nix
190+
-> [String]
191+
-> m (ExitCode, String, String) -- ^ exit code, stdout, stderr
192+
execFlexAny' execConfig pkgBin envBin arguments = GHC.withFrozenCallStack $ do
193+
cp <- procFlex' execConfig pkgBin envBin arguments
194+
H.annotate . ("Command: " <>) $ case IO.cmdspec cp of
195+
IO.ShellCommand cmd -> cmd
196+
IO.RawCommand cmd args -> cmd <> " " <> L.unwords args
197+
H.evalIO $ IO.readCreateProcessWithExitCode cp ""
198+
188199
-- | Execute a process, returning '()'.
189200
exec_
190201
:: (MonadTest m, MonadIO m, HasCallStack)
@@ -194,34 +205,42 @@ exec_
194205
-> m ()
195206
exec_ execConfig bin arguments = void $ exec execConfig bin arguments
196207

197-
-- | Execute a process
208+
-- | Execute a process, returning the stdout. Fail if the call returns
209+
-- with a non-zero exit code. For a version that doesn't fail upon receiving
210+
-- a non-zero exit code, see 'execAny'.
198211
exec
199212
:: (MonadTest m, MonadIO m, HasCallStack)
200213
=> ExecConfig
201214
-> String
202215
-> [String]
203216
-> m String
204217
exec execConfig bin arguments = GHC.withFrozenCallStack $ do
205-
let cp = (IO.proc bin arguments)
206-
{ IO.env = getLast $ execConfigEnv execConfig
207-
, IO.cwd = getLast $ execConfigCwd execConfig
208-
}
209-
H.annotate . ("Command: " <>) $ bin <> " " <> L.unwords arguments
210-
(exitResult, stdout, stderr) <- H.evalIO $ IO.readCreateProcessWithExitCode cp ""
218+
(exitResult, stdout, stderr) <- execAny execConfig bin arguments
211219
case exitResult of
212220
IO.ExitFailure exitCode -> H.failMessage GHC.callStack . L.unlines $
213-
[ "Process exited with non-zero exit-code"
221+
[ "Process exited with non-zero exit-code: " ++ show @Int exitCode
214222
, "━━━━ command ━━━━"
215223
, bin <> " " <> L.unwords (fmap argQuote arguments)
216-
, "━━━━ stdout ━━━━"
217-
, stdout
218-
, "━━━━ stderr ━━━━"
219-
, stderr
220-
, "━━━━ exit code ━━━━"
221-
, show @Int exitCode
222224
]
225+
++ if L.null stdout then [] else ["━━━━ stdout ━━━━" , stdout]
226+
++ if L.null stderr then [] else ["━━━━ stderr ━━━━" , stderr]
223227
IO.ExitSuccess -> return stdout
224228

229+
-- | Execute a process, returning the error code, the stdout, and the stderr.
230+
execAny
231+
:: (MonadTest m, MonadIO m, HasCallStack)
232+
=> ExecConfig
233+
-> String -- ^ The binary to launch
234+
-> [String] -- ^ The binary's arguments
235+
-> m (ExitCode, String, String) -- ^ exit code, stdout, stderr
236+
execAny execConfig bin arguments = GHC.withFrozenCallStack $ do
237+
let cp = (IO.proc bin arguments)
238+
{ IO.env = getLast $ execConfigEnv execConfig
239+
, IO.cwd = getLast $ execConfigCwd execConfig
240+
}
241+
H.annotate . ("Command: " <>) $ bin <> " " <> L.unwords arguments
242+
H.evalIO $ IO.readCreateProcessWithExitCode cp ""
243+
225244
-- | Wait for process to exit.
226245
waitForProcess
227246
:: (MonadTest m, MonadIO m, HasCallStack)

0 commit comments

Comments
 (0)