Skip to content

Commit 0ff2585

Browse files
committed
--test-suite-timeout-grace
1 parent c38ba8f commit 0ff2585

22 files changed

Lines changed: 244 additions & 15 deletions

File tree

ChangeLog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ Other enhancements:
2323
* Add option `--reach <packages>` to Stack's `dot` and `ls dependencies`
2424
commands, to prune packages that cannot reach any of the specified packages in
2525
the dependency graph.
26+
* Add option `--test-suite-timeout-grace ARG` (and corresponding non-project
27+
configuration key `test-suite-timeout-grace`) to pair with
28+
`--test-suite-timeout ARG` and perform staged timeout termination for test
29+
suites.
2630

2731
Bug fixes:
2832

doc/commands/bench_command.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ stack bench [TARGET] [--dry-run] [--pedantic] [--fast] [--ghc-options OPTIONS]
1717
[--[no-]keep-going] [--[no-]keep-tmp-files] [--[no-]force-dirty]
1818
[--[no-]test] [--[no-]rerun-tests] [--ta|--test-arguments TEST_ARGS]
1919
[--coverage] [--[no-]run-tests] [--test-suite-timeout ARG]
20+
[--test-suite-timeout-grace ARG]
2021
[--[no-]tests-allow-stdin] [--[no-]bench]
2122
[--ba|--benchmark-arguments BENCH_ARGS] [--[no-]run-benchmarks]
2223
[--[no-]reconfigure] [--cabal-verbosity VERBOSITY |

doc/commands/build_command.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ stack build [TARGET] [--dry-run] [--pedantic] [--fast] [--ghc-options OPTIONS]
1818
[--[no-]keep-going] [--[no-]keep-tmp-files] [--[no-]force-dirty]
1919
[--[no-]test] [--[no-]rerun-tests] [--ta|--test-arguments TEST_ARGS]
2020
[--coverage] [--[no-]run-tests] [--test-suite-timeout ARG]
21+
[--test-suite-timeout-grace ARG]
2122
[--[no-]tests-allow-stdin] [--[no-]bench]
2223
[--ba|--benchmark-arguments BENCH_ARGS] [--[no-]run-benchmarks]
2324
[--[no-]reconfigure] [--cabal-verbosity VERBOSITY |

doc/commands/haddock_command.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ stack haddock [TARGET] [--dry-run] [--pedantic] [--fast] [--ghc-options OPTIONS]
1717
[--[no-]keep-going] [--[no-]keep-tmp-files] [--[no-]force-dirty]
1818
[--[no-]test] [--[no-]rerun-tests]
1919
[--ta|--test-arguments TEST_ARGS] [--coverage] [--[no-]run-tests]
20-
[--test-suite-timeout ARG] [--[no-]tests-allow-stdin]
20+
[--test-suite-timeout ARG] [--test-suite-timeout-grace ARG]
21+
[--[no-]tests-allow-stdin]
2122
[--[no-]bench] [--ba|--benchmark-arguments BENCH_ARGS]
2223
[--[no-]run-benchmarks] [--[no-]reconfigure]
2324
[--cabal-verbosity VERBOSITY | --[no-]cabal-verbose]

doc/commands/install_command.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ stack install [TARGET] [--dry-run] [--pedantic] [--fast] [--ghc-options OPTIONS]
1717
[--[no-]keep-going] [--[no-]keep-tmp-files] [--[no-]force-dirty]
1818
[--[no-]test] [--[no-]rerun-tests]
1919
[--ta|--test-arguments TEST_ARGS] [--coverage] [--[no-]run-tests]
20-
[--test-suite-timeout ARG] [--[no-]tests-allow-stdin]
20+
[--test-suite-timeout ARG] [--test-suite-timeout-grace ARG]
21+
[--[no-]tests-allow-stdin]
2122
[--[no-]bench] [--ba|--benchmark-arguments BENCH_ARGS]
2223
[--[no-]run-benchmarks] [--[no-]reconfigure]
2324
[--cabal-verbosity VERBOSITY | --[no-]cabal-verbose]

doc/commands/test_command.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ stack test [TARGET] [--dry-run] [--pedantic] [--fast] [--ghc-options OPTIONS]
1717
[--[no-]keep-going] [--[no-]keep-tmp-files] [--[no-]force-dirty]
1818
[--[no-]test] [--[no-]rerun-tests] [--ta|--test-arguments TEST_ARGS]
1919
[--coverage] [--[no-]run-tests] [--test-suite-timeout ARG]
20+
[--test-suite-timeout-grace ARG]
2021
[--[no-]tests-allow-stdin] [--[no-]bench]
2122
[--ba|--benchmark-arguments BENCH_ARGS] [--[no-]run-benchmarks]
2223
[--[no-]reconfigure] [--cabal-verbosity VERBOSITY |

doc/configure/yaml/non-project.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,10 @@ build:
214214
additional-args: []
215215
coverage: false
216216
no-run-tests: false
217+
# Maximum test suite run time in seconds.
218+
test-suite-timeout:
219+
# Grace period in seconds after test-suite-timeout before force kill.
220+
test-suite-timeout-grace:
217221
bench: false
218222
benchmark-opts:
219223

src/Stack/Build/ExecutePackage.hs

Lines changed: 41 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ import Stack.Build.ExecuteEnv
6868
( ExcludeTHLoading (..), ExecuteEnv (..), KeepOutputOpen (..)
6969
, OutputType (..), withSingleContext
7070
)
71+
import Stack.Build.TestSuiteTimeout
72+
( forceKill, prepareForEscalation, terminateGracefully )
7173
import Stack.Build.Source ( addUnlistedToBuildCache )
7274
import Stack.Config.ConfigureScript ( ensureConfigureScript )
7375
import Stack.ConfigureOpts
@@ -1156,13 +1158,47 @@ singleTest topts testsToRun ac ee task installedMap = do
11561158
)
11571159
createSource
11581160
OTLogFile _ h -> Nothing <$ useHandleOpen h
1159-
optionalTimeout action
1161+
runOutput p =
1162+
case (getStdout p, getStderr p) of
1163+
(Nothing, Nothing) -> pure ()
1164+
(Just x, Just y) -> concurrently_ x y
1165+
(x, y) -> assert False $
1166+
concurrently_
1167+
(fromMaybe (pure ()) x)
1168+
(fromMaybe (pure ()) y)
1169+
timeoutWithGrace p maxSecs graceSecs = do
1170+
mExit <- timeout (maxSecs * 1000000) (waitExitCode p)
1171+
case mExit of
1172+
Just ec -> pure (Just ec)
1173+
Nothing -> do
1174+
terminateGracefully p
1175+
mGraceExit <- timeout (graceSecs * 1000000)
1176+
(waitExitCode p)
1177+
case mGraceExit of
1178+
Just _ -> pure Nothing
1179+
Nothing -> do
1180+
forceKill p
1181+
void $ waitExitCode p
1182+
pure Nothing
1183+
runWithTimeout pc
1184+
| Just maxSecs <- topts.maximumTimeSeconds, maxSecs > 0
1185+
, Just graceSecs <- topts.timeoutGraceSeconds
1186+
, graceSecs > 0 =
1187+
withProcessWait (prepareForEscalation pc) $ \p -> do
1188+
(_, mec') <- concurrently
1189+
(runOutput p)
1190+
(timeoutWithGrace p maxSecs graceSecs)
1191+
pure mec'
11601192
| Just maxSecs <- topts.maximumTimeSeconds, maxSecs > 0 =
1161-
timeout (maxSecs * 1000000) action
1162-
| otherwise = Just <$> action
1193+
timeout (maxSecs * 1000000) $
1194+
withProcessWait pc $ \p -> do
1195+
runOutput p
1196+
waitExitCode p
1197+
| otherwise =
1198+
Just <$> withProcessWait pc (\p -> runOutput p *> waitExitCode p)
11631199

11641200
mec <- withWorkingDir (toFilePath pkgDir) $
1165-
optionalTimeout $ proc (toFilePath exePath) args $ \pc0 -> do
1201+
proc (toFilePath exePath) args $ \pc0 -> do
11661202
changeStdin <-
11671203
if isTestTypeLib
11681204
then do
@@ -1185,15 +1221,7 @@ singleTest topts testsToRun ac ee task installedMap = do
11851221
$ setStdout output
11861222
$ setStderr output
11871223
pc0
1188-
withProcessWait pc $ \p -> do
1189-
case (getStdout p, getStderr p) of
1190-
(Nothing, Nothing) -> pure ()
1191-
(Just x, Just y) -> concurrently_ x y
1192-
(x, y) -> assert False $
1193-
concurrently_
1194-
(fromMaybe (pure ()) x)
1195-
(fromMaybe (pure ()) y)
1196-
waitExitCode p
1224+
runWithTimeout pc
11971225
-- Add a trailing newline, incase the test
11981226
-- output didn't finish with a newline.
11991227
case outputType of

src/Stack/BuildOpts.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ defaultTestOpts = TestOpts
7474
, coverage = defaultFirstFalse toMonoid.coverage
7575
, runTests = defaultFirstTrue toMonoid.runTests
7676
, maximumTimeSeconds = Nothing
77+
, timeoutGraceSeconds = Nothing
7778
, allowStdin = defaultFirstTrue toMonoid.allowStdin
7879
}
7980
where

src/Stack/Config/Build.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,10 @@ testOptsFromMonoid toMonoid madditional = defaultTestOpts
135135
fromFirst
136136
defaultTestOpts.maximumTimeSeconds
137137
toMonoid.maximumTimeSeconds
138+
, TestOpts.timeoutGraceSeconds =
139+
fromFirst
140+
defaultTestOpts.timeoutGraceSeconds
141+
toMonoid.timeoutGraceSeconds
138142
, TestOpts.allowStdin = fromFirstTrue toMonoid.allowStdin
139143
}
140144

0 commit comments

Comments
 (0)