6
6
import os
7
7
import os .path
8
8
import re
9
+
10
+ from typing import Any , Dict , Iterator , Tuple , Union
9
11
from xml .etree import ElementTree
10
12
11
13
import pytest
12
14
13
15
import coverage
16
+ from coverage import Coverage
14
17
from coverage .exceptions import NoDataError
15
18
from coverage .files import abs_file
16
19
from coverage .misc import import_local_file
23
26
class XmlTestHelpers (CoverageTest ):
24
27
"""Methods to use from XML tests."""
25
28
26
- def run_doit (self ):
29
+ def run_doit (self ) -> Coverage :
27
30
"""Construct a simple sub-package."""
28
31
self .make_file ("sub/__init__.py" )
29
32
self .make_file ("sub/doit.py" , "print('doit!')" )
@@ -32,7 +35,7 @@ def run_doit(self):
32
35
self .start_import_stop (cov , "main" )
33
36
return cov
34
37
35
- def make_tree (self , width , depth , curdir = "." ):
38
+ def make_tree (self , width : int , depth : int , curdir : str = "." ) -> None :
36
39
"""Make a tree of packages.
37
40
38
41
Makes `width` directories, named d0 .. d{width-1}. Each directory has
@@ -44,7 +47,7 @@ def make_tree(self, width, depth, curdir="."):
44
47
if depth == 0 :
45
48
return
46
49
47
- def here (p ) :
50
+ def here (p : str ) -> str :
48
51
"""A path for `p` in our currently interesting directory."""
49
52
return os .path .join (curdir , p )
50
53
@@ -57,7 +60,11 @@ def here(p):
57
60
filename = here (f"f{ i } .py" )
58
61
self .make_file (filename , f"# { filename } \n " )
59
62
60
- def assert_source (self , xmldom , src ):
63
+ def assert_source (
64
+ self ,
65
+ xmldom : Union [ElementTree .Element , ElementTree .ElementTree ],
66
+ src : str ,
67
+ ) -> None :
61
68
"""Assert that the XML has a <source> element with `src`."""
62
69
src = abs_file (src )
63
70
elts = xmldom .findall (".//sources/source" )
@@ -69,7 +76,7 @@ class XmlTestHelpersTest(XmlTestHelpers, CoverageTest):
69
76
70
77
run_in_temp_dir = False
71
78
72
- def test_assert_source (self ):
79
+ def test_assert_source (self ) -> None :
73
80
dom = ElementTree .fromstring ("""\
74
81
<doc>
75
82
<src>foo</src>
@@ -94,24 +101,24 @@ def test_assert_source(self):
94
101
class XmlReportTest (XmlTestHelpers , CoverageTest ):
95
102
"""Tests of the XML reports from coverage.py."""
96
103
97
- def make_mycode_data (self ):
104
+ def make_mycode_data (self ) -> None :
98
105
"""Pretend that we ran mycode.py, so we can report on it."""
99
106
self .make_file ("mycode.py" , "print('hello')\n " )
100
107
self .make_data_file (lines = {abs_file ("mycode.py" ): [1 ]})
101
108
102
- def run_xml_report (self , ** kwargs ) :
109
+ def run_xml_report (self , ** kwargs : Any ) -> None :
103
110
"""Run xml_report()"""
104
111
cov = coverage .Coverage ()
105
112
cov .load ()
106
113
cov .xml_report (** kwargs )
107
114
108
- def test_default_file_placement (self ):
115
+ def test_default_file_placement (self ) -> None :
109
116
self .make_mycode_data ()
110
117
self .run_xml_report ()
111
118
self .assert_exists ("coverage.xml" )
112
119
assert self .stdout () == ""
113
120
114
- def test_argument_affects_xml_placement (self ):
121
+ def test_argument_affects_xml_placement (self ) -> None :
115
122
self .make_mycode_data ()
116
123
cov = coverage .Coverage (messages = True )
117
124
cov .load ()
@@ -120,28 +127,28 @@ def test_argument_affects_xml_placement(self):
120
127
self .assert_doesnt_exist ("coverage.xml" )
121
128
self .assert_exists ("put_it_there.xml" )
122
129
123
- def test_output_directory_does_not_exist (self ):
130
+ def test_output_directory_does_not_exist (self ) -> None :
124
131
self .make_mycode_data ()
125
132
self .run_xml_report (outfile = "nonexistent/put_it_there.xml" )
126
133
self .assert_doesnt_exist ("coverage.xml" )
127
134
self .assert_doesnt_exist ("put_it_there.xml" )
128
135
self .assert_exists ("nonexistent/put_it_there.xml" )
129
136
130
- def test_config_affects_xml_placement (self ):
137
+ def test_config_affects_xml_placement (self ) -> None :
131
138
self .make_mycode_data ()
132
139
self .make_file (".coveragerc" , "[xml]\n output = xml.out\n " )
133
140
self .run_xml_report ()
134
141
self .assert_doesnt_exist ("coverage.xml" )
135
142
self .assert_exists ("xml.out" )
136
143
137
- def test_no_data (self ):
144
+ def test_no_data (self ) -> None :
138
145
# https://github.com/nedbat/coveragepy/issues/210
139
146
with pytest .raises (NoDataError , match = "No data to report." ):
140
147
self .run_xml_report ()
141
148
self .assert_doesnt_exist ("coverage.xml" )
142
149
self .assert_doesnt_exist (".coverage" )
143
150
144
- def test_no_source (self ):
151
+ def test_no_source (self ) -> None :
145
152
# Written while investigating a bug, might as well keep it.
146
153
# https://github.com/nedbat/coveragepy/issues/208
147
154
self .make_file ("innocuous.py" , "a = 4" )
@@ -156,23 +163,23 @@ def test_no_source(self):
156
163
)
157
164
self .assert_exists ("coverage.xml" )
158
165
159
- def test_filename_format_showing_everything (self ):
166
+ def test_filename_format_showing_everything (self ) -> None :
160
167
cov = self .run_doit ()
161
168
cov .xml_report ()
162
169
dom = ElementTree .parse ("coverage.xml" )
163
170
elts = dom .findall (".//class[@name='doit.py']" )
164
171
assert len (elts ) == 1
165
172
assert elts [0 ].get ('filename' ) == "sub/doit.py"
166
173
167
- def test_filename_format_including_filename (self ):
174
+ def test_filename_format_including_filename (self ) -> None :
168
175
cov = self .run_doit ()
169
176
cov .xml_report (["sub/doit.py" ])
170
177
dom = ElementTree .parse ("coverage.xml" )
171
178
elts = dom .findall (".//class[@name='doit.py']" )
172
179
assert len (elts ) == 1
173
180
assert elts [0 ].get ('filename' ) == "sub/doit.py"
174
181
175
- def test_filename_format_including_module (self ):
182
+ def test_filename_format_including_module (self ) -> None :
176
183
cov = self .run_doit ()
177
184
import sub .doit # pylint: disable=import-error
178
185
cov .xml_report ([sub .doit ])
@@ -181,7 +188,7 @@ def test_filename_format_including_module(self):
181
188
assert len (elts ) == 1
182
189
assert elts [0 ].get ('filename' ) == "sub/doit.py"
183
190
184
- def test_reporting_on_nothing (self ):
191
+ def test_reporting_on_nothing (self ) -> None :
185
192
# Used to raise a zero division error:
186
193
# https://github.com/nedbat/coveragepy/issues/250
187
194
self .make_file ("empty.py" , "" )
@@ -194,7 +201,7 @@ def test_reporting_on_nothing(self):
194
201
assert elts [0 ].get ('filename' ) == "empty.py"
195
202
assert elts [0 ].get ('line-rate' ) == '1'
196
203
197
- def test_empty_file_is_100_not_0 (self ):
204
+ def test_empty_file_is_100_not_0 (self ) -> None :
198
205
# https://github.com/nedbat/coveragepy/issues/345
199
206
cov = self .run_doit ()
200
207
cov .xml_report ()
@@ -203,14 +210,14 @@ def test_empty_file_is_100_not_0(self):
203
210
assert len (elts ) == 1
204
211
assert elts [0 ].get ('line-rate' ) == '1'
205
212
206
- def test_empty_file_is_skipped (self ):
213
+ def test_empty_file_is_skipped (self ) -> None :
207
214
cov = self .run_doit ()
208
215
cov .xml_report (skip_empty = True )
209
216
dom = ElementTree .parse ("coverage.xml" )
210
217
elts = dom .findall (".//class[@name='__init__.py']" )
211
218
assert len (elts ) == 0
212
219
213
- def test_curdir_source (self ):
220
+ def test_curdir_source (self ) -> None :
214
221
# With no source= option, the XML report should explain that the source
215
222
# is in the current directory.
216
223
cov = self .run_doit ()
@@ -220,7 +227,7 @@ def test_curdir_source(self):
220
227
sources = dom .findall (".//source" )
221
228
assert len (sources ) == 1
222
229
223
- def test_deep_source (self ):
230
+ def test_deep_source (self ) -> None :
224
231
# When using source=, the XML report needs to mention those directories
225
232
# in the <source> elements.
226
233
# https://github.com/nedbat/coveragepy/issues/439
@@ -264,15 +271,15 @@ def test_deep_source(self):
264
271
'name' : 'bar.py' ,
265
272
}
266
273
267
- def test_nonascii_directory (self ):
274
+ def test_nonascii_directory (self ) -> None :
268
275
# https://github.com/nedbat/coveragepy/issues/573
269
276
self .make_file ("테스트/program.py" , "a = 1" )
270
277
with change_dir ("테스트" ):
271
278
cov = coverage .Coverage ()
272
279
self .start_import_stop (cov , "program" )
273
280
cov .xml_report ()
274
281
275
- def test_accented_dot_py (self ):
282
+ def test_accented_dot_py (self ) -> None :
276
283
# Make a file with a non-ascii character in the filename.
277
284
self .make_file ("h\xe2 t.py" , "print('accented')" )
278
285
self .make_data_file (lines = {abs_file ("h\xe2 t.py" ): [1 ]})
@@ -285,7 +292,7 @@ def test_accented_dot_py(self):
285
292
assert ' filename="h\xe2 t.py"' .encode () in xml
286
293
assert ' name="h\xe2 t.py"' .encode () in xml
287
294
288
- def test_accented_directory (self ):
295
+ def test_accented_directory (self ) -> None :
289
296
# Make a file with a non-ascii character in the directory name.
290
297
self .make_file ("\xe2 /accented.py" , "print('accented')" )
291
298
self .make_data_file (lines = {abs_file ("\xe2 /accented.py" ): [1 ]})
@@ -310,7 +317,7 @@ def test_accented_directory(self):
310
317
}
311
318
312
319
313
- def unbackslash (v ) :
320
+ def unbackslash (v : Any ) -> Any :
314
321
"""Find strings in `v`, and replace backslashes with slashes throughout."""
315
322
if isinstance (v , (tuple , list )):
316
323
return [unbackslash (vv ) for vv in v ]
@@ -324,24 +331,24 @@ def unbackslash(v):
324
331
class XmlPackageStructureTest (XmlTestHelpers , CoverageTest ):
325
332
"""Tests about the package structure reported in the coverage.xml file."""
326
333
327
- def package_and_class_tags (self , cov ) :
334
+ def package_and_class_tags (self , cov : Coverage ) -> Iterator [ Tuple [ str , Dict [ str , Any ]]] :
328
335
"""Run an XML report on `cov`, and get the package and class tags."""
329
336
cov .xml_report ()
330
337
dom = ElementTree .parse ("coverage.xml" )
331
338
for node in dom .iter ():
332
339
if node .tag in ('package' , 'class' ):
333
340
yield (node .tag , {a :v for a ,v in node .items () if a in ('name' , 'filename' )})
334
341
335
- def assert_package_and_class_tags (self , cov , result ) :
342
+ def assert_package_and_class_tags (self , cov : Coverage , result : Any ) -> None :
336
343
"""Check the XML package and class tags from `cov` match `result`."""
337
344
assert unbackslash (list (self .package_and_class_tags (cov ))) == unbackslash (result )
338
345
339
- def test_package_names (self ):
346
+ def test_package_names (self ) -> None :
340
347
self .make_tree (width = 1 , depth = 3 )
341
348
self .make_file ("main.py" , """\
342
349
from d0.d0 import f0
343
350
""" )
344
- cov = coverage .Coverage (source = "." )
351
+ cov = coverage .Coverage (source = [ "." ] )
345
352
self .start_import_stop (cov , "main" )
346
353
self .assert_package_and_class_tags (cov , [
347
354
('package' , {'name' : "." }),
@@ -354,12 +361,12 @@ def test_package_names(self):
354
361
('class' , {'filename' : "d0/d0/f0.py" , 'name' : "f0.py" }),
355
362
])
356
363
357
- def test_package_depth_1 (self ):
364
+ def test_package_depth_1 (self ) -> None :
358
365
self .make_tree (width = 1 , depth = 4 )
359
366
self .make_file ("main.py" , """\
360
367
from d0.d0 import f0
361
368
""" )
362
- cov = coverage .Coverage (source = "." )
369
+ cov = coverage .Coverage (source = [ "." ] )
363
370
self .start_import_stop (cov , "main" )
364
371
365
372
cov .set_option ("xml:package_depth" , 1 )
@@ -375,12 +382,12 @@ def test_package_depth_1(self):
375
382
('class' , {'filename' : "d0/f0.py" , 'name' : "f0.py" }),
376
383
])
377
384
378
- def test_package_depth_2 (self ):
385
+ def test_package_depth_2 (self ) -> None :
379
386
self .make_tree (width = 1 , depth = 4 )
380
387
self .make_file ("main.py" , """\
381
388
from d0.d0 import f0
382
389
""" )
383
- cov = coverage .Coverage (source = "." )
390
+ cov = coverage .Coverage (source = [ "." ] )
384
391
self .start_import_stop (cov , "main" )
385
392
386
393
cov .set_option ("xml:package_depth" , 2 )
@@ -397,12 +404,12 @@ def test_package_depth_2(self):
397
404
('class' , {'filename' : "d0/d0/f0.py" , 'name' : "f0.py" }),
398
405
])
399
406
400
- def test_package_depth_3 (self ):
407
+ def test_package_depth_3 (self ) -> None :
401
408
self .make_tree (width = 1 , depth = 4 )
402
409
self .make_file ("main.py" , """\
403
410
from d0.d0 import f0
404
411
""" )
405
- cov = coverage .Coverage (source = "." )
412
+ cov = coverage .Coverage (source = [ "." ] )
406
413
self .start_import_stop (cov , "main" )
407
414
408
415
cov .set_option ("xml:package_depth" , 3 )
@@ -420,7 +427,7 @@ def test_package_depth_3(self):
420
427
('class' , {'filename' : "d0/d0/d0/f0.py" , 'name' : "f0.py" }),
421
428
])
422
429
423
- def test_source_prefix (self ):
430
+ def test_source_prefix (self ) -> None :
424
431
# https://github.com/nedbat/coveragepy/issues/465
425
432
# https://github.com/nedbat/coveragepy/issues/526
426
433
self .make_file ("src/mod.py" , "print(17)" )
@@ -434,7 +441,7 @@ def test_source_prefix(self):
434
441
dom = ElementTree .parse ("coverage.xml" )
435
442
self .assert_source (dom , "src" )
436
443
437
- def test_relative_source (self ):
444
+ def test_relative_source (self ) -> None :
438
445
self .make_file ("src/mod.py" , "print(17)" )
439
446
cov = coverage .Coverage (source = ["src" ])
440
447
cov .set_option ("run:relative_files" , True )
@@ -448,7 +455,7 @@ def test_relative_source(self):
448
455
assert [elt .text for elt in elts ] == ["src" ]
449
456
450
457
451
- def compare_xml (expected , actual , ** kwargs ) :
458
+ def compare_xml (expected : str , actual : str , actual_extra : bool = False ) -> None :
452
459
"""Specialized compare function for our XML files."""
453
460
source_path = coverage .files .relative_directory ().rstrip (r"\/" )
454
461
@@ -458,13 +465,13 @@ def compare_xml(expected, actual, **kwargs):
458
465
(r'<source>\s*.*?\s*</source>' , '<source>%s</source>' % re .escape (source_path )),
459
466
(r'/coverage.readthedocs.io/?[-.\w/]*' , '/coverage.readthedocs.io/VER' ),
460
467
]
461
- compare (expected , actual , scrubs = scrubs , ** kwargs )
468
+ compare (expected , actual , scrubs = scrubs , actual_extra = actual_extra )
462
469
463
470
464
471
class XmlGoldTest (CoverageTest ):
465
472
"""Tests of XML reporting that use gold files."""
466
473
467
- def test_a_xml_1 (self ):
474
+ def test_a_xml_1 (self ) -> None :
468
475
self .make_file ("a.py" , """\
469
476
if 1 < 2:
470
477
# Needed a < to look at HTML entities.
@@ -478,7 +485,7 @@ def test_a_xml_1(self):
478
485
cov .xml_report (a , outfile = "coverage.xml" )
479
486
compare_xml (gold_path ("xml/x_xml" ), "." , actual_extra = True )
480
487
481
- def test_a_xml_2 (self ):
488
+ def test_a_xml_2 (self ) -> None :
482
489
self .make_file ("a.py" , """\
483
490
if 1 < 2:
484
491
# Needed a < to look at HTML entities.
@@ -498,7 +505,7 @@ def test_a_xml_2(self):
498
505
cov .xml_report (a )
499
506
compare_xml (gold_path ("xml/x_xml" ), "xml_2" )
500
507
501
- def test_y_xml_branch (self ):
508
+ def test_y_xml_branch (self ) -> None :
502
509
self .make_file ("y.py" , """\
503
510
def choice(x):
504
511
if x < 2:
0 commit comments