Skip to content

Commit 947a2af

Browse files
kadengvmoens
andauthored
[Bugfix] Codecov does not cover multiprocessed tests #879 (#893)
Co-authored-by: Vincent Moens <vincentmoens@gmail.com>
1 parent ee7a498 commit 947a2af

File tree

12 files changed

+103
-29
lines changed

12 files changed

+103
-29
lines changed
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Copyright (c) Meta Platforms, Inc. and affiliates.
2+
#
3+
# This source code is licensed under the MIT license found in the
4+
# LICENSE file in the root directory of this source tree.
5+
6+
"""
7+
When coverage run is launched with --concurrency=multiprocessing, it
8+
needs a config file for further arguments.
9+
10+
This script is a drop-in wrapper to conveniently pass command line arguments
11+
nevertheless. It writes temporary coverage config files on the fly and
12+
invokes coverage with proper arguments
13+
"""
14+
15+
import os
16+
import shlex
17+
import subprocess
18+
import sys
19+
import tempfile
20+
from pathlib import Path
21+
from typing import List
22+
23+
24+
def write_config(config_path: Path, argv: List[str]) -> None:
25+
"""
26+
Write a coverage.py config that is equivalent to the command line arguments passed here.
27+
Args:
28+
config_path: Path to write config to
29+
argv: Arguments passed to this script, which need to be converted to config file entries
30+
"""
31+
assert not config_path.exists(), "Temporary coverage config exists already"
32+
cmdline = " ".join(shlex.quote(arg) for arg in argv[1:])
33+
with open(str(config_path), "wt", encoding="utf-8") as fh:
34+
fh.write(
35+
f"""# .coveragerc to control coverage.py
36+
[run]
37+
parallel=True
38+
concurrency=
39+
multiprocessing
40+
thread
41+
command_line={cmdline}
42+
"""
43+
)
44+
45+
46+
def main(argv: List[str]) -> int:
47+
if len(argv) < 1:
48+
print("Usage: 'python coverage_run_parallel.py <command> [command arguments]'")
49+
sys.exit(1)
50+
# The temporary config is written into a temp dir that will be deleted
51+
# including all contents on context exit.
52+
# Note: Do not use tempfile.NamedTemporaryFile as this file cannot
53+
# be read by other processes on Windows
54+
with tempfile.TemporaryDirectory(prefix=".torchrl_coverage_config_tmp_") as tempdir:
55+
config_path = Path(tempdir) / ".coveragerc"
56+
os.environ["COVERAGE_RCFILE"] = str(
57+
config_path
58+
) # This gets passed down to subprocesses
59+
write_config(config_path, argv)
60+
return subprocess.run(["coverage", "run"]).returncode
61+
62+
63+
if __name__ == "__main__":
64+
sys.exit(main(sys.argv))

.circleci/unittest/linux/scripts/run_test.sh

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ lib_dir="${env_dir}/lib"
2424
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$lib_dir
2525
export MKL_THREADING_LAYER=GNU
2626

27-
coverage run -m pytest test/smoke_test.py -v --durations 20
28-
coverage run -m pytest test/smoke_test_deps.py -v --durations 20 -k 'test_gym or test_dm_control_pixels or test_dm_control or test_tb'
29-
coverage run -m pytest --instafail -v --durations 20
27+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test.py -v --durations 20
28+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test_deps.py -v --durations 20 -k 'test_gym or test_dm_control_pixels or test_dm_control or test_tb'
29+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest --instafail -v --durations 20
30+
coverage combine
3031
coverage xml -i

.circleci/unittest/linux_examples/scripts/run_test.sh

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ lib_dir="${env_dir}/lib"
2424
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$lib_dir
2525
export MKL_THREADING_LAYER=GNU
2626

27-
coverage run -m pytest test/smoke_test.py -v --durations 20
28-
coverage run -m pytest test/smoke_test_deps.py -v --durations 20
29-
coverage run examples/ddpg/ddpg.py \
27+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test.py -v --durations 20
28+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test_deps.py -v --durations 20
29+
python .circleci/unittest/helpers/coverage_run_parallel.py examples/ddpg/ddpg.py \
3030
total_frames=48 \
3131
init_random_frames=10 \
3232
batch_size=10 \
@@ -38,7 +38,7 @@ coverage run examples/ddpg/ddpg.py \
3838
record_video=True \
3939
record_frames=4 \
4040
buffer_size=120
41-
coverage run examples/a2c/a2c.py \
41+
python .circleci/unittest/helpers/coverage_run_parallel.py examples/a2c/a2c.py \
4242
total_frames=48 \
4343
batch_size=10 \
4444
frames_per_batch=16 \
@@ -49,7 +49,7 @@ coverage run examples/a2c/a2c.py \
4949
record_video=True \
5050
record_frames=4 \
5151
logger=csv
52-
coverage run examples/dqn/dqn.py \
52+
python .circleci/unittest/helpers/coverage_run_parallel.py examples/dqn/dqn.py \
5353
total_frames=48 \
5454
init_random_frames=10 \
5555
batch_size=10 \
@@ -61,7 +61,7 @@ coverage run examples/dqn/dqn.py \
6161
record_video=True \
6262
record_frames=4 \
6363
buffer_size=120
64-
coverage run examples/redq/redq.py \
64+
python .circleci/unittest/helpers/coverage_run_parallel.py examples/redq/redq.py \
6565
total_frames=48 \
6666
init_random_frames=10 \
6767
batch_size=10 \
@@ -73,7 +73,7 @@ coverage run examples/redq/redq.py \
7373
record_video=True \
7474
record_frames=4 \
7575
buffer_size=120
76-
coverage run examples/sac/sac.py \
76+
python .circleci/unittest/helpers/coverage_run_parallel.py examples/sac/sac.py \
7777
total_frames=48 \
7878
init_random_frames=10 \
7979
batch_size=10 \
@@ -85,7 +85,7 @@ coverage run examples/sac/sac.py \
8585
record_video=True \
8686
record_frames=4 \
8787
buffer_size=120
88-
coverage run examples/ppo/ppo.py \
88+
python .circleci/unittest/helpers/coverage_run_parallel.py examples/ppo/ppo.py \
8989
total_frames=48 \
9090
batch_size=10 \
9191
frames_per_batch=16 \
@@ -96,7 +96,7 @@ coverage run examples/ppo/ppo.py \
9696
record_video=True \
9797
record_frames=4 \
9898
lr_scheduler=
99-
coverage run examples/dreamer/dreamer.py \
99+
python .circleci/unittest/helpers/coverage_run_parallel.py examples/dreamer/dreamer.py \
100100
total_frames=48 \
101101
init_random_frames=10 \
102102
batch_size=10 \
@@ -109,4 +109,5 @@ coverage run examples/dreamer/dreamer.py \
109109
record_frames=4 \
110110
buffer_size=120 \
111111
rssm_hidden_dim=17
112+
coverage combine
112113
coverage xml -i

.circleci/unittest/linux_libs/scripts_brax/run_test.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ export MAGNUM_LOG=verbose MAGNUM_GPU_VALIDATION=ON
2828
# this workflow only tests the libs
2929
python -c "import brax"
3030

31-
coverage run -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestBrax
31+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestBrax
32+
coverage combine
3233
coverage xml -i

.circleci/unittest/linux_libs/scripts_envpool/run_test.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,6 @@ export MKL_THREADING_LAYER=GNU
2727
# this workflow only tests the libs
2828
python -c "import envpool"
2929

30-
coverage run -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestEnvPool
30+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestEnvPool
31+
coverage combine
3132
coverage xml -i

.circleci/unittest/linux_libs/scripts_gym/run_test.sh

100755100644
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ lib_dir="${env_dir}/lib"
2121
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$lib_dir
2222
export MKL_THREADING_LAYER=GNU
2323

24-
coverage run -m pytest test/smoke_test.py -v --durations 20
25-
coverage run -m pytest test/smoke_test_deps.py -v --durations 20 -k 'test_gym or test_dm_control_pixels or test_dm_control'
26-
MUJOCO_GL=egl python test/test_libs.py -k test_collect
27-
MUJOCO_GL=egl coverage run -m pytest --instafail -v --durations 20 -k 'test_libs'
24+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test.py -v --durations 20
25+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test_deps.py -v --durations 20 -k 'test_gym or test_dm_control_pixels or test_dm_control'
26+
MUJOCO_GL=egl python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest --instafail -v --durations 20 -k 'test_libs'
27+
coverage combine
2828
coverage xml -i

.circleci/unittest/linux_libs/scripts_habitat/run_test.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@ env = HabitatEnv('HabitatRenderPick-v0')
4141
env.reset()
4242
"""
4343

44-
coverage run -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestHabitat
44+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestHabitat
45+
coverage combine
4546
coverage xml -i

.circleci/unittest/linux_libs/scripts_jumanji/run_test.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ export MAGNUM_LOG=verbose MAGNUM_GPU_VALIDATION=ON
2828
# this workflow only tests the libs
2929
python -c "import jumanji"
3030

31-
coverage run -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestJumanji
31+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestJumanji
32+
coverage combine
3233
coverage xml -i

.circleci/unittest/linux_libs/scripts_vmas/run_test.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,6 @@ export MAGNUM_LOG=verbose MAGNUM_GPU_VALIDATION=ON
2525
# this workflow only tests the libs
2626
python -c "import vmas"
2727

28-
coverage run -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestVmas
28+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/test_libs.py --instafail -v --durations 20 --capture no -k TestVmas
29+
coverage combine
2930
coverage xml -i

.circleci/unittest/linux_olddeps/scripts_gym_0_13/run_test.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ lib_dir="${env_dir}/lib"
2020
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$lib_dir
2121
export MKL_THREADING_LAYER=GNU
2222

23-
coverage run -m pytest test/smoke_test.py -v --durations 20
24-
coverage run -m pytest test/smoke_test_deps.py -v --durations 20 -k 'test_gym or test_dm_control_pixels or test_dm_control'
25-
#MUJOCO_GL=egl coverage run -m xvfb-run -a pytest --instafail -v --durations 20
26-
MUJOCO_GL=egl coverage run -m pytest --instafail -v --durations 20
23+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test.py -v --durations 20
24+
python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest test/smoke_test_deps.py -v --durations 20 -k 'test_gym or test_dm_control_pixels or test_dm_control'
25+
#MUJOCO_GL=egl python .circleci/unittest/helpers/coverage_run_parallel.py -m xvfb-run -a pytest --instafail -v --durations 20
26+
MUJOCO_GL=egl python .circleci/unittest/helpers/coverage_run_parallel.py -m pytest --instafail -v --durations 20
2727
#pytest --instafail -v --durations 20
2828
#python test/test_libs.py
29+
coverage combine
2930
coverage xml -i

0 commit comments

Comments
 (0)