Coverage for tests / test_process.py: 100.000%

620 statements  

« prev     ^ index     » next       coverage.py v7.12.1a0.dev1, created at 2025-11-29 20:34 +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 site 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

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

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

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

20 

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

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

23 

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

25 

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

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

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

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

30 

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

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

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

34 

35 

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

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

38 

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

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

41 "mycode.py", 

42 """\ 

43 h = "Hello" 

44 w = "world" 

45 """, 

46 ) 

47 

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

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

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

51 

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

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

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

55 "mycode.py", 

56 """\ 

57 import covmod1 

58 import covmodzip1 

59 a = 1 

60 print('done') 

61 """, 

62 ) 

63 

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

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

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

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

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

69 

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

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

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

73 "envornot.py", 

74 """\ 

75 import os 

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

77 """, 

78 ) 

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

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

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

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

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

84 # But `coverage run` does have it. 

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

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

87 

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

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

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

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

92 # Together, they run 8 lines. 

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

94 "b_or_c.py", 

95 """\ 

96 import sys 

97 a = 2 

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

99 b = 4 

100 else: 

101 c = 6 

102 c2 = 7 

103 d = 8 

104 print('done') 

105 """, 

106 ) 

107 

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

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

110 

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

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

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

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

115 

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

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

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

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

120 

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

122 # executed. 

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

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

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

126 

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

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

129 

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

131 ".coveragerc", 

132 """\ 

133 [run] 

134 data_file = .mycovdata 

135 """, 

136 ) 

137 

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

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

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

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

142 

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

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

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

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

147 

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

149 # executed. 

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

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

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

153 

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

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

156 

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

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

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

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

161 

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

163 # executed. 

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

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

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

167 

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

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

170 

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

172 ".coveragerc", 

173 """\ 

174 [run] 

175 source = . 

176 parallel = true 

177 """, 

178 ) 

179 

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

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

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

183 

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

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

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

187 

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

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

190 

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

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

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

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

195 

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

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

198 

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

200 # executed. 

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

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

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

204 

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

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

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

208 Name Stmts Miss Cover 

209 ------------------------------- 

210 b_or_c.py 8 0 100% 

211 ------------------------------- 

212 TOTAL 8 0 100% 

213 """) 

214 

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

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

217 "d1/x.py", 

218 """\ 

219 a = 1 

220 b = 2 

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

222 """, 

223 ) 

224 

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

226 "d2/x.py", 

227 """\ 

228 # 1 

229 # 2 

230 # 3 

231 c = 4 

232 d = 5 

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

234 """, 

235 ) 

236 

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

238 ".coveragerc", 

239 """\ 

240 [run] 

241 source = . 

242 parallel = True 

243 

244 [paths] 

245 source = 

246 src 

247 */d1 

248 */d2 

249 """, 

250 ) 

251 

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

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

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

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

256 

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

258 

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

260 

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

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

263 

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

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

266 

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

268 # files have been combined together. 

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

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

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

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

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

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

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

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

277 

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

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

280 ".coveragerc", 

281 """\ 

282 [run] 

283 data_file = data.dat 

284 parallel = True 

285 """, 

286 ) 

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

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

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

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

291 

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

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

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

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

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

297 

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

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

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

301 "fleeting.py", 

302 """\ 

303 s = 'goodbye, cruel world!' 

304 """, 

305 ) 

306 

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

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

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

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

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

312 

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

314 # files, so try again with no extension. 

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

316 "fleeting", 

317 """\ 

318 s = 'goodbye, cruel world!' 

319 """, 

320 ) 

321 

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

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

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

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

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

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

328 out, 

329 ) 

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

331 

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

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

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

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

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

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

338 

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

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

341 "throw.py", 

342 """\ 

343 class MyException(Exception): 

344 pass 

345 

346 def f1(): 

347 raise MyException("hey!") 

348 

349 def f2(): 

350 f1() 

351 

352 f2() 

353 """, 

354 ) 

355 

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

357 # same traceback. 

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

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

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

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

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

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

364 

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

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

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

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

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

370 

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

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

373 "exit.py", 

374 """\ 

375 import sys 

376 def f1(): 

377 print("about to exit..") 

378 sys.exit(17) 

379 

380 def f2(): 

381 f1() 

382 

383 f2() 

384 """, 

385 ) 

386 

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

388 # same output. No traceback. 

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

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

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

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

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

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

395 

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

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

398 "exit_none.py", 

399 """\ 

400 import sys 

401 def f1(): 

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

403 sys.exit() 

404 

405 f1() 

406 """, 

407 ) 

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

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

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

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

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

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

414 

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

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

417 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

418 "fork.py", 

419 """\ 

420 import os 

421 

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

423 ret = os.fork() 

424 

425 if ret == 0: 

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

427 else: 

428 os.waitpid(ret, 0) 

429 """, 

430 ) 

431 total_lines = 6 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

432 

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

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

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

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

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

438 

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

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

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

442 # end of the file name. 

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

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

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

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

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

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

449 

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

451 for data_file in data_files: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

452 data = coverage.CoverageData(data_file) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

453 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

455 

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

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

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

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

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

461 

462 data = coverage.CoverageData() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

463 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

465 

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

467 ppid = pids["parent"] 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

468 cpid = pids["child"] 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

469 assert ppid != cpid 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

471 assert len(plines) == 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

473 assert len(clines) == 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

475 assert len(reported_pids) == 2 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

476 

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

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

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

480 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

481 "forky.py", 

482 """\ 

483 import os 

484 import tempfile 

485 import time 

486 

487 complete_file = tempfile.mkstemp()[1] 

488 pid = os.fork() 

489 if pid: 

490 while True: 

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

492 data = f.read() 

493 if "Complete" in data: 

494 break 

495 time.sleep(.02) 

496 os.remove(complete_file) 

497 else: 

498 time.sleep(.1) 

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

500 f.write("Complete") 

501 os._exit(0) 

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

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

504 """, 

505 ) 

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

507 if patch: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

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

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

511 data = coverage.CoverageData() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

512 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

514 if patch: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

515 assert seen == total_lines 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

516 else: 

517 assert seen < total_lines 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

519 

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

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

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

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

524 "hello.py", 

525 """\ 

526 import sys, os, the_other 

527 print("Hello") 

528 """, 

529 ) 

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

531 "the_other.py", 

532 """\ 

533 print("What?") 

534 """, 

535 ) 

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

537 ".coveragerc", 

538 """\ 

539 [run] 

540 source = 

541 . 

542 xyzzy 

543 """, 

544 ) 

545 

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

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

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

549 

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

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

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

553 # will fail. 

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

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

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

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

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

559 

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

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

562 "No module named 'no_such_module'" in out 

563 ) 

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

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

566 

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

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

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

570 

571 self.make_file( 

572 "bug164.py", 

573 """\ 

574 import threading 

575 import time 

576 

577 class MyThread (threading.Thread): 

578 def run(self): 

579 print("Hello") 

580 

581 thr = MyThread() 

582 thr.start() 

583 thr.join() 

584 """, 

585 ) 

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

587 

588 assert "Hello\n" in out 

589 assert "warning" not in out 

590 

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

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

593 self.make_file( 

594 "settrace.py", 

595 """\ 

596 import sys 

597 print("Hello") 

598 sys.settrace(None) 

599 print("Goodbye") 

600 """, 

601 ) 

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

603 assert "Hello\n" in out 

604 assert "Goodbye\n" in out 

605 

606 assert "Trace function changed" in out 

607 

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

609 # coverage.py's own trace function. 

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

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

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

613 # function for a simpler one. 

614 # 

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

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

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

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

619 # the C trace function or not. 

620 

621 self.make_file( 

622 "showtrace.py", 

623 """\ 

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

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

626 

627 import inspect 

628 

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

630 # may be None. 

631 trace_fn = inspect.currentframe().f_trace 

632 if trace_fn is None: 

633 trace_name = "None" 

634 else: 

635 # Get the name of the tracer class. 

636 try: 

637 trace_name = trace_fn.__self__.__class__.__name__ 

638 except AttributeError: 

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

640 # which doesn't have __self__. 

641 trace_name = trace_fn.__class__.__name__ 

642 

643 print(trace_name) 

644 """, 

645 ) 

646 

647 # When running without coverage, no trace function 

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

649 assert py_out == "None\n" 

650 

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

652 if testenv.C_TRACER: 

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

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

655 assert cov_out == "CTracer\n" 

656 elif testenv.SYS_MON: 

657 assert cov_out == "None\n" 

658 else: 

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

660 # also show the Python function. 

661 assert cov_out == "PyTracer\n" 

662 

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

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

665 assert timid_out == "PyTracer\n" 

666 

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

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

669 "hello.py", 

670 """\ 

671 import goodbye 

672 import coverage 

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

674 cov.start() 

675 print(goodbye.f()) 

676 cov.stop() 

677 """, 

678 ) 

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

680 "goodbye.py", 

681 """\ 

682 def f(): 

683 return "Goodbye!" 

684 """, 

685 ) 

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

687 

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

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

690 

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

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

693 + "(already-imported)" 

694 ) 

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

696 

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

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

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

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

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

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

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

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

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

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

707 "weird_file.py", 

708 r""" 

709 globs = {} 

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

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

712 print(globs['a']) 

713 print(globs['b']) 

714 """, 

715 ) 

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

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

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

719 

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

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

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

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

724 "allok.py", 

725 """\ 

726 import warnings 

727 warnings.simplefilter('default') 

728 import coverage 

729 print("No warnings!") 

730 """, 

731 ) 

732 

733 # Some of our testing infrastructure can issue warnings. 

734 # Turn it all off for the subprocess. 

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

736 

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

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

739 

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

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

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

743 "foo.py", 

744 """\ 

745 def foo(): 

746 pass 

747 """, 

748 ) 

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

750 "run_twice.py", 

751 """\ 

752 import sys 

753 import coverage 

754 

755 for i in [1, 2]: 

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

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

758 inst.load() 

759 inst.start() 

760 import foo 

761 inst.stop() 

762 inst.save() 

763 """, 

764 ) 

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

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

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

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

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

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

771 "Run 1\n" 

772 + "Run 2\n" 

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

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

775 ) 

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

777 

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

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

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

781 # module, like it used to: 

782 # $ python -m coverage 

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

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

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

786 

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

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

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

790 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

791 "dummy_hello.py", 

792 """\ 

793 import os 

794 import signal 

795 

796 print(f"Sending SIGUSR1 to myself") 

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

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

799 

800 print("Done and goodbye") 

801 """, 

802 ) 

803 out = self.run_command( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

805 status=-signal.SIGKILL, 

806 ) 

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

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

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

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

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

812 Name Stmts Miss Cover Missing 

813 ---------------------------------------------- 

814 dummy_hello.py 6 2 67% 6-8 

815 ---------------------------------------------- 

816 TOTAL 6 2 67% 

817 """) 

818 

819 

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

821 

822 

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

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

825 

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

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

828 

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

830 

831 """ 

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

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

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

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

836 

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

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

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

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

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

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

843 

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

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

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

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

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

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

850 

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

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

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

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

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

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

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

858 

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

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

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

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

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

864 

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

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

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

868 

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

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

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

872 

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

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

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

876 

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

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

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

880 

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

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

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

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

885 

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

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

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

889 

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

891 """regression test for #328 

892 

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

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

895 """ 

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

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

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

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

900 ) 

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

902 

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

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

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

906 # CoverageWarning: Module process_test was previously imported, 

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

908 # 

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

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

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

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

913 ".coveragerc", 

914 """\ 

915 [run] 

916 disable_warnings = module-not-measured 

917 """, 

918 ) 

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

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

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

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

923 ) 

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

925 

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

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

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

929 

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

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

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

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

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

935 # keeps the test working. 

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

937 "myscript", 

938 """\ 

939 import sys; sys.dont_write_bytecode = True 

940 import process_test.try_execfile 

941 """, 

942 ) 

943 

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

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

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

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

948 

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

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

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

952 

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

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

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

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

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

958 

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

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

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

962 

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

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

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

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

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

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

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

970 

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

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

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

974 # command. 

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

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

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

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

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

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

981 

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

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

984 env.WINDOWS, 

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

986 ) 

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

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

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

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

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

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

993 self.assert_tryexecfile_output(expected, actual) 1abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNO

994 

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

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

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

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

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

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

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

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

1003 

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

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

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

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

1008 

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

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

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

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

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

1014 

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

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

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

1018 # fiddle with it. 

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

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

1021 """\ 

1022 SOMETHING = "hello-xyzzy" 

1023 """, 

1024 ) 

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

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

1027 "run_coverage.py", 

1028 f"""\ 

1029 import sys 

1030 sys.path[0:0] = [ 

1031 r'{abc}', 

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

1033 ] 

1034 

1035 import coverage.cmdline 

1036 

1037 if __name__ == '__main__': 

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

1039 """, 

1040 ) 

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

1042 "how_is_it.py", 

1043 """\ 

1044 import pprint, sys 

1045 pprint.pprint(sys.path) 

1046 import thing 

1047 print(thing.SOMETHING) 

1048 """, 

1049 ) 

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

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

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

1053 

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

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

1056 

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

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

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

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

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

1062 ) 

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

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

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

1066 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1067 "elsewhere/bin/fake-coverage", 

1068 f"""\ 

1069 #!{sys.executable} 

1070 import re 

1071 import sys 

1072 from coverage.cmdline import main 

1073 if __name__ == '__main__': 

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

1075 sys.exit(main()) 

1076 """, 

1077 ) 

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

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

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

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

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

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

1084 

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

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

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

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

1089 # warnings about measured files being imported before start. 

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

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

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

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

1094 

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

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

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

1098 

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

1100 

1101 # Name Stmts Miss Cover Missing 

1102 # ------------------------------------------------------ 

1103 # proj/__init__.py 1 0 100% 

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

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

1106 # proj/thecode.py 1 0 100% 

1107 # ------------------------------------------------------ 

1108 # TOTAL 3 0 100% 

1109 

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

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

1112 

1113 

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

1115 """Tests of sys.excepthook support.""" 

1116 

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

1118 

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

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

1121 "excepthook.py", 

1122 """\ 

1123 import sys 

1124 

1125 def excepthook(*args): 

1126 print('in excepthook') 

1127 if maybe == 2: 

1128 print('definitely') 

1129 

1130 sys.excepthook = excepthook 

1131 

1132 maybe = 1 

1133 raise RuntimeError('Error Outside') 

1134 """, 

1135 ) 

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

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

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

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

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

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

1142 

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

1144 # executed. 

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

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

1147 print(f"{line_counts(data) = }") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1148 print(f"{data = }") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1149 print("data.lines excepthook.py:", data.lines(os.path.abspath("excepthook.py"))) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

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

1151 

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

1153 not env.CPYTHON, 

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

1155 ) 

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

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

1158 "excepthook_exit.py", 

1159 """\ 

1160 import sys 

1161 

1162 def excepthook(*args): 

1163 print('in excepthook') 

1164 sys.exit(0) 

1165 

1166 sys.excepthook = excepthook 

1167 

1168 raise RuntimeError('Error Outside') 

1169 """, 

1170 ) 

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

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

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

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

1175 

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

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

1178 

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

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

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

1182 "excepthook_throw.py", 

1183 """\ 

1184 import sys 

1185 

1186 def excepthook(*args): 

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

1188 # with interleaved stdout/stderr comparisons in the assertions 

1189 # in the test. 

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

1191 raise RuntimeError('Error Inside') 

1192 

1193 sys.excepthook = excepthook 

1194 

1195 raise RuntimeError('Error Outside') 

1196 """, 

1197 ) 

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

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

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

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

1202 

1203 

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

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

1206 

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

1208 

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

1210 # "coverage3" works on py3 

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

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

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

1214 

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

1216 # "coverage2" doesn't work on py3 

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

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

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

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

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

1222 

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

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

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

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

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

1228 

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

1230 "cmd", 

1231 [ 

1232 "coverage", 

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

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

1235 ], 

1236 ) 

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

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

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

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

1241 

1242 

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

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

1245 

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

1247 

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

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

1250 # Run pydoc. 

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

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

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

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

1255 

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

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

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

1259 

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

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

1262 

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

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

1265 

1266 

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

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

1269 

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

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

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

1273 "forty_two_plus.py", 

1274 """\ 

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

1276 a = 1 

1277 b = 2 

1278 if a > 3: 

1279 b = 4 

1280 c = 5 

1281 d = 6 

1282 e = 7 

1283 """, 

1284 ) 

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

1286 

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

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

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

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

1291 

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

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

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

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

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

1297 

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

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

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

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

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

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

1304 

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

1306 # A file with 99.9% coverage: 

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

1308 "ninety_nine_plus.py", 

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

1310 ) 

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

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

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

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

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

1316 

1317 

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

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

1320 

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

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

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

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

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

1326 

1327 

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

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

1330 

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

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

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

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

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

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

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

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

1339 

1340 

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

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

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

1344 

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

1346 import os 

1347 import sys 

1348 import tempfile 

1349 

1350 tmpdir = tempfile.mkdtemp() 

1351 os.chdir(tmpdir) 

1352 os.rmdir(tmpdir) 

1353 print(sys.argv[1]) 

1354 """ 

1355 

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

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

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

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

1360 

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

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

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

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

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

1366 # Check that the lines we expect are there. 

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

1368 Traceback (most recent call last): 

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

1370 print(sys.argv[1]) 

1371 IndexError: list index out of range 

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

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

1374 

1375 

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

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

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

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

1380 

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

1382 """Create main.py and sub.py.""" 

1383 # Main will run sub.py 

1384 self.make_file( 

1385 "main.py", 

1386 """\ 

1387 import os, os.path, sys 

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

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

1390 """, 

1391 ) 

1392 # sub.py will write a few lines. 

1393 self.make_file( 

1394 "sub.py", 

1395 """\ 

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

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

1398 a = 3 

1399 """, 

1400 ) 

1401 

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

1403 self.make_main_and_sub() 

1404 self.make_file( 

1405 ".coveragerc", 

1406 """\ 

1407 [run] 

1408 patch = subprocess 

1409 """, 

1410 ) 

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

1412 self.run_command("coverage combine") 

1413 self.assert_exists(".coverage") 

1414 data = coverage.CoverageData() 

1415 data.read() 

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

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

1418 

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

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

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

1422 # it. 

1423 self.make_main_and_sub() 

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

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

1426 data.write() 

1427 

1428 self.make_file( 

1429 "coverage.ini", 

1430 """\ 

1431 [run] 

1432 data_file = .mycovdata 

1433 """, 

1434 ) 

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

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

1437 

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

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

1440 

1441 # Read the data from .coverage 

1442 self.assert_exists(".mycovdata") 

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

1444 data.read() 

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

1446 

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

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

1449 self.make_main_and_sub() 

1450 self.make_file( 

1451 "coverage.ini", 

1452 """\ 

1453 [run] 

1454 parallel = true 

1455 """, 

1456 ) 

1457 

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

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

1460 

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

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

1463 

1464 self.run_command("coverage combine") 

1465 

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

1467 self.assert_exists(".coverage") 

1468 data = coverage.CoverageData() 

1469 data.read() 

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

1471 

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

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

1474 msg = ( 

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

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

1477 ) 

1478 assert len(data_files) == 1, msg 

1479 

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

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

1482 # subprocesses. 

1483 self.make_file( 

1484 "main.py", 

1485 """\ 

1486 import subprocess 

1487 import sys 

1488 print(subprocess.check_output( 

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

1490 cwd="subdir", 

1491 encoding="utf-8", 

1492 )) 

1493 """, 

1494 ) 

1495 self.make_file( 

1496 "subdir/subproc.py", 

1497 """\ 

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

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

1500 """, 

1501 ) 

1502 self.make_file( 

1503 ".coveragerc", 

1504 """\ 

1505 [run] 

1506 patch = subprocess 

1507 data_file = .covdata 

1508 """, 

1509 ) 

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

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

1512 assert out == "hello\n" 

1513 self.run_command("coverage combine") 

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

1515 data.read() 

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

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

1518 

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

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

1521 ) 

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

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

1524 self.make_file( 

1525 "subfunctions.py", 

1526 """\ 

1527 import subprocess, sys 

1528 

1529 def f1(): 

1530 print("function 1") 

1531 def f2(): 

1532 print("function 2") 

1533 

1534 functions = [f1, f2] 

1535 

1536 cases = sys.argv[1:] 

1537 if len(cases) > 1: 

1538 for c in cases: 

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

1540 else: 

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

1542 """, 

1543 ) 

1544 self.make_file( 

1545 ".coveragerc", 

1546 """\ 

1547 [run] 

1548 patch = subprocess 

1549 """, 

1550 ) 

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

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

1553 self.run_command("coverage combine") 

1554 data = coverage.CoverageData() 

1555 data.read() 

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

1557 

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

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

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

1561 self.make_file( 

1562 "main/main.py", 

1563 """\ 

1564 import os, subprocess, sys 

1565 orig = os.getcwd() 

1566 os.chdir("./d") 

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

1568 os.chdir(orig) 

1569 """, 

1570 ) 

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

1572 self.make_file( 

1573 "main/sub.py", 

1574 """ 

1575 import other 

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

1577 """, 

1578 ) 

1579 self.make_file( 

1580 "main/pyproject.toml", 

1581 """\ 

1582 [tool.coverage.run] 

1583 patch = ["subprocess"] 

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

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

1586 """, 

1587 ) 

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

1589 with change_dir("main"): 

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

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

1592 self.run_command("coverage combine") 

1593 data = coverage.CoverageData() 

1594 data.read() 

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

1596 

1597 

1598@pytest.fixture 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1599def _clean_pth_files() -> Iterable[None]: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1600 """A fixture to clean up any .pth files we created during the test.""" 

1601 # The execv test needs to make .pth files so that subprocesses will get 

1602 # measured. But there's no way for coverage to remove those files because 

1603 # they need to exist when the new program starts, and there's no 

1604 # information carried over to clean them automatically. 

1605 # 

1606 # So for these tests, we clean them as part of the test suite. 

1607 pth_files: set[Path] = set() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1608 for d in site.getsitepackages(): 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1609 pth_files.update(Path(d).glob("*.pth")) 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1610 

1611 try: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1612 yield 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1613 finally: 

1614 for d in site.getsitepackages(): 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1615 for pth in Path(d).glob("*.pth"): 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1616 if pth not in pth_files: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1617 pth.unlink() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1618 

1619 

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

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

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

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

1624 

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

1626 "fname", 

1627 [ 

1628 base + suffix 

1629 for base, suffix in itertools.product( 

1630 ["exec", "spawn"], 

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

1632 ) 

1633 ], 

1634 ) 

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

1636 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1637 ".coveragerc", 

1638 """\ 

1639 [run] 

1640 patch = subprocess, execv 

1641 """, 

1642 ) 

1643 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1644 "main.py", 

1645 f"""\ 

1646 import os, sys 

1647 print("In main") 

1648 args = [] 

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

1650 args.append(os.P_WAIT) 

1651 args.append(sys.executable) 

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

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

1654 args.extend(prog_args) 

1655 else: 

1656 args.append(prog_args) 

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

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

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

1660 sys.stdout.flush() 

1661 os.{fname}(*args) 

1662 """, 

1663 ) 

1664 self.make_file( 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1665 "other.py", 

1666 """\ 

1667 import os, sys 

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

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

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

1671 """, 

1672 ) 

1673 

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

1675 expected = "In main\n" 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

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

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

1679 else: 

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

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

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

1683 assert out == expected 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1684 

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

1686 data = coverage.CoverageData() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1687 data.read() 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1688 

1689 main_lines = 12 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1690 if "spawn" in fname: 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1691 main_lines += 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

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

1693 main_lines += 1 1PQRSabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNTO

1694 

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

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

1697 

1698 

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

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

1701 

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

1703 

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

1705 

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

1707 module, and 

1708 

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

1710 

1711 """ 

1712 

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

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

1715 @pytest.mark.parametrize("source", ["main", "sub"]) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

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

1717 def test_pth_and_source_work_together( 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1718 self, 

1719 dashm: str, 

1720 package: str, 

1721 source: str, 

1722 _create_pth_file: None, 

1723 ) -> None: 

1724 """Run the test for a particular combination of factors. 

1725 

1726 The arguments are all strings: 

1727 

1728 * `dashm`: Either "" (run the program as a file) or "-m" (run the 

1729 program as a module). 

1730 

1731 * `package`: Either "" (put the source at the top level) or a 

1732 package name to use to hold the source. 

1733 

1734 * `source`: Either "main" or "sub", which file to use as the 

1735 ``--source`` argument. 

1736 

1737 """ 

1738 

1739 def fullname(modname: str) -> str: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1740 """What is the full module name for `modname` for this test?""" 

1741 if package and dashm: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1742 return ".".join((package, modname)) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1743 else: 

1744 return modname 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1745 

1746 def path(basename: str) -> str: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1747 """Where should `basename` be created for this test?""" 

1748 return os.path.join(package, basename) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1749 

1750 # Main will run sub.py. 

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

1752 path("main.py"), 

1753 """\ 

1754 import %s 

1755 a = 2 

1756 b = 3 

1757 """ 

1758 % fullname("sub"), 

1759 ) 

1760 if package: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1761 self.make_file(path("__init__.py"), "") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1762 # sub.py will write a few lines. 

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

1764 path("sub.py"), 

1765 """\ 

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

1767 f.write("Hello, world!") 

1768 a = 3 

1769 """, 

1770 ) 

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

1772 "coverage.ini", 

1773 """\ 

1774 [run] 

1775 source = %s 

1776 """ 

1777 % fullname(source), 

1778 ) 

1779 

1780 self.set_environ("COVERAGE_PROCESS_START", "coverage.ini") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1781 

1782 if dashm: 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1783 cmd = "python -m %s" % fullname("main") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1784 else: 

1785 cmd = "python %s" % path("main.py") 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1786 

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

1788 

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

1790 assert f.read() == "Hello, world!" 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1791 

1792 # Read the data from .coverage 

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

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

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

1796 summary = line_counts(data) 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

1797 assert summary[source + ".py"] == 3 1PQRS'(abcdUVefghWXijklYZmnop01qrstuv234wxyzAB567CDEFGH89!IJKLMN#$%TO)

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