Skip to content

Commit e40a168

Browse files
giordanomaleadt
andauthored
Stop workers when there are no more tests to run (#107)
Co-authored-by: Tim Besard <tim.besard@gmail.com>
1 parent 33980bb commit e40a168

File tree

3 files changed

+118
-1
lines changed

3 files changed

+118
-1
lines changed

src/ParallelTestRunner.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1059,7 +1059,12 @@ function runtests(mod::Module, args::ParsedArgs;
10591059
Malt.stop(wrkr)
10601060
end
10611061

1062-
delete!(running_tests, test)
1062+
Base.@lock test_lock begin
1063+
delete!(running_tests, test)
1064+
end
1065+
end
1066+
if p !== nothing
1067+
Malt.stop(p)
10631068
end
10641069
end)
10651070
end

test/runtests.jl

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ using Test
33

44
cd(@__DIR__)
55

6+
include(joinpath(@__DIR__, "utils.jl"))
7+
68
@testset "ParallelTestRunner" verbose=true begin
79

810
@testset "basic use" begin
@@ -401,4 +403,84 @@ end
401403
@test ParallelTestRunner.ID_COUNTER[] == old_id_counter + njobs
402404
end
403405

406+
407+
# Issue <https://github.com/JuliaTesting/ParallelTestRunner.jl/issues/106>.
408+
@testset "default workers stopped at end" begin
409+
# Use default workers (no test_worker) so the framework creates and should stop them.
410+
# More tests than workers so some tasks finish early and must stop their worker.
411+
testsuite = Dict(
412+
"t1" => :(),
413+
"t2" => :(),
414+
"t3" => :(),
415+
"t4" => :(),
416+
"t5" => :(),
417+
"t6" => quote
418+
# Make this test run longer than the others so that it runs alone...
419+
sleep(5)
420+
children = _count_child_pids($(getpid()))
421+
# ...then check there's only one worker still running. WARNING: this test may be
422+
# flaky on very busy systems, if at this point some of the other tests are still
423+
# running, hope for the best.
424+
if children >= 0
425+
@test children == 1
426+
end
427+
end,
428+
)
429+
before = _count_child_pids()
430+
if before < 0
431+
# Counting child PIDs not supported on this platform
432+
@test_skip false
433+
else
434+
old_id_counter = ParallelTestRunner.ID_COUNTER[]
435+
njobs = 2
436+
io = IOBuffer()
437+
ioc = IOContext(io, :color => true)
438+
try
439+
runtests(ParallelTestRunner, ["--jobs=$(njobs)", "--verbose"];
440+
testsuite, stdout=ioc, stderr=ioc, init_code=:(include($(joinpath(@__DIR__, "utils.jl")))))
441+
catch
442+
# Show output in case of failure, to help debugging.
443+
output = String(take!(io))
444+
printstyled(stderr, "Output of failed test >>>>>>>>>>>>>>>>>>>>\n", color=:red, bold=true)
445+
println(stderr, output)
446+
printstyled(stderr, "End of output <<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", color=:red, bold=true)
447+
rethrow()
448+
end
449+
# Make sure we didn't spawn more workers than expected.
450+
@test ParallelTestRunner.ID_COUNTER[] == old_id_counter + njobs
451+
# Allow a moment for worker processes to exit
452+
for _ in 1:50
453+
sleep(0.1)
454+
after = _count_child_pids()
455+
after >= 0 && after <= before && break
456+
end
457+
after = _count_child_pids()
458+
@test after >= 0
459+
@test after == before
460+
end
461+
end
462+
463+
# Custom workers are handled differently:
464+
# <https://github.com/JuliaTesting/ParallelTestRunner.jl/pull/107#issuecomment-3980645143>.
465+
# But we still want to make sure they're terminated at the end.
466+
@testset "custom workers stopped at end" begin
467+
testsuite = Dict(
468+
"a" => :(),
469+
"b" => :(),
470+
"c" => :(),
471+
"d" => :(),
472+
"e" => :(),
473+
"f" => :(),
474+
)
475+
procs = Base.Process[]
476+
procs_lock = ReentrantLock()
477+
function test_worker(name)
478+
wrkr = addworker()
479+
Base.@lock procs_lock push!(procs, wrkr.w.proc)
480+
return wrkr
481+
end
482+
runtests(ParallelTestRunner, Base.ARGS; test_worker, testsuite, stdout=devnull, stderr=devnull)
483+
@test all(!Base.process_running, procs)
484+
end
485+
404486
end

test/utils.jl

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Count direct child processes of current process (for default-worker test).
2+
# Returns -1 if unsupported so the test can be skipped.
3+
function _count_child_pids(pid = getpid())
4+
if Sys.isunix() && !isnothing(Sys.which("ps"))
5+
pids = Int[]
6+
out = try
7+
# Suggested in <https://askubuntu.com/a/512872>.
8+
readchomp(`ps -o ppid= -o pid= -A`)
9+
catch
10+
return -1
11+
end
12+
lines = split(out, '\n')
13+
# The output of `ps` for the current process always contains `ps` itself
14+
# because it's spawned by the current process, in that case we subtract
15+
# one to always exclude it, otherwise if we're getting the number of
16+
# children of another process we start from 0.
17+
count = pid == getpid() : -1 : 0
18+
for line in lines
19+
m = match(r" *(\d+) +(\d+)", line)
20+
if !isnothing(m)
21+
if parse(Int, m[1]) == pid
22+
count += 1
23+
end
24+
end
25+
end
26+
return count
27+
else
28+
return -1
29+
end
30+
end

0 commit comments

Comments
 (0)