Coverage for tests / test_process.py: 100.000%

605 statements  

« prev     ^ index     » next       coverage.py v7.12.1a0.dev1, created at 2025-11-30 17:57 +0000

1# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 

2# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt 

3 

4"""Tests for process behavior of coverage.py.""" 

5 

6from __future__ import annotations 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

7 

8import csv 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

9import glob 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

10import itertools 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

11import os 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

12import os.path 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

13import platform 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

14import re 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

15import signal 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

16import stat 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

17import sys 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

18import textwrap 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

19 

20from pathlib import Path 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

21from typing import Any 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

22 

23import pytest 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

24 

25import coverage 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

26from coverage import env 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

27from coverage.data import line_counts 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

28from coverage.files import abs_file, python_reported_file 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

29 

30from tests import testenv 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

31from tests.coveragetest import CoverageTest, TESTS_DIR 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

32from tests.helpers import change_dir, re_lines, re_lines_text 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

33 

34 

35class ProcessTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

36 """Tests of the per-process behavior of coverage.py.""" 

37 

38 def test_save_on_exit(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

39 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

40 "mycode.py", 

41 """\ 

42 h = "Hello" 

43 w = "world" 

44 """, 

45 ) 

46 

47 self.assert_doesnt_exist(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

48 self.run_command("coverage run mycode.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

49 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

50 

51 def test_tests_dir_is_importable(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

52 # Checks that we can import modules from the tests directory at all! 

53 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

54 "mycode.py", 

55 """\ 

56 import covmod1 

57 import covmodzip1 

58 a = 1 

59 print('done') 

60 """, 

61 ) 

62 

63 self.assert_doesnt_exist(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

64 self.add_test_modules_to_pythonpath() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

65 out = self.run_command("coverage run mycode.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

66 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

67 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

68 

69 def test_coverage_run_envvar_is_in_coveragerun(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

70 # Test that we are setting COVERAGE_RUN when we run. 

71 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

72 "envornot.py", 

73 """\ 

74 import os 

75 print(os.getenv("COVERAGE_RUN", "nope")) 

76 """, 

77 ) 

78 self.del_environ("COVERAGE_RUN") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

79 # Regular Python doesn't have the environment variable. 

80 out = self.run_command("python envornot.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

81 assert out == "nope\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

82 self.del_environ("COVERAGE_RUN") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

83 # But `coverage run` does have it. 

84 out = self.run_command("coverage run envornot.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

85 assert out == "true\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

86 

87 def make_b_or_c_py(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

88 """Create b_or_c.py, used in a few of these tests.""" 

89 # "b_or_c.py b" will run 6 lines. 

90 # "b_or_c.py c" will run 7 lines. 

91 # Together, they run 8 lines. 

92 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

93 "b_or_c.py", 

94 """\ 

95 import sys 

96 a = 2 

97 if sys.argv[1] == 'b': 

98 b = 4 

99 else: 

100 c = 6 

101 c2 = 7 

102 d = 8 

103 print('done') 

104 """, 

105 ) 

106 

107 def test_append_data(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

108 self.make_b_or_c_py() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

109 

110 out = self.run_command("coverage run b_or_c.py b") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

111 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

112 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

113 self.assert_file_count(".coverage.*", 0) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

114 

115 out = self.run_command("coverage run --append b_or_c.py c") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

116 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

117 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

118 self.assert_file_count(".coverage.*", 0) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

119 

120 # Read the coverage file and see that b_or_c.py has all 8 lines 

121 # executed. 

122 data = coverage.CoverageData() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

123 data.read() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

124 assert line_counts(data)["b_or_c.py"] == 8 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

125 

126 def test_append_data_with_different_file(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

127 self.make_b_or_c_py() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

128 

129 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

130 ".coveragerc", 

131 """\ 

132 [run] 

133 data_file = .mycovdata 

134 """, 

135 ) 

136 

137 out = self.run_command("coverage run b_or_c.py b") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

138 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

139 self.assert_doesnt_exist(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

140 self.assert_exists(".mycovdata") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

141 

142 out = self.run_command("coverage run --append b_or_c.py c") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

143 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

144 self.assert_doesnt_exist(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

145 self.assert_exists(".mycovdata") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

146 

147 # Read the coverage file and see that b_or_c.py has all 8 lines 

148 # executed. 

149 data = coverage.CoverageData(".mycovdata") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

150 data.read() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

151 assert line_counts(data)["b_or_c.py"] == 8 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

152 

153 def test_append_can_create_a_data_file(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

154 self.make_b_or_c_py() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

155 

156 out = self.run_command("coverage run --append b_or_c.py b") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

157 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

158 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

159 self.assert_file_count(".coverage.*", 0) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

160 

161 # Read the coverage file and see that b_or_c.py has only 6 lines 

162 # executed. 

163 data = coverage.CoverageData() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

164 data.read() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

165 assert line_counts(data)["b_or_c.py"] == 6 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

166 

167 def test_combine_with_rc(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

168 self.make_b_or_c_py() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

169 

170 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

171 ".coveragerc", 

172 """\ 

173 [run] 

174 source = . 

175 parallel = true 

176 """, 

177 ) 

178 

179 out = self.run_command("coverage run b_or_c.py b") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

180 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

181 self.assert_doesnt_exist(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

182 

183 out = self.run_command("coverage run b_or_c.py c") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

184 assert out == "done\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

185 self.assert_doesnt_exist(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

186 

187 # After two runs, there should be two .coverage.machine.123 files. 

188 self.assert_file_count(".coverage.*", 2) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

189 

190 # Combine the parallel coverage data files into .coverage . 

191 self.run_command("coverage combine") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

192 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

193 self.assert_exists(".coveragerc") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

194 

195 # After combining, there should be only the .coverage file. 

196 self.assert_file_count(".coverage.*", 0) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

197 

198 # Read the coverage file and see that b_or_c.py has all 8 lines 

199 # executed. 

200 data = coverage.CoverageData() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

201 data.read() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

202 assert line_counts(data)["b_or_c.py"] == 8 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

203 

204 # Reporting should still work even with the .rc file 

205 out = self.run_command("coverage report") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

206 assert out == textwrap.dedent("""\ 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

207 Name Stmts Miss Cover 

208 ------------------------------- 

209 b_or_c.py 8 0 100% 

210 ------------------------------- 

211 TOTAL 8 0 100% 

212 """) 

213 

214 def test_combine_with_aliases(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

215 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

216 "d1/x.py", 

217 """\ 

218 a = 1 

219 b = 2 

220 print(f"{a} {b}") 

221 """, 

222 ) 

223 

224 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

225 "d2/x.py", 

226 """\ 

227 # 1 

228 # 2 

229 # 3 

230 c = 4 

231 d = 5 

232 print(f"{c} {d}") 

233 """, 

234 ) 

235 

236 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

237 ".coveragerc", 

238 """\ 

239 [run] 

240 source = . 

241 parallel = True 

242 

243 [paths] 

244 source = 

245 src 

246 */d1 

247 */d2 

248 """, 

249 ) 

250 

251 out = self.run_command("coverage run " + os.path.normpath("d1/x.py")) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

252 assert out == "1 2\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

253 out = self.run_command("coverage run " + os.path.normpath("d2/x.py")) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

254 assert out == "4 5\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

255 

256 self.assert_file_count(".coverage.*", 2) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

257 

258 self.make_file("src/x.py", "") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

259 

260 self.run_command("coverage combine") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

261 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

262 

263 # After combining, there should be only the .coverage file. 

264 self.assert_file_count(".coverage.*", 0) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

265 

266 # Read the coverage data file and see that the two different x.py 

267 # files have been combined together. 

268 data = coverage.CoverageData() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

269 data.read() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

270 summary = line_counts(data, fullpath=True) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

271 assert len(summary) == 1 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

272 actual = abs_file(list(summary.keys())[0]) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

273 expected = abs_file("src/x.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

274 assert expected == actual 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

275 assert list(summary.values())[0] == 6 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

276 

277 def test_erase_parallel(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

278 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

279 ".coveragerc", 

280 """\ 

281 [run] 

282 data_file = data.dat 

283 parallel = True 

284 """, 

285 ) 

286 self.make_file("data.dat") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

287 self.make_file("data.dat.fooey") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

288 self.make_file("data.dat.gooey") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

289 self.make_file(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

290 

291 self.run_command("coverage erase") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

292 self.assert_doesnt_exist("data.dat") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

293 self.assert_doesnt_exist("data.dat.fooey") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

294 self.assert_doesnt_exist("data.dat.gooey") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

295 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

296 

297 def test_missing_source_file(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

298 # Check what happens if the source is missing when reporting happens. 

299 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

300 "fleeting.py", 

301 """\ 

302 s = 'goodbye, cruel world!' 

303 """, 

304 ) 

305 

306 self.run_command("coverage run fleeting.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

307 os.remove("fleeting.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

308 out = self.run_command("coverage html -d htmlcov", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

309 assert re.search("No source for code: '.*fleeting.py'", out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

310 assert "Traceback" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

311 

312 # It happens that the code paths are different for *.py and other 

313 # files, so try again with no extension. 

314 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

315 "fleeting", 

316 """\ 

317 s = 'goodbye, cruel world!' 

318 """, 

319 ) 

320 

321 self.run_command("coverage run fleeting") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

322 os.remove("fleeting") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

323 out = self.run_command("coverage html -d htmlcov", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

324 assert re.search(r"No source for code: '.*fleeting'", out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

325 assert re.search( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

326 r"; see https://coverage.readthedocs.io/en/[^/]+/messages.html#error-no-source", 

327 out, 

328 ) 

329 assert "Traceback" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

330 

331 def test_running_missing_file(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

332 status, out = self.run_command_status("coverage run xyzzy.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

333 assert re.search("No file to run: .*xyzzy.py", out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

334 assert "raceback" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

335 assert "rror" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

336 assert status == 1 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

337 

338 def test_code_throws(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

339 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

340 "throw.py", 

341 """\ 

342 class MyException(Exception): 

343 pass 

344 

345 def f1(): 

346 raise MyException("hey!") 

347 

348 def f2(): 

349 f1() 

350 

351 f2() 

352 """, 

353 ) 

354 

355 # The important thing is for "coverage run" and "python" to report the 

356 # same traceback. 

357 out = self.run_command("coverage run throw.py", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

358 out2 = self.run_command("python throw.py", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

359 if env.PYPY: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

360 # PyPy has an extra frame in the traceback for some reason 

361 out2 = re_lines_text("toplevel", out2, match=False) 1TO)

362 assert out == out2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

363 

364 # But also make sure that the output is what we expect. 

365 path = python_reported_file("throw.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

366 msg = f'File "{re.escape(path)}", line 8, in f2' 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

367 assert re.search(msg, out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

368 assert 'raise MyException("hey!")' in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

369 

370 def test_code_exits(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

371 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

372 "exit.py", 

373 """\ 

374 import sys 

375 def f1(): 

376 print("about to exit..") 

377 sys.exit(17) 

378 

379 def f2(): 

380 f1() 

381 

382 f2() 

383 """, 

384 ) 

385 

386 # The important thing is for "coverage run" and "python" to have the 

387 # same output. No traceback. 

388 status, out = self.run_command_status("coverage run exit.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

389 status2, out2 = self.run_command_status("python exit.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

390 assert out == out2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

391 assert out == "about to exit..\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

392 assert status == status2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

393 assert status == 17 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

394 

395 def test_code_exits_no_arg(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

396 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

397 "exit_none.py", 

398 """\ 

399 import sys 

400 def f1(): 

401 print("about to exit quietly..") 

402 sys.exit() 

403 

404 f1() 

405 """, 

406 ) 

407 status, out = self.run_command_status("coverage run exit_none.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

408 status2, out2 = self.run_command_status("python exit_none.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

409 assert out == out2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

410 assert out == "about to exit quietly..\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

411 assert status == status2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

412 assert status == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

413 

414 @pytest.mark.skipif(not hasattr(os, "fork"), reason="Can't test os.fork, it doesn't exist.") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

415 def test_fork(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

416 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

417 "fork.py", 

418 """\ 

419 import os 

420 

421 print(f"parent,{os.getpid()}", flush=True) 

422 ret = os.fork() 

423 

424 if ret == 0: 

425 print(f"child,{os.getpid()}", flush=True) 

426 else: 

427 os.waitpid(ret, 0) 

428 """, 

429 ) 

430 total_lines = 6 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

431 

432 self.set_environ("COVERAGE_DEBUG_FILE", "debug.out") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

433 out = self.run_command("coverage run --debug=pid,process,trace -p fork.py") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

434 pids = {key: int(pid) for key, pid in csv.reader(out.splitlines())} 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

435 assert set(pids) == {"parent", "child"} 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

436 self.assert_doesnt_exist(".coverage") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

437 

438 # After running the forking program, there should be two 

439 # .coverage.machine.pid.randomword files. The pids should match our 

440 # processes, and the files should have different random words at the 

441 # end of the file name. 

442 self.assert_file_count(".coverage.*", 2) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

443 data_files = glob.glob(".coverage.*") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

444 filepids = {int(name.split(".")[-2]) for name in data_files} 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

445 assert filepids == set(pids.values()) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

446 suffixes = {name.split(".")[-1] for name in data_files} 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

447 assert len(suffixes) == 2, f"Same random suffix: {data_files}" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

448 

449 # Each data file should have a subset of the lines. 

450 for data_file in data_files: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

451 data = coverage.CoverageData(data_file) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

452 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

453 assert line_counts(data)["fork.py"] < total_lines 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

454 

455 # Combine the parallel coverage data files into a .coverage file. 

456 # After combining, there should be only the .coverage file. 

457 self.run_command("coverage combine") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

458 self.assert_exists(".coverage") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

459 self.assert_file_count(".coverage.*", 0) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

460 

461 data = coverage.CoverageData() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

462 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

463 assert line_counts(data)["fork.py"] == total_lines 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

464 

465 debug_text = Path("debug.out").read_text(encoding="utf-8") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

466 ppid = pids["parent"] 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

467 cpid = pids["child"] 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

468 assert ppid != cpid 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

469 plines = re_lines(rf"{ppid}\.[0-9a-f]+: New process: pid={ppid}, executable", debug_text) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

470 assert len(plines) == 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

471 clines = re_lines(rf"{cpid}\.[0-9a-f]+: New process: forked {ppid} -> {cpid}", debug_text) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

472 assert len(clines) == 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

473 reported_pids = {line.split(".")[0] for line in debug_text.splitlines()} 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

474 assert len(reported_pids) == 2 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

475 

476 @pytest.mark.skipif(not hasattr(os, "fork"), reason="Can't test os.fork, it doesn't exist.") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

477 @pytest.mark.parametrize("patch", [False, True]) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

478 def test_os_exit(self, patch: bool) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

479 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

480 "forky.py", 

481 """\ 

482 import os 

483 import tempfile 

484 import time 

485 

486 complete_file = tempfile.mkstemp()[1] 

487 pid = os.fork() 

488 if pid: 

489 while True: 

490 with open(complete_file, encoding="ascii") as f: 

491 data = f.read() 

492 if "Complete" in data: 

493 break 

494 time.sleep(.02) 

495 os.remove(complete_file) 

496 else: 

497 time.sleep(.1) 

498 with open(complete_file, mode="w", encoding="ascii") as f: 

499 f.write("Complete") 

500 os._exit(0) 

501 # if this exists, then we broke os._exit completely 

502 open("impossible.txt", mode="w") 

503 """, 

504 ) 

505 total_lines = 17 # don't count the last impossible.txt line 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

506 if patch: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

507 self.make_file(".coveragerc", "[run]\npatch = _exit\n") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

508 self.run_command("coverage run -p forky.py") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

509 self.run_command("coverage combine") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

510 data = coverage.CoverageData() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

511 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

512 seen = line_counts(data)["forky.py"] 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

513 if patch: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

514 assert seen == total_lines 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

515 else: 

516 assert seen < total_lines 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

517 self.assert_doesnt_exist("impossible.txt") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

518 

519 def test_warnings_during_reporting(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

520 # While fixing issue #224, the warnings were being printed far too 

521 # often. Make sure they're not any more. 

522 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

523 "hello.py", 

524 """\ 

525 import sys, os, the_other 

526 print("Hello") 

527 """, 

528 ) 

529 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

530 "the_other.py", 

531 """\ 

532 print("What?") 

533 """, 

534 ) 

535 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

536 ".coveragerc", 

537 """\ 

538 [run] 

539 source = 

540 . 

541 xyzzy 

542 """, 

543 ) 

544 

545 self.run_command("coverage run hello.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

546 out = self.run_command("coverage html") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

547 assert out.count("Module xyzzy was never imported.") == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

548 

549 def test_warns_if_never_run(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

550 # Note: the name of the function can't have "warning" in it, or the 

551 # absolute path of the file will have "warning" in it, and an assertion 

552 # will fail. 

553 out = self.run_command("coverage run i_dont_exist.py", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

554 path = python_reported_file("i_dont_exist.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

555 assert f"No file to run: '{path}'" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

556 assert "warning" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

557 assert "Exception" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

558 

559 out = self.run_command("coverage run -m no_such_module", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

560 assert ("No module named no_such_module" in out) or ( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

561 "No module named 'no_such_module'" in out 

562 ) 

563 assert "warning" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

564 assert "Exception" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

565 

566 @pytest.mark.skipif(env.METACOV, reason="Can't test tracers changing during metacoverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

567 def test_warnings_trace_function_changed_with_threads(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

568 # https://github.com/coveragepy/coveragepy/issues/164 

569 

570 self.make_file( 

571 "bug164.py", 

572 """\ 

573 import threading 

574 import time 

575 

576 class MyThread (threading.Thread): 

577 def run(self): 

578 print("Hello") 

579 

580 thr = MyThread() 

581 thr.start() 

582 thr.join() 

583 """, 

584 ) 

585 out = self.run_command("coverage run --timid bug164.py") 

586 

587 assert "Hello\n" in out 

588 assert "warning" not in out 

589 

590 @pytest.mark.skipif(env.METACOV, reason="Can't test tracers changing during metacoverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

591 def test_warning_trace_function_changed(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

592 self.make_file( 

593 "settrace.py", 

594 """\ 

595 import sys 

596 print("Hello") 

597 sys.settrace(None) 

598 print("Goodbye") 

599 """, 

600 ) 

601 out = self.run_command("coverage run --timid settrace.py") 

602 assert "Hello\n" in out 

603 assert "Goodbye\n" in out 

604 

605 assert "Trace function changed" in out 

606 

607 # When meta-coverage testing, this test doesn't work, because it finds 

608 # coverage.py's own trace function. 

609 @pytest.mark.skipif(env.METACOV, reason="Can't test timid during coverage measurement.") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

610 def test_timid(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

611 # Test that the --timid command line argument properly swaps the tracer 

612 # function for a simpler one. 

613 # 

614 # This is complicated by the fact that the tests are run twice for each 

615 # version: once with a compiled C-based trace function, and once without 

616 # it, to also test the Python trace function. So this test has to examine 

617 # an environment variable set in igor.py to know whether to expect to see 

618 # the C trace function or not. 

619 

620 self.make_file( 

621 "showtrace.py", 

622 """\ 

623 # Show the current frame's trace function, so that we can test what the 

624 # command-line options do to the trace function used. 

625 

626 import inspect 

627 

628 # Show what the trace function is. If a C-based function is used, then f_trace 

629 # may be None. 

630 trace_fn = inspect.currentframe().f_trace 

631 if trace_fn is None: 

632 trace_name = "None" 

633 else: 

634 # Get the name of the tracer class. 

635 try: 

636 trace_name = trace_fn.__self__.__class__.__name__ 

637 except AttributeError: 

638 # A C-based function could also manifest as an f_trace value 

639 # which doesn't have __self__. 

640 trace_name = trace_fn.__class__.__name__ 

641 

642 print(trace_name) 

643 """, 

644 ) 

645 

646 # When running without coverage, no trace function 

647 py_out = self.run_command("python showtrace.py") 

648 assert py_out == "None\n" 

649 

650 cov_out = self.run_command("coverage run showtrace.py") 

651 if testenv.C_TRACER: 

652 # If the C trace function is being tested, then regular running should have 

653 # the C function, which registers itself as f_trace. 

654 assert cov_out == "CTracer\n" 

655 elif testenv.SYS_MON: 

656 assert cov_out == "None\n" 

657 else: 

658 # If the Python trace function is being tested, then regular running will 

659 # also show the Python function. 

660 assert cov_out == "PyTracer\n" 

661 

662 # When running timidly, the trace function is always Python. 

663 timid_out = self.run_command("coverage run --timid showtrace.py") 

664 assert timid_out == "PyTracer\n" 

665 

666 def test_warn_preimported(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

667 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

668 "hello.py", 

669 """\ 

670 import goodbye 

671 import coverage 

672 cov = coverage.Coverage(include=["good*"], check_preimported=True) 

673 cov.start() 

674 print(goodbye.f()) 

675 cov.stop() 

676 """, 

677 ) 

678 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

679 "goodbye.py", 

680 """\ 

681 def f(): 

682 return "Goodbye!" 

683 """, 

684 ) 

685 goodbye_path = os.path.abspath("goodbye.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

686 

687 out = self.run_command("python hello.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

688 assert "Goodbye!" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

689 

690 msg = ( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

691 f"CoverageWarning: Already imported a file that will be measured: {goodbye_path} " 

692 + "(already-imported)" 

693 ) 

694 assert msg in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

695 

696 # Pypy passes locally, but fails in CI? Perhaps the version of macOS is 

697 # significant? https://foss.heptapod.net/pypy/pypy/-/issues/3074 

698 @pytest.mark.skipif(env.PYPY, reason="PyPy is unreliable with this test") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

699 def test_lang_c(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

700 # LANG=C forces getfilesystemencoding on Linux to 'ascii', which causes 

701 # failures with non-ascii file names. We don't want to make a real file 

702 # with strange characters, though, because that gets the test runners 

703 # tangled up. This will isolate the concerns to the coverage.py code. 

704 # https://github.com/coveragepy/coveragepy/issues/533 

705 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

706 "weird_file.py", 

707 r""" 

708 globs = {} 

709 code = "a = 1\nb = 2\n" 

710 exec(compile(code, "wut\xe9\xea\xeb\xec\x01\x02.py", 'exec'), globs) 

711 print(globs['a']) 

712 print(globs['b']) 

713 """, 

714 ) 

715 self.set_environ("LANG", "C") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

716 out = self.run_command("coverage run weird_file.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

717 assert out == "1\n2\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

718 

719 def test_deprecation_warnings(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

720 # Test that coverage doesn't trigger deprecation warnings. 

721 # https://github.com/coveragepy/coveragepy/issues/305 

722 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

723 "allok.py", 

724 """\ 

725 import warnings 

726 warnings.simplefilter('default') 

727 import coverage 

728 print("No warnings!") 

729 """, 

730 ) 

731 

732 # Some of our testing infrastructure can issue warnings. 

733 # Turn it all off for the subprocess. 

734 self.del_environ("COVERAGE_TESTING") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

735 

736 out = self.run_command("python allok.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

737 assert out == "No warnings!\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

738 

739 def test_run_twice(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

740 # https://github.com/coveragepy/coveragepy/issues/353 

741 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

742 "foo.py", 

743 """\ 

744 def foo(): 

745 pass 

746 """, 

747 ) 

748 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

749 "run_twice.py", 

750 """\ 

751 import sys 

752 import coverage 

753 

754 for i in [1, 2]: 

755 sys.stderr.write(f"Run {i}\\n") 

756 inst = coverage.Coverage(source=['foo']) 

757 inst.load() 

758 inst.start() 

759 import foo 

760 inst.stop() 

761 inst.save() 

762 """, 

763 ) 

764 out = self.run_command("python run_twice.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

765 # Remove the file location and source line from the warning. 

766 out = re.sub(r"(?m)^[\\/\w.:~_-]+:\d+: CoverageWarning: ", "f:d: CoverageWarning: ", out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

767 out = re.sub(r"(?m)^\s+self.warn.*$\n", "", out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

768 out = re.sub(r"; see https://.*$", "", out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

769 expected = ( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

770 "Run 1\n" 

771 + "Run 2\n" 

772 + "f:d: CoverageWarning: Module foo was previously imported, but not measured " 

773 + "(module-not-measured)\n" 

774 ) 

775 assert out == expected 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

776 

777 def test_module_name(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

778 # https://github.com/coveragepy/coveragepy/issues/478 

779 # Make sure help doesn't show a silly command name when run as a 

780 # module, like it used to: 

781 # $ python -m coverage 

782 # Code coverage for Python. Use '__main__.py help' for help. 

783 out = self.run_command("python -m coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

784 assert "Use 'coverage help' for help" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

785 

786 @pytest.mark.skipif(env.WINDOWS, reason="This test is not for Windows") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

787 def test_save_signal_usr1(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

788 self.assert_doesnt_exist(".coverage") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

789 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

790 "dummy_hello.py", 

791 """\ 

792 import os 

793 import signal 

794 

795 print(f"Sending SIGUSR1 to myself") 

796 os.kill(os.getpid(), signal.SIGUSR1) 

797 os.kill(os.getpid(), signal.SIGKILL) 

798 

799 print("Done and goodbye") 

800 """, 

801 ) 

802 out = self.run_command( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

803 "coverage run --save-signal=USR1 dummy_hello.py", 

804 status=-signal.SIGKILL, 

805 ) 

806 # `startswith` because on Linux it also prints "Killed" 

807 assert out.startswith("Sending SIGUSR1 to myself\nSaving coverage data...\n") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

808 self.assert_exists(".coverage") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

809 out = self.run_command("coverage report -m") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

810 assert out == textwrap.dedent("""\ 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

811 Name Stmts Miss Cover Missing 

812 ---------------------------------------------- 

813 dummy_hello.py 6 2 67% 6-8 

814 ---------------------------------------------- 

815 TOTAL 6 2 67% 

816 """) 

817 

818 

819TRY_EXECFILE = os.path.join(os.path.dirname(__file__), "modules/process_test/try_execfile.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

820 

821 

822class EnvironmentTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

823 """Tests using try_execfile.py to test the execution environment.""" 

824 

825 def assert_tryexecfile_output(self, expected: str, actual: str) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

826 """Assert that the output we got is a successful run of try_execfile.py. 

827 

828 `expected` and `actual` must be the same. 

829 

830 """ 

831 # First, is this even credible try_execfile.py output? 

832 assert '"DATA": "xyzzy"' in actual 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

833 # If this fails, "+" is actual, and "-" is expected 

834 assert actual == expected 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

835 

836 def test_coverage_run_is_like_python(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

837 with open(TRY_EXECFILE, encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

838 self.make_file("run_me.py", f.read()) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

839 expected = self.run_command("python run_me.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

840 actual = self.run_command("coverage run run_me.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

841 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

842 

843 def test_coverage_run_far_away_is_like_python(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

844 with open(TRY_EXECFILE, encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

845 self.make_file("sub/overthere/prog.py", f.read()) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

846 expected = self.run_command("python sub/overthere/prog.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

847 actual = self.run_command("coverage run sub/overthere/prog.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

848 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

849 

850 @pytest.mark.skipif(not env.WINDOWS, reason="This is about Windows paths") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

851 def test_coverage_run_far_away_is_like_python_windows(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

852 with open(TRY_EXECFILE, encoding="utf-8") as f: 1'(UVWXYZ0123456789!#$%)

853 self.make_file("sub/overthere/prog.py", f.read()) 1'(UVWXYZ0123456789!#$%)

854 expected = self.run_command("python sub\\overthere\\prog.py") 1'(UVWXYZ0123456789!#$%)

855 actual = self.run_command("coverage run sub\\overthere\\prog.py") 1'(UVWXYZ0123456789!#$%)

856 self.assert_tryexecfile_output(expected, actual) 1'(UVWXYZ0123456789!#$%)

857 

858 def test_coverage_run_dashm_is_like_python_dashm(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

859 self.add_test_modules_to_pythonpath() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

860 expected = self.run_command("python -m process_test.try_execfile") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

861 actual = self.run_command("coverage run -m process_test.try_execfile") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

862 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

863 

864 def test_coverage_run_dir_is_like_python_dir(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

865 with open(TRY_EXECFILE, encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

866 self.make_file("with_main/__main__.py", f.read()) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

867 

868 expected = self.run_command("python with_main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

869 actual = self.run_command("coverage run with_main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

870 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

871 

872 def test_coverage_run_dashm_dir_no_init_is_like_python(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

873 with open(TRY_EXECFILE, encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

874 self.make_file("with_main/__main__.py", f.read()) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

875 

876 expected = self.run_command("python -m with_main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

877 actual = self.run_command("coverage run -m with_main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

878 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

879 

880 def test_coverage_run_dashm_dir_with_init_is_like_python(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

881 with open(TRY_EXECFILE, encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

882 self.make_file("with_main/__main__.py", f.read()) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

883 self.make_file("with_main/__init__.py", "") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

884 

885 expected = self.run_command("python -m with_main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

886 actual = self.run_command("coverage run -m with_main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

887 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

888 

889 def test_coverage_run_dashm_equal_to_doubledashsource(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

890 """regression test for #328 

891 

892 When imported by -m, a module's __name__ is __main__, but we need the 

893 --source machinery to know and respect the original name. 

894 """ 

895 self.add_test_modules_to_pythonpath() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

896 expected = self.run_command("python -m process_test.try_execfile") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

897 actual = self.run_command( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

898 "coverage run --source process_test.try_execfile -m process_test.try_execfile", 

899 ) 

900 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

901 

902 def test_coverage_run_dashm_superset_of_doubledashsource(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

903 """Edge case: --source foo -m foo.bar""" 

904 # Ugh: without this config file, we'll get a warning about 

905 # CoverageWarning: Module process_test was previously imported, 

906 # but not measured (module-not-measured) 

907 # 

908 # This is because process_test/__init__.py is imported while looking 

909 # for process_test.try_execfile. That import happens while setting 

910 # sys.path before start() is called. 

911 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

912 ".coveragerc", 

913 """\ 

914 [run] 

915 disable_warnings = module-not-measured 

916 """, 

917 ) 

918 self.add_test_modules_to_pythonpath() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

919 expected = self.run_command("python -m process_test.try_execfile") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

920 actual = self.run_command( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

921 "coverage run --source process_test -m process_test.try_execfile", 

922 ) 

923 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

924 

925 st, out = self.run_command_status("coverage report") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

926 assert st == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

927 assert self.line_count(out) == 6, out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

928 

929 def test_coverage_run_script_imports_doubledashsource(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

930 # This file imports try_execfile, which compiles it to .pyc, so the 

931 # first run will have __file__ == "try_execfile.py" and the second will 

932 # have __file__ == "try_execfile.pyc", which throws off the comparison. 

933 # Setting dont_write_bytecode True stops the compilation to .pyc and 

934 # keeps the test working. 

935 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

936 "myscript", 

937 """\ 

938 import sys; sys.dont_write_bytecode = True 

939 import process_test.try_execfile 

940 """, 

941 ) 

942 

943 self.add_test_modules_to_pythonpath() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

944 expected = self.run_command("python myscript") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

945 actual = self.run_command("coverage run --source process_test myscript") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

946 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

947 

948 st, out = self.run_command_status("coverage report") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

949 assert st == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

950 assert self.line_count(out) == 6, out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

951 

952 def test_coverage_run_dashm_is_like_python_dashm_off_path(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

953 # https://github.com/coveragepy/coveragepy/issues/242 

954 self.make_file("sub/__init__.py", "") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

955 with open(TRY_EXECFILE, encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

956 self.make_file("sub/run_me.py", f.read()) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

957 

958 expected = self.run_command("python -m sub.run_me") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

959 actual = self.run_command("coverage run -m sub.run_me") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

960 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

961 

962 def test_coverage_run_dashm_is_like_python_dashm_with__main__207(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

963 # https://github.com/coveragepy/coveragepy/issues/207 

964 self.make_file("package/__init__.py", "print('init')") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

965 self.make_file("package/__main__.py", "print('main')") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

966 expected = self.run_command("python -m package") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

967 actual = self.run_command("coverage run -m package") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

968 assert expected == actual 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

969 

970 def test_coverage_zip_is_like_python(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

971 # Test running coverage from a zip file itself. Some environments 

972 # (windows?) zip up the coverage main to be used as the coverage 

973 # command. 

974 with open(TRY_EXECFILE, encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

975 self.make_file("run_me.py", f.read()) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

976 expected = self.run_command("python run_me.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

977 cov_main = os.path.join(TESTS_DIR, "covmain.zip") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

978 actual = self.run_command(f"python {cov_main} run run_me.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

979 self.assert_tryexecfile_output(expected, actual) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

980 

981 @pytest.mark.skipif(env.PYVERSION < (3, 11), reason="PYTHONSAFEPATH is new in 3.11") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

982 @pytest.mark.skipif( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

983 env.WINDOWS, 

984 reason="Windows gets this wrong: https://github.com/python/cpython/issues/131484", 

985 ) 

986 def test_pythonsafepath(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

987 with open(TRY_EXECFILE, encoding="utf-8") as f: 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO

988 self.make_file("run_me.py", f.read()) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO

989 self.set_environ("PYTHONSAFEPATH", "1") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO

990 expected = self.run_command("python run_me.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO

991 actual = self.run_command("coverage run run_me.py") 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO

992 self.assert_tryexecfile_output(expected, actual) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO

993 

994 @pytest.mark.skipif(env.PYVERSION < (3, 11), reason="PYTHONSAFEPATH is new in 3.11") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

995 def test_pythonsafepath_dashm_runme(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

996 with open(TRY_EXECFILE, encoding="utf-8") as f: 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

997 self.make_file("run_me.py", f.read()) 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

998 self.set_environ("PYTHONSAFEPATH", "1") 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

999 expected = self.run_command("python run_me.py") 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1000 actual = self.run_command("python -m coverage run run_me.py") 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1001 self.assert_tryexecfile_output(expected, actual) 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1002 

1003 @pytest.mark.skipif(env.PYVERSION < (3, 11), reason="PYTHONSAFEPATH is new in 3.11") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1004 def test_pythonsafepath_dashm(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1005 with open(TRY_EXECFILE, encoding="utf-8") as f: 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1006 self.make_file("with_main/__main__.py", f.read()) 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1007 

1008 self.set_environ("PYTHONSAFEPATH", "1") 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1009 expected = self.run_command("python -m with_main", status=1) 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1010 actual = self.run_command("coverage run -m with_main", status=1) 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1011 assert re.search("No module named '?with_main'?", actual) 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1012 assert re.search("No module named '?with_main'?", expected) 1abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%O)

1013 

1014 def test_coverage_custom_script(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1015 # https://github.com/coveragepy/coveragepy/issues/678 

1016 # If sys.path[0] isn't the Python default, then coverage.py won't 

1017 # fiddle with it. 

1018 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1019 "a/b/c/thing.py", 

1020 """\ 

1021 SOMETHING = "hello-xyzzy" 

1022 """, 

1023 ) 

1024 abc = os.path.abspath("a/b/c") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1025 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1026 "run_coverage.py", 

1027 f"""\ 

1028 import sys 

1029 sys.path[0:0] = [ 

1030 r'{abc}', 

1031 '/Users/somebody/temp/something/eggs/something-4.5.1-py2.7-xxx-10.13-x86_64.egg', 

1032 ] 

1033 

1034 import coverage.cmdline 

1035 

1036 if __name__ == '__main__': 

1037 sys.exit(coverage.cmdline.main()) 

1038 """, 

1039 ) 

1040 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1041 "how_is_it.py", 

1042 """\ 

1043 import pprint, sys 

1044 pprint.pprint(sys.path) 

1045 import thing 

1046 print(thing.SOMETHING) 

1047 """, 

1048 ) 

1049 # If this test fails, it will be with "can't import thing". 

1050 out = self.run_command("python run_coverage.py run how_is_it.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1051 assert "hello-xyzzy" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1052 

1053 out = self.run_command("python -m run_coverage run how_is_it.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1054 assert "hello-xyzzy" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1055 

1056 @pytest.mark.skipif(env.WINDOWS, reason="Windows can't make symlinks") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1057 @pytest.mark.skipif( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1058 platform.python_version().endswith("+"), 

1059 reason="setuptools barfs on dev versions: https://github.com/pypa/packaging/issues/678", 

1060 # https://github.com/coveragepy/coveragepy/issues/1556 

1061 ) 

1062 def test_bug_862(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1063 # This used to simulate how pyenv and pyenv-virtualenv create the 

1064 # coverage executable. Now the code shows how venv does it. 

1065 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1066 "elsewhere/bin/fake-coverage", 

1067 f"""\ 

1068 #!{sys.executable} 

1069 import re 

1070 import sys 

1071 from coverage.cmdline import main 

1072 if __name__ == '__main__': 

1073 sys.argv[0] = re.sub(r'(-script\\.pyw|\\.exe)?$', '', sys.argv[0]) 

1074 sys.exit(main()) 

1075 """, 

1076 ) 

1077 os.chmod("elsewhere/bin/fake-coverage", stat.S_IREAD | stat.S_IEXEC) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1078 os.symlink("elsewhere", "somewhere") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1079 self.make_file("foo.py", "print('inside foo')") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1080 self.make_file("bar.py", "import foo") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1081 out = self.run_command("somewhere/bin/fake-coverage run bar.py") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1082 assert "inside foo\n" == out 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1083 

1084 def test_bug_909(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1085 # https://github.com/coveragepy/coveragepy/issues/909 

1086 # The __init__ files were being imported before measurement started, 

1087 # so the line in __init__.py was being marked as missed, and there were 

1088 # warnings about measured files being imported before start. 

1089 self.make_file("proj/__init__.py", "print('Init')") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1090 self.make_file("proj/thecode.py", "print('The code')") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1091 self.make_file("proj/tests/__init__.py", "") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1092 self.make_file("proj/tests/test_it.py", "import proj.thecode") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1093 

1094 expected = "Init\nThe code\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1095 actual = self.run_command("coverage run --source=proj -m proj.tests.test_it") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1096 assert expected == actual 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1097 

1098 report = self.run_command("coverage report -m") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1099 

1100 # Name Stmts Miss Cover Missing 

1101 # ------------------------------------------------------ 

1102 # proj/__init__.py 1 0 100% 

1103 # proj/tests/__init__.py 0 0 100% 

1104 # proj/tests/test_it.py 1 0 100% 

1105 # proj/thecode.py 1 0 100% 

1106 # ------------------------------------------------------ 

1107 # TOTAL 3 0 100% 

1108 

1109 squeezed = self.squeezed_lines(report) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1110 assert squeezed[2].replace("\\", "/") == "proj/__init__.py 1 0 100%" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1111 

1112 

1113class ExcepthookTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1114 """Tests of sys.excepthook support.""" 

1115 

1116 # TODO: do we need these as process tests if we have test_execfile.py:RunFileTest? 

1117 

1118 def test_excepthook(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1119 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1120 "excepthook.py", 

1121 """\ 

1122 import sys 

1123 

1124 def excepthook(*args): 

1125 print('in excepthook') 

1126 if maybe == 2: 

1127 print('definitely') 

1128 

1129 sys.excepthook = excepthook 

1130 

1131 maybe = 1 

1132 raise RuntimeError('Error Outside') 

1133 """, 

1134 ) 

1135 cov_st, cov_out = self.run_command_status("coverage run excepthook.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1136 py_st, py_out = self.run_command_status("python excepthook.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1137 assert cov_st == py_st 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1138 assert cov_st == 1 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1139 assert "in excepthook" in py_out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1140 assert cov_out == py_out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1141 

1142 # Read the coverage file and see that excepthook.py has 7 lines 

1143 # executed. 

1144 data = coverage.CoverageData() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1145 data.read() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1146 assert line_counts(data)["excepthook.py"] == 7 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1147 

1148 @pytest.mark.skipif( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1149 not env.CPYTHON, 

1150 reason="non-CPython handles excepthook exits differently, punt for now.", 

1151 ) 

1152 def test_excepthook_exit(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1153 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1154 "excepthook_exit.py", 

1155 """\ 

1156 import sys 

1157 

1158 def excepthook(*args): 

1159 print('in excepthook') 

1160 sys.exit(0) 

1161 

1162 sys.excepthook = excepthook 

1163 

1164 raise RuntimeError('Error Outside') 

1165 """, 

1166 ) 

1167 cov_st, cov_out = self.run_command_status("coverage run excepthook_exit.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1168 py_st, py_out = self.run_command_status("python excepthook_exit.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1169 assert cov_st == py_st 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1170 assert cov_st == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1171 

1172 assert py_out == "in excepthook\n" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1173 assert cov_out == py_out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1174 

1175 @pytest.mark.skipif(env.PYPY, reason="PyPy handles excepthook throws differently.") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1176 def test_excepthook_throw(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1177 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1178 "excepthook_throw.py", 

1179 """\ 

1180 import sys 

1181 

1182 def excepthook(*args): 

1183 # Write this message to stderr so that we don't have to deal 

1184 # with interleaved stdout/stderr comparisons in the assertions 

1185 # in the test. 

1186 sys.stderr.write('in excepthook\\n') 

1187 raise RuntimeError('Error Inside') 

1188 

1189 sys.excepthook = excepthook 

1190 

1191 raise RuntimeError('Error Outside') 

1192 """, 

1193 ) 

1194 cov_out = self.run_command("coverage run excepthook_throw.py", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1195 py_out = self.run_command("python excepthook_throw.py", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1196 assert "in excepthook" in py_out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1197 assert cov_out == py_out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%

1198 

1199 

1200class AliasedCommandTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1201 """Tests of the version-specific command aliases.""" 

1202 

1203 run_in_temp_dir = False 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1204 

1205 def test_major_version_works(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1206 # "coverage3" works on py3 

1207 cmd = "coverage%d" % sys.version_info[0] 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1208 out = self.run_command(cmd) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1209 assert "Code coverage for Python" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1210 

1211 def test_wrong_alias_doesnt_work(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1212 # "coverage2" doesn't work on py3 

1213 assert sys.version_info[0] == 3 # Let us know when Python 4 is out... 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1214 badcmd = "coverage2" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1215 status, out = self.run_command_status(badcmd) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1216 assert "Code coverage for Python" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1217 assert status != 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1218 

1219 def test_specific_alias_works(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1220 # "coverage-3.9" works on py3.9 

1221 cmd = "coverage-%d.%d" % sys.version_info[:2] 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1222 out = self.run_command(cmd) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1223 assert "Code coverage for Python" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1224 

1225 @pytest.mark.parametrize( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1226 "cmd", 

1227 [ 

1228 "coverage", 

1229 "coverage%d" % sys.version_info[0], 

1230 "coverage-%d.%d" % sys.version_info[:2], 

1231 ], 

1232 ) 

1233 def test_aliases_used_in_messages(self, cmd: str) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1234 out = self.run_command(f"{cmd} foobar", status=1) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1235 assert "Unknown command: 'foobar'" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1236 assert f"Use '{cmd} help' for help" in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1237 

1238 

1239class PydocTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1240 """Test that pydoc can get our information.""" 

1241 

1242 run_in_temp_dir = False 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1243 

1244 def assert_pydoc_ok(self, name: str, thing: Any) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1245 """Check that pydoc of `name` finds the docstring from `thing`.""" 

1246 # Run pydoc. 

1247 out = self.run_command("python -m pydoc " + name) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1248 # It should say "Help on..", and not have a traceback 

1249 assert out.startswith("Help on ") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1250 assert "Traceback" not in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1251 

1252 # All of the lines in the docstring should be there somewhere. 

1253 for line in thing.__doc__.splitlines(): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1254 assert line.strip() in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1255 

1256 def test_pydoc_coverage(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1257 self.assert_pydoc_ok("coverage", coverage) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1258 

1259 def test_pydoc_coverage_coverage(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1260 self.assert_pydoc_ok("coverage.Coverage", coverage.Coverage) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1261 

1262 

1263class FailUnderTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1264 """Tests of the --fail-under switch.""" 

1265 

1266 def setUp(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1267 super().setUp() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1268 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1269 "forty_two_plus.py", 

1270 """\ 

1271 # I have 42.857% (3/7) coverage! 

1272 a = 1 

1273 b = 2 

1274 if a > 3: 

1275 b = 4 

1276 c = 5 

1277 d = 6 

1278 e = 7 

1279 """, 

1280 ) 

1281 self.make_data_file(lines={abs_file("forty_two_plus.py"): [2, 3, 4]}) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1282 

1283 def test_report_43_is_ok(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1284 st, out = self.run_command_status("coverage report --fail-under=43") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1285 assert st == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1286 assert self.last_line_squeezed(out) == "TOTAL 7 4 43%" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1287 

1288 def test_report_43_is_not_ok(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1289 st, out = self.run_command_status("coverage report --fail-under=44") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1290 assert st == 2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1291 expected = "Coverage failure: total of 43 is less than fail-under=44" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1292 assert expected == self.last_line_squeezed(out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1293 

1294 def test_report_42p86_is_not_ok(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1295 self.make_file(".coveragerc", "[report]\nprecision = 2") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1296 st, out = self.run_command_status("coverage report --fail-under=42.88") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1297 assert st == 2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1298 expected = "Coverage failure: total of 42.86 is less than fail-under=42.88" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1299 assert expected == self.last_line_squeezed(out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1300 

1301 def test_report_99p9_is_not_ok(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1302 # A file with 99.9% coverage: 

1303 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1304 "ninety_nine_plus.py", 

1305 "a = 1\n" + "b = 2\n" * 2000 + "if a > 3:\n" + " c = 4\n", 

1306 ) 

1307 self.make_data_file(lines={abs_file("ninety_nine_plus.py"): range(1, 2002)}) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1308 st, out = self.run_command_status("coverage report --fail-under=100") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1309 assert st == 2 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1310 expected = "Coverage failure: total of 99 is less than fail-under=100" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1311 assert expected == self.last_line_squeezed(out) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1312 

1313 

1314class FailUnderNoFilesTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1315 """Test that nothing to report results in an error exit status.""" 

1316 

1317 def test_report(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1318 self.make_file(".coveragerc", "[report]\nfail_under = 99\n") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1319 st, out = self.run_command_status("coverage report") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1320 assert "No data to report." in out 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1321 assert st == 1 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1322 

1323 

1324class FailUnderEmptyFilesTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1325 """Test that empty files produce the proper fail_under exit status.""" 

1326 

1327 def test_report(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1328 self.make_file(".coveragerc", "[report]\nfail_under = 99\n") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1329 self.make_file("empty.py", "") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1330 st, _ = self.run_command_status("coverage run empty.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1331 assert st == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1332 st, _ = self.run_command_status("coverage report") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1333 # An empty file is marked as 100% covered, so this is ok. 

1334 assert st == 0 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1335 

1336 

1337@pytest.mark.skipif(env.WINDOWS, reason="Windows can't delete the directory in use.") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1338class YankedDirectoryTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1339 """Tests of what happens when the current directory is deleted.""" 

1340 

1341 BUG_806 = """\ 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1342 import os 

1343 import sys 

1344 import tempfile 

1345 

1346 tmpdir = tempfile.mkdtemp() 

1347 os.chdir(tmpdir) 

1348 os.rmdir(tmpdir) 

1349 print(sys.argv[1]) 

1350 """ 

1351 

1352 def test_removing_directory(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1353 self.make_file("bug806.py", self.BUG_806) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1354 out = self.run_command("coverage run bug806.py noerror") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1355 assert out == "noerror\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1356 

1357 def test_removing_directory_with_error(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1358 self.make_file("bug806.py", self.BUG_806) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1359 out = self.run_command("coverage run bug806.py", status=1) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1360 path = python_reported_file("bug806.py") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1361 # Python 3.11 adds an extra line to the traceback. 

1362 # Check that the lines we expect are there. 

1363 lines = textwrap.dedent(f"""\ 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1364 Traceback (most recent call last): 

1365 File "{path}", line 8, in <module> 

1366 print(sys.argv[1]) 

1367 IndexError: list index out of range 

1368 """).splitlines(keepends=True) 

1369 assert all(line in out for line in lines) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1370 

1371 

1372@pytest.mark.skipif(env.METACOV, reason="Can't test subprocess pth file during metacoverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1373@pytest.mark.xdist_group(name="needs_pth") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1374class ProcessStartupTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1375 """Test that we can measure coverage in subprocesses.""" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1376 

1377 def make_main_and_sub(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1378 """Create main.py and sub.py.""" 

1379 # Main will run sub.py 

1380 self.make_file( 

1381 "main.py", 

1382 """\ 

1383 import os, os.path, sys 

1384 ex = os.path.basename(sys.executable) 

1385 os.system(ex + " sub.py") 

1386 """, 

1387 ) 

1388 # sub.py will write a few lines. 

1389 self.make_file( 

1390 "sub.py", 

1391 """\ 

1392 with open("out.txt", "w", encoding="utf-8") as f: 

1393 f.write("Hello, world!\\n") 

1394 a = 3 

1395 """, 

1396 ) 

1397 

1398 def test_patch_subprocess(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1399 self.make_main_and_sub() 

1400 self.make_file( 

1401 ".coveragerc", 

1402 """\ 

1403 [run] 

1404 patch = subprocess 

1405 """, 

1406 ) 

1407 self.run_command("coverage run main.py") 

1408 self.run_command("coverage combine") 

1409 self.assert_exists(".coverage") 

1410 data = coverage.CoverageData() 

1411 data.read() 

1412 assert line_counts(data)["main.py"] == 3 

1413 assert line_counts(data)["sub.py"] == 3 

1414 

1415 def test_subprocess_with_pth_files(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1416 # An existing data file should not be read when a subprocess gets 

1417 # measured automatically. Create the data file here with bogus data in 

1418 # it. 

1419 self.make_main_and_sub() 

1420 data = coverage.CoverageData(".mycovdata") 

1421 data.add_lines({os.path.abspath("sub.py"): range(100)}) 

1422 data.write() 

1423 

1424 self.make_file( 

1425 "coverage.ini", 

1426 """\ 

1427 [run] 

1428 data_file = .mycovdata 

1429 """, 

1430 ) 

1431 self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") 

1432 import main # pylint: disable=unused-import, import-error 

1433 

1434 with open("out.txt", encoding="utf-8") as f: 

1435 assert f.read() == "Hello, world!\n" 

1436 

1437 # Read the data from .coverage 

1438 self.assert_exists(".mycovdata") 

1439 data = coverage.CoverageData(".mycovdata") 

1440 data.read() 

1441 assert line_counts(data)["sub.py"] == 3 

1442 

1443 def test_subprocess_with_pth_files_and_parallel(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1444 # https://github.com/coveragepy/coveragepy/issues/492 

1445 self.make_main_and_sub() 

1446 self.make_file( 

1447 "coverage.ini", 

1448 """\ 

1449 [run] 

1450 parallel = true 

1451 """, 

1452 ) 

1453 

1454 self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") 

1455 self.run_command("coverage run main.py") 

1456 

1457 with open("out.txt", encoding="utf-8") as f: 

1458 assert f.read() == "Hello, world!\n" 

1459 

1460 self.run_command("coverage combine") 

1461 

1462 # assert that the combined .coverage data file is correct 

1463 self.assert_exists(".coverage") 

1464 data = coverage.CoverageData() 

1465 data.read() 

1466 assert line_counts(data)["sub.py"] == 3 

1467 

1468 # assert that there are *no* extra data files left over after a combine 

1469 data_files = glob.glob(os.getcwd() + "/.coverage*") 

1470 msg = ( 

1471 "Expected only .coverage after combine, looks like there are " 

1472 + f"extra data files that were not cleaned up: {data_files!r}" 

1473 ) 

1474 assert len(data_files) == 1, msg 

1475 

1476 def test_subprocess_in_directories(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1477 # Bug 2025: patch=subprocess didn't find data files from subdirectory 

1478 # subprocesses. 

1479 self.make_file( 

1480 "main.py", 

1481 """\ 

1482 import subprocess 

1483 import sys 

1484 print(subprocess.check_output( 

1485 [sys.executable, "subproc.py"], 

1486 cwd="subdir", 

1487 encoding="utf-8", 

1488 )) 

1489 """, 

1490 ) 

1491 self.make_file( 

1492 "subdir/subproc.py", 

1493 """\ 

1494 with open("readme.txt", encoding="utf-8") as f: 

1495 print(f.read(), end="") 

1496 """, 

1497 ) 

1498 self.make_file( 

1499 ".coveragerc", 

1500 """\ 

1501 [run] 

1502 patch = subprocess 

1503 data_file = .covdata 

1504 """, 

1505 ) 

1506 self.make_file("subdir/readme.txt", "hello") 

1507 out = self.run_command("coverage run main.py") 

1508 assert out == "hello\n" 

1509 self.run_command("coverage combine") 

1510 data = coverage.CoverageData(".covdata") 

1511 data.read() 

1512 assert line_counts(data)["main.py"] == 6 

1513 assert line_counts(data)["subproc.py"] == 2 

1514 

1515 @pytest.mark.skipif( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1516 not testenv.CAN_MEASURE_BRANCHES, reason="Can't measure branches with this core" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1517 ) 

1518 def test_subprocess_gets_nonfile_config(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1519 # https://github.com/coveragepy/coveragepy/issues/2021 

1520 self.make_file( 

1521 "subfunctions.py", 

1522 """\ 

1523 import subprocess, sys 

1524 

1525 def f1(): 

1526 print("function 1") 

1527 def f2(): 

1528 print("function 2") 

1529 

1530 functions = [f1, f2] 

1531 

1532 cases = sys.argv[1:] 

1533 if len(cases) > 1: 

1534 for c in cases: 

1535 subprocess.call([sys.executable, __file__, c]) 

1536 else: 

1537 functions[int(cases[0])]() 

1538 """, 

1539 ) 

1540 self.make_file( 

1541 ".coveragerc", 

1542 """\ 

1543 [run] 

1544 patch = subprocess 

1545 """, 

1546 ) 

1547 out = self.run_command("coverage run --branch subfunctions.py 0 1") 

1548 assert out.endswith("function 1\nfunction 2\n") 

1549 self.run_command("coverage combine") 

1550 data = coverage.CoverageData() 

1551 data.read() 

1552 assert line_counts(data)["subfunctions.py"] == 11 

1553 

1554 def test_subprocess_dir_with_source(self) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1555 # https://github.com/coveragepy/coveragepy/issues/1499 

1556 self.make_file("main/d/README", "A sub-directory") 

1557 self.make_file( 

1558 "main/main.py", 

1559 """\ 

1560 import os, subprocess, sys 

1561 orig = os.getcwd() 

1562 os.chdir("./d") 

1563 subprocess.run([sys.executable, f"{orig}/sub.py"]) 

1564 os.chdir(orig) 

1565 """, 

1566 ) 

1567 self.make_file("lib/other.py", "print('Other', flush=True)") 

1568 self.make_file( 

1569 "main/sub.py", 

1570 """ 

1571 import other 

1572 print("Hello, world!", flush=True) 

1573 """, 

1574 ) 

1575 self.make_file( 

1576 "main/pyproject.toml", 

1577 """\ 

1578 [tool.coverage.run] 

1579 patch = ["subprocess"] 

1580 source = [".", "other"] 

1581 disable_warnings = ["module-not-imported"] 

1582 """, 

1583 ) 

1584 self.set_environ("PYTHONPATH", os.path.abspath("lib")) 

1585 with change_dir("main"): 

1586 out = self.run_command("coverage run main.py") 

1587 assert out == "Other\nHello, world!\n" 

1588 self.run_command("coverage combine") 

1589 data = coverage.CoverageData() 

1590 data.read() 

1591 assert line_counts(data) == {"main.py": 5, "sub.py": 2, "other.py": 1} 

1592 

1593 

1594@pytest.mark.skipif(env.WINDOWS, reason="patch=execv isn't supported on Windows") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1595@pytest.mark.xdist_group(name="needs_pth") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1596class ExecvTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1597 """Test that we can measure coverage in subprocesses.""" 

1598 

1599 @pytest.mark.parametrize( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1600 "fname", 

1601 [ 

1602 base + suffix 

1603 for base, suffix in itertools.product( 

1604 ["exec", "spawn"], 

1605 ["l", "le", "lp", "lpe", "v", "ve", "vp", "vpe"], 

1606 ) 

1607 ], 

1608 ) 

1609 def test_execv_patch(self, fname: str) -> None: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1610 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1611 ".coveragerc", 

1612 """\ 

1613 [run] 

1614 patch = subprocess, execv 

1615 """, 

1616 ) 

1617 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1618 "main.py", 

1619 f"""\ 

1620 import os, sys 

1621 print("In main") 

1622 args = [] 

1623 if "spawn" in {fname!r}: 

1624 args.append(os.P_WAIT) 

1625 args.append(sys.executable) 

1626 prog_args = ["python", {os.path.abspath("other.py")!r}, "cat", "dog"] 

1627 if "l" in {fname!r}: 

1628 args.extend(prog_args) 

1629 else: 

1630 args.append(prog_args) 

1631 if {fname!r}.endswith("e"): 

1632 args.append({{"SUBVAR": "the-sub-var"}}) 

1633 os.environ["MAINVAR"] = "the-main-var" 

1634 sys.stdout.flush() 

1635 os.{fname}(*args) 

1636 """, 

1637 ) 

1638 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1639 "other.py", 

1640 """\ 

1641 import os, sys 

1642 print(f"MAINVAR = {os.getenv('MAINVAR', 'none')}") 

1643 print(f"SUBVAR = {os.getenv('SUBVAR', 'none')}") 

1644 print(f"{sys.argv[1:] = }") 

1645 """, 

1646 ) 

1647 

1648 out = self.run_command("coverage run main.py") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1649 expected = "In main\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1650 if fname.endswith("e"): 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1651 expected += "MAINVAR = none\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1652 expected += "SUBVAR = the-sub-var\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1653 else: 

1654 expected += "MAINVAR = the-main-var\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1655 expected += "SUBVAR = none\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1656 expected += "sys.argv[1:] = ['cat', 'dog']\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1657 assert out == expected 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1658 

1659 self.run_command("coverage combine") 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1660 data = coverage.CoverageData() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1661 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1662 

1663 main_lines = 12 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1664 if "spawn" in fname: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1665 main_lines += 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1666 if fname.endswith("e"): 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1667 main_lines += 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1668 

1669 assert line_counts(data)["main.py"] == main_lines 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1670 assert line_counts(data)["other.py"] == 4 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1671 

1672 

1673class ProcessStartupWithSourceTest(CoverageTest): 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1674 """Show that we can configure {[run]source} during process-level coverage. 

1675 

1676 There are three interesting variables, for a total of eight tests: 

1677 

1678 1. -m versus a simple script argument (for example, `python myscript`), 

1679 

1680 2. filtering for the top-level (main.py) or second-level (sub.py) 

1681 module, and 

1682 

1683 3. whether the files are in a package or not. 

1684 

1685 """ 

1686 

1687 @pytest.mark.parametrize("dashm", ["-m", ""]) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1688 @pytest.mark.parametrize("package", ["pkg", ""]) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1689 @pytest.mark.parametrize("source", ["main", "sub"]) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1690 @pytest.mark.xdist_group(name="needs_pth") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1691 def test_pth_and_source_work_together( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1692 self, 

1693 dashm: str, 

1694 package: str, 

1695 source: str, 

1696 ) -> None: 

1697 """Run the test for a particular combination of factors. 

1698 

1699 The arguments are all strings: 

1700 

1701 * `dashm`: Either "" (run the program as a file) or "-m" (run the 

1702 program as a module). 

1703 

1704 * `package`: Either "" (put the source at the top level) or a 

1705 package name to use to hold the source. 

1706 

1707 * `source`: Either "main" or "sub", which file to use as the 

1708 ``--source`` argument. 

1709 

1710 """ 

1711 

1712 def fullname(modname: str) -> str: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1713 """What is the full module name for `modname` for this test?""" 

1714 if package and dashm: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1715 return ".".join((package, modname)) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1716 else: 

1717 return modname 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1718 

1719 def path(basename: str) -> str: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1720 """Where should `basename` be created for this test?""" 

1721 return os.path.join(package, basename) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1722 

1723 # Main will run sub.py. 

1724 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1725 path("main.py"), 

1726 """\ 

1727 import %s 

1728 a = 2 

1729 b = 3 

1730 """ 

1731 % fullname("sub"), 

1732 ) 

1733 if package: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1734 self.make_file(path("__init__.py"), "") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1735 # sub.py will write a few lines. 

1736 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1737 path("sub.py"), 

1738 """\ 

1739 with open("out.txt", "w", encoding="utf-8") as f: 

1740 f.write("Hello, world!") 

1741 a = 3 

1742 """, 

1743 ) 

1744 self.make_file( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1745 "coverage.ini", 

1746 """\ 

1747 [run] 

1748 source = %s 

1749 """ 

1750 % fullname(source), 

1751 ) 

1752 

1753 self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1754 

1755 if dashm: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1756 cmd = "python -m %s" % fullname("main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1757 else: 

1758 cmd = "python %s" % path("main.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1759 

1760 self.run_command(cmd) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1761 

1762 with open("out.txt", encoding="utf-8") as f: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1763 assert f.read() == "Hello, world!" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1764 

1765 # Read the data from .coverage 

1766 self.assert_exists(".coverage") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1767 data = coverage.CoverageData() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1768 data.read() 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1769 summary = line_counts(data) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1770 assert summary[source + ".py"] == 3 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1771 assert len(summary) == 1 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)