7
7
8
8
import csv
9
9
import glob
10
+ import itertools
10
11
import os
11
12
import os .path
12
13
import platform
@@ -693,9 +694,8 @@ def test_module_name(self) -> None:
693
694
694
695
@pytest .mark .skipif (env .WINDOWS , reason = "This test is not for Windows" )
695
696
def test_save_signal_usr1 (self ) -> None :
696
- test_file = "dummy_hello.py"
697
697
self .assert_doesnt_exist (".coverage" )
698
- self .make_file (test_file , """\
698
+ self .make_file ("dummy_hello.py" , """\
699
699
import os
700
700
import signal
701
701
@@ -706,7 +706,7 @@ def test_save_signal_usr1(self) -> None:
706
706
print("Done and goodbye")
707
707
""" )
708
708
out = self .run_command (
709
- f "coverage run --save-signal=USR1 { test_file } " ,
709
+ "coverage run --save-signal=USR1 dummy_hello.py " ,
710
710
status = - signal .SIGKILL ,
711
711
)
712
712
# `startswith` because on Linux it also prints "Killed"
@@ -1321,6 +1321,7 @@ def test_removing_directory_with_error(self) -> None:
1321
1321
1322
1322
1323
1323
@pytest .mark .skipif (env .METACOV , reason = "Can't test subprocess pth file during metacoverage" )
1324
+ @pytest .mark .xdist_group (name = "needs_pth" )
1324
1325
class ProcessStartupTest (CoverageTest ):
1325
1326
"""Test that we can measure coverage in subprocesses."""
1326
1327
@@ -1340,7 +1341,6 @@ def setUp(self) -> None:
1340
1341
f.close()
1341
1342
""" )
1342
1343
1343
- @pytest .mark .xdist_group (name = "needs_pth" )
1344
1344
def test_patch_subprocess (self ) -> None :
1345
1345
self .make_file (".coveragerc" , """\
1346
1346
[run]
@@ -1349,12 +1349,11 @@ def test_patch_subprocess(self) -> None:
1349
1349
self .run_command ("coverage run main.py" )
1350
1350
self .run_command ("coverage combine" )
1351
1351
self .assert_exists (".coverage" )
1352
- data = coverage .CoverageData (".coverage" )
1352
+ data = coverage .CoverageData ()
1353
1353
data .read ()
1354
1354
assert line_counts (data )["main.py" ] == 3
1355
1355
assert line_counts (data )["sub.py" ] == 3
1356
1356
1357
- @pytest .mark .xdist_group (name = "needs_pth" )
1358
1357
def test_subprocess_with_pth_files (self , _create_pth_file : None ) -> None :
1359
1358
# An existing data file should not be read when a subprocess gets
1360
1359
# measured automatically. Create the data file here with bogus data in
@@ -1379,7 +1378,6 @@ def test_subprocess_with_pth_files(self, _create_pth_file: None) -> None:
1379
1378
data .read ()
1380
1379
assert line_counts (data )['sub.py' ] == 3
1381
1380
1382
- @pytest .mark .xdist_group (name = "needs_pth" )
1383
1381
def test_subprocess_with_pth_files_and_parallel (self , _create_pth_file : None ) -> None :
1384
1382
# https://github.com/nedbat/coveragepy/issues/492
1385
1383
self .make_file ("coverage.ini" , """\
@@ -1410,6 +1408,76 @@ def test_subprocess_with_pth_files_and_parallel(self, _create_pth_file: None) ->
1410
1408
assert len (data_files ) == 1 , msg
1411
1409
1412
1410
1411
+ @pytest .mark .skipif (env .METACOV , reason = "Can't test subprocess pth file during metacoverage" )
1412
+ @pytest .mark .skipif (env .WINDOWS , reason = "patch=execv isn't supported on Windows" )
1413
+ @pytest .mark .xdist_group (name = "needs_pth" )
1414
+ class ExecvTest (CoverageTest ):
1415
+ """Test that we can measure coverage in subprocesses."""
1416
+
1417
+ @pytest .mark .parametrize ("fname" ,
1418
+ [base + suffix for base , suffix in itertools .product (
1419
+ ["exec" , "spawn" ],
1420
+ ["l" , "le" , "lp" , "lpe" , "v" , "ve" , "vp" , "vpe" ],
1421
+ )]
1422
+ )
1423
+ def test_execv_patch (self , fname : str ) -> None :
1424
+ if not hasattr (os , fname ):
1425
+ pytest .skip (f"This OS doesn't have os.{ fname } " )
1426
+
1427
+ self .make_file (".coveragerc" , """\
1428
+ [run]
1429
+ patch = subprocess, execv
1430
+ """ )
1431
+ self .make_file ("main.py" , f"""\
1432
+ import os, sys
1433
+ print("In main")
1434
+ args = []
1435
+ if "spawn" in { fname !r} :
1436
+ args.append(os.P_WAIT)
1437
+ args.append(sys.executable)
1438
+ prog_args = ["python", { os .path .abspath ("other.py" )!r} , "cat", "dog"]
1439
+ if "l" in { fname !r} :
1440
+ args.extend(prog_args)
1441
+ else:
1442
+ args.append(prog_args)
1443
+ if { fname !r} .endswith("e"):
1444
+ args.append({{"SUBVAR": "the-sub-var"}})
1445
+ os.environ["MAINVAR"] = "the-main-var"
1446
+ sys.stdout.flush()
1447
+ os.{ fname } (*args)
1448
+ """ )
1449
+ self .make_file ("other.py" , """\
1450
+ import os, sys
1451
+ print(f"MAINVAR = {os.getenv('MAINVAR', 'none')}")
1452
+ print(f"SUBVAR = {os.getenv('SUBVAR', 'none')}")
1453
+ print(f"{sys.argv[1:] = }")
1454
+ """ )
1455
+
1456
+ out = self .run_command ("coverage run main.py" )
1457
+ expected = "In main\n "
1458
+ if fname .endswith ("e" ):
1459
+ expected += "MAINVAR = none\n "
1460
+ expected += "SUBVAR = the-sub-var\n "
1461
+ else :
1462
+ expected += "MAINVAR = the-main-var\n "
1463
+ expected += "SUBVAR = none\n "
1464
+ expected += "sys.argv[1:] = ['cat', 'dog']\n "
1465
+ assert out == expected
1466
+
1467
+ self .run_command ("coverage combine" )
1468
+ data = coverage .CoverageData ()
1469
+ data .read ()
1470
+
1471
+ main_lines = 12
1472
+ if "spawn" in fname :
1473
+ main_lines += 1
1474
+ if fname .endswith ("e" ):
1475
+ main_lines += 1
1476
+
1477
+ assert line_counts (data )["main.py" ] == main_lines
1478
+ assert line_counts (data )["other.py" ] == 4
1479
+
1480
+
1413
1481
class ProcessStartupWithSourceTest (CoverageTest ):
1414
1482
"""Show that we can configure {[run]source} during process-level coverage.
1415
1483
0 commit comments