@@ -16,6 +16,7 @@ export TestSetup, TestItem, TestItemResult
16
16
const RETESTITEMS_TEMP_FOLDER = mkpath (joinpath (tempdir (), " ReTestItemsTempLogsDirectory" ))
17
17
const DEFAULT_TESTITEM_TIMEOUT = 30 * 60
18
18
const DEFAULT_RETRIES = 0
19
+ const DEFAULT_MEMORY_THRESHOLD = 0.99
19
20
20
21
if isdefined (Base, :errormonitor )
21
22
const errmon = Base. errormonitor
@@ -139,6 +140,14 @@ will be run.
139
140
Can be used to load packages or set up the environment. Must be a `:block` expression.
140
141
- `test_end_expr::Expr`: an expression that will be evaluated after each testitem is run.
141
142
Can be used to verify that global state is unchanged after running a test. Must be a `:block` expression.
143
+ - `memory_threshold::Real`: Sets the fraction of memory that can be in use before a worker processes are
144
+ restarted to free memory. Defaults to $DEFAULT_MEMORY_THRESHOLD . Only supported with `nworkers > 0`.
145
+ For example, if set to 0.8, then when >80% of the available memory is in use, a worker process will be killed and
146
+ replaced with a new worker before the next testitem is evaluated. The testitem will then be run on the new worker
147
+ process, regardless of if memory pressure dropped below the threshold. If the memory pressure remains above the
148
+ threshold, then a worker process will again be replaced before the next testitem is evaluated.
149
+ Can also be set using the `RETESTITEMS_MEMORY_THRESHOLD` environment variable.
150
+ **Note**: the `memory_threshold` keyword is experimental and may be removed in future versions.
142
151
- `report::Bool=false`: If `true`, write a JUnit-format XML file summarising the test results.
143
152
Can also be set using the `RETESTITEMS_REPORT` environment variable. The location at which
144
153
the XML report is saved can be set using the `RETESTITEMS_REPORT_LOCATION` environment variable.
@@ -189,6 +198,7 @@ function runtests(
189
198
worker_init_expr:: Expr = Expr (:block ),
190
199
testitem_timeout:: Real = parse (Float64, get (ENV , " RETESTITEMS_TESTITEM_TIMEOUT" , string (DEFAULT_TESTITEM_TIMEOUT))),
191
200
retries:: Int = parse (Int, get (ENV , " RETESTITEMS_RETRIES" , string (DEFAULT_RETRIES))),
201
+ memory_threshold:: Real = parse (Float64, get (ENV , " RETESTITEMS_MEMORY_THRESHOLD" , string (DEFAULT_MEMORY_THRESHOLD))),
192
202
debug= 0 ,
193
203
name:: Union{Regex,AbstractString,Nothing} = nothing ,
194
204
tags:: Union{Symbol,AbstractVector{Symbol},Nothing} = nothing ,
@@ -211,6 +221,7 @@ function runtests(
211
221
end
212
222
logs in LOG_DISPLAY_MODES || throw (ArgumentError (" `logs` must be one of $LOG_DISPLAY_MODES , got $(repr (logs)) " ))
213
223
report && logs == :eager && throw (ArgumentError (" `report=true` is not compatible with `logs=:eager`" ))
224
+ (0 ≤ memory_threshold ≤ 1 ) || throw (ArgumentError (" `memory_threshold` must be between 0 and 1, got $(repr (memory_threshold)) " ))
214
225
# If we were given paths but none were valid, then nothing to run.
215
226
! isempty (paths) && isempty (paths′) && return nothing
216
227
shouldrun_combined (ti) = shouldrun (ti) && _shouldrun (name, ti. name) && _shouldrun (tags, ti. tags)
@@ -221,10 +232,10 @@ function runtests(
221
232
debuglvl = Int (debug)
222
233
if debuglvl > 0
223
234
LoggingExtras. withlevel (LoggingExtras. Debug; verbosity= debuglvl) do
224
- _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, verbose_results, debuglvl, report, logs)
235
+ _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs)
225
236
end
226
237
else
227
- return _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, verbose_results, debuglvl, report, logs)
238
+ return _runtests (shouldrun_combined, paths′, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debuglvl, report, logs)
228
239
end
229
240
end
230
241
238
249
# By tracking and reusing test environments, we can avoid this issue.
239
250
const TEST_ENVS = Dict {String, String} ()
240
251
241
- function _runtests (shouldrun, paths, nworkers:: Int , nworker_threads:: String , worker_init_expr:: Expr , test_end_expr:: Expr , testitem_timeout:: Real , retries:: Int , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol )
252
+ function _runtests (shouldrun, paths, nworkers:: Int , nworker_threads:: String , worker_init_expr:: Expr , test_end_expr:: Expr , testitem_timeout:: Real , retries:: Int , memory_threshold :: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol )
242
253
# Don't recursively call `runtests` e.g. if we `include` a file which calls it.
243
254
# So we ignore the `runtests(...)` call in `test/runtests.jl` when `runtests(...)`
244
255
# was called from the command line.
@@ -258,7 +269,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
258
269
if is_running_test_runtests_jl (proj_file)
259
270
# Assume this is `Pkg.test`, so test env already active.
260
271
@debugv 2 " Running in current environment `$(Base. active_project ()) `"
261
- return _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, verbose_results, debug, report, logs)
272
+ return _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs)
262
273
else
263
274
@debugv 1 " Activating test environment for `$proj_file `"
264
275
orig_proj = Base. active_project ()
@@ -271,7 +282,7 @@ function _runtests(shouldrun, paths, nworkers::Int, nworker_threads::String, wor
271
282
testenv = TestEnv. activate ()
272
283
TEST_ENVS[proj_file] = testenv
273
284
end
274
- _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, verbose_results, debug, report, logs)
285
+ _runtests_in_current_env (shouldrun, paths, proj_file, nworkers, nworker_threads, worker_init_expr, test_end_expr, testitem_timeout, retries, memory_threshold, verbose_results, debug, report, logs)
275
286
finally
276
287
Base. set_active_project (orig_proj)
277
288
end
281
292
282
293
function _runtests_in_current_env (
283
294
shouldrun, paths, projectfile:: String , nworkers:: Int , nworker_threads, worker_init_expr:: Expr , test_end_expr:: Expr ,
284
- testitem_timeout:: Real , retries:: Int , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol ,
295
+ testitem_timeout:: Real , retries:: Int , memory_threshold :: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol ,
285
296
)
286
297
start_time = time ()
287
298
proj_name = something (Pkg. Types. read_project (projectfile). name, " " )
@@ -346,7 +357,7 @@ function _runtests_in_current_env(
346
357
ti = starting[i]
347
358
@spawn begin
348
359
with_logger (original_logger) do
349
- manage_worker ($ w, $ proj_name, $ testitems, $ ti, $ nworker_threads, $ worker_init_expr, $ test_end_expr, $ testitem_timeout, $ retries, $ verbose_results, $ debug, $ report, $ logs)
360
+ manage_worker ($ w, $ proj_name, $ testitems, $ ti, $ nworker_threads, $ worker_init_expr, $ test_end_expr, $ testitem_timeout, $ retries, $ memory_threshold, $ verbose_results, $ debug, $ report, $ logs)
350
361
end
351
362
end
352
363
end
@@ -454,13 +465,20 @@ function record_test_error!(testitem, msg, elapsed_seconds::Real=0.0)
454
465
end
455
466
456
467
function manage_worker (
457
- worker:: Worker , proj_name, testitems, testitem, nworker_threads, worker_init_expr:: Expr , test_end_expr:: Expr ,
458
- timeout:: Real , retries:: Int , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol
468
+ worker:: Worker , proj_name:: AbstractString , testitems:: TestItems , testitem:: Union{TestItem,Nothing} , nworker_threads, worker_init_expr:: Expr , test_end_expr:: Expr ,
469
+ timeout:: Real , retries:: Int , memory_threshold :: Real , verbose_results:: Bool , debug:: Int , report:: Bool , logs:: Symbol
459
470
)
460
471
ntestitems = length (testitems. testitems)
461
472
run_number = 1
473
+ memory_threshold_percent = 100 * memory_threshold
462
474
while testitem != = nothing
463
475
ch = Channel {TestItemResult} (1 )
476
+ if memory_percent () > memory_threshold_percent
477
+ @warn " Memory usage ($(Base. Ryu. writefixed (memory_percent (), 1 )) %) is higher than threshold ($(Base. Ryu. writefixed (memory_threshold_percent, 1 )) %). Restarting worker process to try to free memory."
478
+ terminate! (worker)
479
+ wait (worker)
480
+ worker = robust_start_worker (proj_name, nworker_threads, worker_init_expr, ntestitems)
481
+ end
464
482
testitem. workerid[] = worker. pid
465
483
fut = remote_eval (worker, :(ReTestItems. runtestitem ($ testitem, GLOBAL_TEST_CONTEXT; test_end_expr= $ (QuoteNode (test_end_expr)), verbose_results= $ verbose_results, logs= $ (QuoteNode (logs)))))
466
484
max_runs = 1 + max (retries, testitem. retries)
0 commit comments