coverage 7.6.10__cp312-cp312-musllinux_1_2_aarch64.whl → 7.12.0__cp312-cp312-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coverage/__init__.py +3 -1
- coverage/__main__.py +3 -1
- coverage/annotate.py +2 -3
- coverage/bytecode.py +178 -4
- coverage/cmdline.py +330 -155
- coverage/collector.py +32 -43
- coverage/config.py +167 -63
- coverage/context.py +5 -6
- coverage/control.py +165 -86
- coverage/core.py +71 -34
- coverage/data.py +4 -5
- coverage/debug.py +113 -57
- coverage/disposition.py +2 -1
- coverage/env.py +29 -78
- coverage/exceptions.py +29 -7
- coverage/execfile.py +19 -14
- coverage/files.py +24 -19
- coverage/html.py +118 -75
- coverage/htmlfiles/coverage_html.js +12 -10
- coverage/htmlfiles/index.html +45 -10
- coverage/htmlfiles/pyfile.html +2 -2
- coverage/htmlfiles/style.css +54 -6
- coverage/htmlfiles/style.scss +85 -3
- coverage/inorout.py +62 -45
- coverage/jsonreport.py +22 -9
- coverage/lcovreport.py +16 -18
- coverage/misc.py +51 -47
- coverage/multiproc.py +12 -7
- coverage/numbits.py +4 -5
- coverage/parser.py +150 -251
- coverage/patch.py +166 -0
- coverage/phystokens.py +25 -26
- coverage/plugin.py +14 -14
- coverage/plugin_support.py +37 -36
- coverage/python.py +13 -14
- coverage/pytracer.py +31 -33
- coverage/regions.py +3 -2
- coverage/report.py +60 -44
- coverage/report_core.py +7 -10
- coverage/results.py +152 -68
- coverage/sqldata.py +261 -211
- coverage/sqlitedb.py +37 -29
- coverage/sysmon.py +237 -162
- coverage/templite.py +19 -7
- coverage/tomlconfig.py +13 -13
- coverage/tracer.cpython-312-aarch64-linux-musl.so +0 -0
- coverage/tracer.pyi +3 -1
- coverage/types.py +26 -23
- coverage/version.py +4 -19
- coverage/xmlreport.py +17 -14
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/METADATA +50 -28
- coverage-7.12.0.dist-info/RECORD +59 -0
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/WHEEL +1 -1
- coverage-7.6.10.dist-info/RECORD +0 -58
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/entry_points.txt +0 -0
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info/licenses}/LICENSE.txt +0 -0
- {coverage-7.6.10.dist-info → coverage-7.12.0.dist-info}/top_level.txt +0 -0
coverage/html.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
|
2
|
-
# For details: https://github.com/
|
|
2
|
+
# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
|
|
3
3
|
|
|
4
4
|
"""HTML reporting for coverage.py."""
|
|
5
5
|
|
|
@@ -13,26 +13,30 @@ import json
|
|
|
13
13
|
import os
|
|
14
14
|
import re
|
|
15
15
|
import string
|
|
16
|
-
|
|
17
|
-
from dataclasses import dataclass, field
|
|
18
|
-
from typing import Any, TYPE_CHECKING
|
|
19
16
|
from collections.abc import Iterable
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import TYPE_CHECKING, Any
|
|
20
19
|
|
|
21
20
|
import coverage
|
|
22
21
|
from coverage.data import CoverageData, add_data_to_hash
|
|
23
22
|
from coverage.exceptions import NoDataError
|
|
24
23
|
from coverage.files import flat_rootname
|
|
25
24
|
from coverage.misc import (
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
Hasher,
|
|
26
|
+
ensure_dir,
|
|
27
|
+
file_be_gone,
|
|
28
|
+
format_local_datetime,
|
|
29
|
+
human_sorted,
|
|
30
|
+
isolate_module,
|
|
31
|
+
plural,
|
|
32
|
+
stdout_link,
|
|
28
33
|
)
|
|
29
34
|
from coverage.report_core import get_analysis_to_report
|
|
30
|
-
from coverage.results import Analysis, Numbers
|
|
35
|
+
from coverage.results import Analysis, AnalysisNarrower, Numbers
|
|
31
36
|
from coverage.templite import Templite
|
|
32
37
|
from coverage.types import TLineNo, TMorf
|
|
33
38
|
from coverage.version import __url__
|
|
34
39
|
|
|
35
|
-
|
|
36
40
|
if TYPE_CHECKING:
|
|
37
41
|
from coverage import Coverage
|
|
38
42
|
from coverage.plugins import FileReporter
|
|
@@ -42,8 +46,7 @@ os = isolate_module(os)
|
|
|
42
46
|
|
|
43
47
|
|
|
44
48
|
def data_filename(fname: str) -> str:
|
|
45
|
-
"""Return the path to an "htmlfiles" data file of ours.
|
|
46
|
-
"""
|
|
49
|
+
"""Return the path to an "htmlfiles" data file of ours."""
|
|
47
50
|
static_dir = os.path.join(os.path.dirname(__file__), "htmlfiles")
|
|
48
51
|
static_filename = os.path.join(static_dir, fname)
|
|
49
52
|
return static_filename
|
|
@@ -51,7 +54,7 @@ def data_filename(fname: str) -> str:
|
|
|
51
54
|
|
|
52
55
|
def read_data(fname: str) -> str:
|
|
53
56
|
"""Return the contents of a data file of ours."""
|
|
54
|
-
with open(data_filename(fname)) as data_file:
|
|
57
|
+
with open(data_filename(fname), encoding="utf-8") as data_file:
|
|
55
58
|
return data_file.read()
|
|
56
59
|
|
|
57
60
|
|
|
@@ -65,6 +68,7 @@ def write_html(fname: str, html: str) -> None:
|
|
|
65
68
|
@dataclass
|
|
66
69
|
class LineData:
|
|
67
70
|
"""The data for each source line of HTML output."""
|
|
71
|
+
|
|
68
72
|
tokens: list[tuple[str, str]]
|
|
69
73
|
number: TLineNo
|
|
70
74
|
category: str
|
|
@@ -83,6 +87,7 @@ class LineData:
|
|
|
83
87
|
@dataclass
|
|
84
88
|
class FileData:
|
|
85
89
|
"""The data for each source file of HTML output."""
|
|
90
|
+
|
|
86
91
|
relative_filename: str
|
|
87
92
|
nums: Numbers
|
|
88
93
|
lines: list[LineData]
|
|
@@ -91,6 +96,7 @@ class FileData:
|
|
|
91
96
|
@dataclass
|
|
92
97
|
class IndexItem:
|
|
93
98
|
"""Information for each index entry, to render an index page."""
|
|
99
|
+
|
|
94
100
|
url: str = ""
|
|
95
101
|
file: str = ""
|
|
96
102
|
description: str = ""
|
|
@@ -100,6 +106,7 @@ class IndexItem:
|
|
|
100
106
|
@dataclass
|
|
101
107
|
class IndexPage:
|
|
102
108
|
"""Data for each index page."""
|
|
109
|
+
|
|
103
110
|
noun: str
|
|
104
111
|
plural: str
|
|
105
112
|
filename: str
|
|
@@ -138,10 +145,13 @@ class HtmlDataGeneration:
|
|
|
138
145
|
|
|
139
146
|
lines = []
|
|
140
147
|
branch_stats = analysis.branch_stats()
|
|
148
|
+
multiline_map = {}
|
|
149
|
+
if hasattr(fr, "multiline_map"):
|
|
150
|
+
multiline_map = fr.multiline_map()
|
|
141
151
|
|
|
142
152
|
for lineno, tokens in enumerate(fr.source_token_lines(), start=1):
|
|
143
153
|
# Figure out how to mark this line.
|
|
144
|
-
category = ""
|
|
154
|
+
category = category2 = ""
|
|
145
155
|
short_annotations = []
|
|
146
156
|
long_annotations = []
|
|
147
157
|
|
|
@@ -169,6 +179,18 @@ class HtmlDataGeneration:
|
|
|
169
179
|
)
|
|
170
180
|
elif lineno in analysis.statements:
|
|
171
181
|
category = "run"
|
|
182
|
+
elif first_line := multiline_map.get(lineno):
|
|
183
|
+
if first_line in analysis.excluded:
|
|
184
|
+
category2 = "exc2"
|
|
185
|
+
elif first_line in analysis.missing:
|
|
186
|
+
category2 = "mis2"
|
|
187
|
+
elif self.has_arcs and first_line in missing_branch_arcs:
|
|
188
|
+
category2 = "par2"
|
|
189
|
+
# I don't understand why this last condition is marked as
|
|
190
|
+
# partial. If I add an else with an exception, the exception
|
|
191
|
+
# is raised.
|
|
192
|
+
elif first_line in analysis.statements: # pragma: part covered
|
|
193
|
+
category2 = "run2"
|
|
172
194
|
|
|
173
195
|
contexts = []
|
|
174
196
|
contexts_label = ""
|
|
@@ -181,16 +203,18 @@ class HtmlDataGeneration:
|
|
|
181
203
|
contexts_label = f"{len(contexts)} ctx"
|
|
182
204
|
context_list = contexts
|
|
183
205
|
|
|
184
|
-
lines.append(
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
206
|
+
lines.append(
|
|
207
|
+
LineData(
|
|
208
|
+
tokens=tokens,
|
|
209
|
+
number=lineno,
|
|
210
|
+
category=category or category2,
|
|
211
|
+
contexts=contexts,
|
|
212
|
+
contexts_label=contexts_label,
|
|
213
|
+
context_list=context_list,
|
|
214
|
+
short_annotations=short_annotations,
|
|
215
|
+
long_annotations=long_annotations,
|
|
216
|
+
)
|
|
217
|
+
)
|
|
194
218
|
|
|
195
219
|
file_data = FileData(
|
|
196
220
|
relative_filename=fr.relative_filename(),
|
|
@@ -203,6 +227,7 @@ class HtmlDataGeneration:
|
|
|
203
227
|
|
|
204
228
|
class FileToReport:
|
|
205
229
|
"""A file we're considering reporting."""
|
|
230
|
+
|
|
206
231
|
def __init__(self, fr: FileReporter, analysis: Analysis) -> None:
|
|
207
232
|
self.fr = fr
|
|
208
233
|
self.analysis = analysis
|
|
@@ -213,6 +238,7 @@ class FileToReport:
|
|
|
213
238
|
|
|
214
239
|
HTML_SAFE = string.ascii_letters + string.digits + "!#$%'()*+,-./:;=?@[]^_`{|}~"
|
|
215
240
|
|
|
241
|
+
|
|
216
242
|
@functools.cache
|
|
217
243
|
def encode_int(n: int) -> str:
|
|
218
244
|
"""Create a short HTML-safe string from an integer, using HTML_SAFE."""
|
|
@@ -287,8 +313,7 @@ class HtmlReporter:
|
|
|
287
313
|
# Functions available in the templates.
|
|
288
314
|
"escape": escape,
|
|
289
315
|
"pair": pair,
|
|
290
|
-
"
|
|
291
|
-
|
|
316
|
+
"pretty_file": pretty_file,
|
|
292
317
|
# Constants for this report.
|
|
293
318
|
"__url__": __url__,
|
|
294
319
|
"__version__": coverage.__version__,
|
|
@@ -296,9 +321,7 @@ class HtmlReporter:
|
|
|
296
321
|
"time_stamp": format_local_datetime(datetime.datetime.now()),
|
|
297
322
|
"extra_css": self.extra_css,
|
|
298
323
|
"has_arcs": self.has_arcs,
|
|
299
|
-
"show_contexts": self.config.show_contexts,
|
|
300
324
|
"statics": {},
|
|
301
|
-
|
|
302
325
|
# Constants for all reports.
|
|
303
326
|
# These css classes determine which lines are highlighted by default.
|
|
304
327
|
"category": {
|
|
@@ -306,6 +329,10 @@ class HtmlReporter:
|
|
|
306
329
|
"mis": "mis show_mis",
|
|
307
330
|
"par": "par run show_par",
|
|
308
331
|
"run": "run",
|
|
332
|
+
"exc2": "exc exc2 show_exc",
|
|
333
|
+
"mis2": "mis mis2 show_mis",
|
|
334
|
+
"par2": "par par2 ru2 show_par",
|
|
335
|
+
"run2": "run run2",
|
|
309
336
|
},
|
|
310
337
|
}
|
|
311
338
|
self.index_tmpl = Templite(read_data("index.html"), self.template_globals)
|
|
@@ -394,7 +421,7 @@ class HtmlReporter:
|
|
|
394
421
|
dest = copy_with_cache_bust(src, self.directory)
|
|
395
422
|
if not slug:
|
|
396
423
|
slug = os.path.basename(src).replace(".", "_")
|
|
397
|
-
self.template_globals["statics"][slug] = dest
|
|
424
|
+
self.template_globals["statics"][slug] = dest # type: ignore
|
|
398
425
|
|
|
399
426
|
def make_local_static_report_files(self) -> None:
|
|
400
427
|
"""Make local instances of static files for HTML report."""
|
|
@@ -412,7 +439,7 @@ class HtmlReporter:
|
|
|
412
439
|
# .gitignore can't be copied from the source tree because if it was in
|
|
413
440
|
# the source tree, it would stop the static files from being checked in.
|
|
414
441
|
if self.directory_was_empty:
|
|
415
|
-
with open(os.path.join(self.directory, ".gitignore"), "w") as fgi:
|
|
442
|
+
with open(os.path.join(self.directory, ".gitignore"), "w", encoding="utf-8") as fgi:
|
|
416
443
|
fgi.write("# Created by coverage.py\n*\n")
|
|
417
444
|
|
|
418
445
|
def should_report(self, analysis: Analysis, index_page: IndexPage) -> bool:
|
|
@@ -423,8 +450,8 @@ class HtmlReporter:
|
|
|
423
450
|
|
|
424
451
|
if self.skip_covered:
|
|
425
452
|
# Don't report on 100% files.
|
|
426
|
-
no_missing_lines = (nums.n_missing == 0)
|
|
427
|
-
no_missing_branches = (nums.n_partial_branches == 0)
|
|
453
|
+
no_missing_lines = (nums.n_missing == 0) # fmt: skip
|
|
454
|
+
no_missing_branches = (nums.n_partial_branches == 0) # fmt: skip
|
|
428
455
|
if no_missing_lines and no_missing_branches:
|
|
429
456
|
index_page.skipped_covered_count += 1
|
|
430
457
|
return False
|
|
@@ -478,9 +505,8 @@ class HtmlReporter:
|
|
|
478
505
|
encode_int(context_codes[c_context]) for c_context in ldata.context_list
|
|
479
506
|
]
|
|
480
507
|
code_width = max(len(ec) for ec in encoded_contexts)
|
|
481
|
-
ldata.context_str = (
|
|
482
|
-
|
|
483
|
-
+ "".join(ec.ljust(code_width) for ec in encoded_contexts)
|
|
508
|
+
ldata.context_str = str(code_width) + "".join(
|
|
509
|
+
ec.ljust(code_width) for ec in encoded_contexts
|
|
484
510
|
)
|
|
485
511
|
else:
|
|
486
512
|
ldata.context_str = ""
|
|
@@ -489,8 +515,7 @@ class HtmlReporter:
|
|
|
489
515
|
# 202F is NARROW NO-BREAK SPACE.
|
|
490
516
|
# 219B is RIGHTWARDS ARROW WITH STROKE.
|
|
491
517
|
ldata.annotate = ", ".join(
|
|
492
|
-
f"{ldata.number} ↛ {d}"
|
|
493
|
-
for d in ldata.short_annotations
|
|
518
|
+
f"{ldata.number} ↛ {d}" for d in ldata.short_annotations
|
|
494
519
|
)
|
|
495
520
|
else:
|
|
496
521
|
ldata.annotate = None
|
|
@@ -509,24 +534,26 @@ class HtmlReporter:
|
|
|
509
534
|
css_classes = []
|
|
510
535
|
if ldata.category:
|
|
511
536
|
css_classes.append(
|
|
512
|
-
self.template_globals["category"][ldata.category],
|
|
537
|
+
self.template_globals["category"][ldata.category], # type: ignore[index]
|
|
513
538
|
)
|
|
514
539
|
ldata.css_class = " ".join(css_classes) or "pln"
|
|
515
540
|
|
|
516
541
|
html_path = os.path.join(self.directory, ftr.html_filename)
|
|
517
|
-
html = self.source_tmpl.render(
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
542
|
+
html = self.source_tmpl.render(
|
|
543
|
+
{
|
|
544
|
+
**file_data.__dict__,
|
|
545
|
+
"contexts_json": contexts_json,
|
|
546
|
+
"prev_html": ftr.prev_html,
|
|
547
|
+
"next_html": ftr.next_html,
|
|
548
|
+
}
|
|
549
|
+
)
|
|
523
550
|
write_html(html_path, html)
|
|
524
551
|
|
|
525
552
|
# Save this file's information for the index page.
|
|
526
553
|
index_info = IndexItem(
|
|
527
|
-
url
|
|
528
|
-
file
|
|
529
|
-
nums
|
|
554
|
+
url=ftr.html_filename,
|
|
555
|
+
file=escape(ftr.fr.relative_filename()),
|
|
556
|
+
nums=ftr.analysis.numbers,
|
|
530
557
|
)
|
|
531
558
|
self.index_pages["file"].summaries.append(index_info)
|
|
532
559
|
self.incr.set_index_info(ftr.rootname, index_info)
|
|
@@ -554,39 +581,51 @@ class HtmlReporter:
|
|
|
554
581
|
|
|
555
582
|
for noun in region_nouns:
|
|
556
583
|
page_data = self.index_pages[noun]
|
|
557
|
-
outside_lines = set(range(1, num_lines + 1))
|
|
558
584
|
|
|
585
|
+
outside_lines = set(range(1, num_lines + 1))
|
|
559
586
|
for region in regions:
|
|
560
587
|
if region.kind != noun:
|
|
561
588
|
continue
|
|
562
589
|
outside_lines -= region.lines
|
|
563
|
-
|
|
590
|
+
|
|
591
|
+
narrower = AnalysisNarrower(ftr.analysis)
|
|
592
|
+
narrower.add_regions(r.lines for r in regions if r.kind == noun)
|
|
593
|
+
narrower.add_regions([outside_lines])
|
|
594
|
+
|
|
595
|
+
for region in regions:
|
|
596
|
+
if region.kind != noun:
|
|
597
|
+
continue
|
|
598
|
+
analysis = narrower.narrow(region.lines)
|
|
564
599
|
if not self.should_report(analysis, page_data):
|
|
565
600
|
continue
|
|
566
601
|
sorting_name = region.name.rpartition(".")[-1].lstrip("_")
|
|
567
|
-
page_data.summaries.append(
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
602
|
+
page_data.summaries.append(
|
|
603
|
+
IndexItem(
|
|
604
|
+
url=f"{ftr.html_filename}#t{region.start}",
|
|
605
|
+
file=escape(ftr.fr.relative_filename()),
|
|
606
|
+
description=(
|
|
607
|
+
f"<data value='{escape(sorting_name)}'>"
|
|
608
|
+
+ escape(region.name)
|
|
609
|
+
+ "</data>"
|
|
610
|
+
),
|
|
611
|
+
nums=analysis.numbers,
|
|
612
|
+
)
|
|
613
|
+
)
|
|
614
|
+
|
|
615
|
+
analysis = narrower.narrow(outside_lines)
|
|
579
616
|
if self.should_report(analysis, page_data):
|
|
580
|
-
page_data.summaries.append(
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
617
|
+
page_data.summaries.append(
|
|
618
|
+
IndexItem(
|
|
619
|
+
url=ftr.html_filename,
|
|
620
|
+
file=escape(ftr.fr.relative_filename()),
|
|
621
|
+
description=(
|
|
622
|
+
"<data value=''>"
|
|
623
|
+
+ f"<span class='no-noun'>(no {escape(noun)})</span>"
|
|
624
|
+
+ "</data>"
|
|
625
|
+
),
|
|
626
|
+
nums=analysis.numbers,
|
|
627
|
+
)
|
|
628
|
+
)
|
|
590
629
|
|
|
591
630
|
for noun, index_page in self.index_pages.items():
|
|
592
631
|
if noun != "file":
|
|
@@ -636,6 +675,7 @@ class HtmlReporter:
|
|
|
636
675
|
@dataclass
|
|
637
676
|
class FileInfo:
|
|
638
677
|
"""Summary of the information from last rendering, to avoid duplicate work."""
|
|
678
|
+
|
|
639
679
|
hash: str = ""
|
|
640
680
|
index: IndexItem = field(default_factory=IndexItem)
|
|
641
681
|
|
|
@@ -706,7 +746,7 @@ class IncrementalChecker:
|
|
|
706
746
|
"""Read the information we stored last time."""
|
|
707
747
|
try:
|
|
708
748
|
status_file = os.path.join(self.directory, self.STATUS_FILE)
|
|
709
|
-
with open(status_file) as fstatus:
|
|
749
|
+
with open(status_file, encoding="utf-8") as fstatus:
|
|
710
750
|
status = json.load(fstatus)
|
|
711
751
|
except (OSError, ValueError):
|
|
712
752
|
# Status file is missing or malformed.
|
|
@@ -742,12 +782,9 @@ class IncrementalChecker:
|
|
|
742
782
|
"format": self.STATUS_FORMAT,
|
|
743
783
|
"version": coverage.__version__,
|
|
744
784
|
"globals": self.globals,
|
|
745
|
-
"files": {
|
|
746
|
-
fname: dataclasses.asdict(finfo)
|
|
747
|
-
for fname, finfo in self.files.items()
|
|
748
|
-
},
|
|
785
|
+
"files": {fname: dataclasses.asdict(finfo) for fname, finfo in self.files.items()},
|
|
749
786
|
}
|
|
750
|
-
with open(status_file, "w") as fout:
|
|
787
|
+
with open(status_file, "w", encoding="utf-8") as fout:
|
|
751
788
|
json.dump(status_data, fout, separators=(",", ":"))
|
|
752
789
|
|
|
753
790
|
def check_global_data(self, *data: Any) -> None:
|
|
@@ -802,6 +839,7 @@ class IncrementalChecker:
|
|
|
802
839
|
|
|
803
840
|
# Helpers for templates and generating HTML
|
|
804
841
|
|
|
842
|
+
|
|
805
843
|
def escape(t: str) -> str:
|
|
806
844
|
"""HTML-escape the text in `t`.
|
|
807
845
|
|
|
@@ -815,3 +853,8 @@ def escape(t: str) -> str:
|
|
|
815
853
|
def pair(ratio: tuple[int, int]) -> str:
|
|
816
854
|
"""Format a pair of numbers so JavaScript can read them in an attribute."""
|
|
817
855
|
return "{} {}".format(*ratio)
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
def pretty_file(filename: str) -> str:
|
|
859
|
+
"""Return a prettier version of `filename` for display."""
|
|
860
|
+
return re.sub(r"[/\\]", "\N{THIN SPACE}\\g<0>\N{THIN SPACE}", filename)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
|
2
|
-
// For details: https://github.com/
|
|
2
|
+
// For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt
|
|
3
3
|
|
|
4
4
|
// Coverage.py HTML report browser code.
|
|
5
5
|
/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
|
|
@@ -140,12 +140,15 @@ coverage.wire_up_filter = function () {
|
|
|
140
140
|
const table_body_rows = table.querySelectorAll("tbody tr");
|
|
141
141
|
const no_rows = document.getElementById("no_rows");
|
|
142
142
|
|
|
143
|
+
const footer = table.tFoot.rows[0];
|
|
144
|
+
const ratio_columns = Array.from(footer.cells).map(cell => Boolean(cell.dataset.ratio));
|
|
145
|
+
|
|
143
146
|
// Observe filter keyevents.
|
|
144
147
|
const filter_handler = (event => {
|
|
145
148
|
// Keep running total of each metric, first index contains number of shown rows
|
|
146
|
-
const totals =
|
|
147
|
-
|
|
148
|
-
|
|
149
|
+
const totals = ratio_columns.map(
|
|
150
|
+
is_ratio => is_ratio ? {"numer": 0, "denom": 0} : 0
|
|
151
|
+
);
|
|
149
152
|
|
|
150
153
|
var text = document.getElementById("filter").value;
|
|
151
154
|
// Store filter value
|
|
@@ -191,11 +194,11 @@ coverage.wire_up_filter = function () {
|
|
|
191
194
|
for (let column = 0; column < totals.length; column++) {
|
|
192
195
|
// Accumulate dynamic totals
|
|
193
196
|
cell = row.cells[column] // nosemgrep: eslint.detect-object-injection
|
|
194
|
-
if (cell.
|
|
197
|
+
if (cell.matches(".name, .spacer")) {
|
|
195
198
|
continue;
|
|
196
199
|
}
|
|
197
|
-
if (column
|
|
198
|
-
//
|
|
200
|
+
if (ratio_columns[column] && cell.dataset.ratio) {
|
|
201
|
+
// Column stores a ratio
|
|
199
202
|
const [numer, denom] = cell.dataset.ratio.split(" ");
|
|
200
203
|
totals[column]["numer"] += parseInt(numer, 10); // nosemgrep: eslint.detect-object-injection
|
|
201
204
|
totals[column]["denom"] += parseInt(denom, 10); // nosemgrep: eslint.detect-object-injection
|
|
@@ -218,17 +221,16 @@ coverage.wire_up_filter = function () {
|
|
|
218
221
|
no_rows.style.display = null;
|
|
219
222
|
table.style.display = null;
|
|
220
223
|
|
|
221
|
-
const footer = table.tFoot.rows[0];
|
|
222
224
|
// Calculate new dynamic sum values based on visible rows.
|
|
223
225
|
for (let column = 0; column < totals.length; column++) {
|
|
224
226
|
// Get footer cell element.
|
|
225
227
|
const cell = footer.cells[column]; // nosemgrep: eslint.detect-object-injection
|
|
226
|
-
if (cell.
|
|
228
|
+
if (cell.matches(".name, .spacer")) {
|
|
227
229
|
continue;
|
|
228
230
|
}
|
|
229
231
|
|
|
230
232
|
// Set value into dynamic footer cell element.
|
|
231
|
-
if (column
|
|
233
|
+
if (ratio_columns[column]) {
|
|
232
234
|
// Percentage column uses the numerator and denominator,
|
|
233
235
|
// and adapts to the number of decimal places.
|
|
234
236
|
const match = /\.([0-9]+)/.exec(cell.textContent);
|
coverage/htmlfiles/index.html
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
|
|
2
|
-
{# For details: https://github.com/
|
|
2
|
+
{# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt #}
|
|
3
3
|
|
|
4
4
|
<!DOCTYPE html>
|
|
5
5
|
<html lang="en">
|
|
@@ -81,53 +81,88 @@
|
|
|
81
81
|
<table class="index" data-sortable>
|
|
82
82
|
<thead>
|
|
83
83
|
{# The title="" attr doesn't work in Safari. #}
|
|
84
|
+
{% if has_arcs %}
|
|
85
|
+
<tr class="tablehead grouphead">
|
|
86
|
+
<th class="spacer"> </th>
|
|
87
|
+
{% if region_noun %}
|
|
88
|
+
<th class="spacer"> </th>
|
|
89
|
+
{% endif %}
|
|
90
|
+
<th class="spacer"> </th>
|
|
91
|
+
<th class="left" colspan="{% if has_arcs %}4{% else %}3{% endif %}">Statements</th>
|
|
92
|
+
<th class="spacer"> </th>
|
|
93
|
+
<th class="left" colspan="3">Branches</th>
|
|
94
|
+
<th class="spacer"> </th>
|
|
95
|
+
<th>Total</th>
|
|
96
|
+
</tr>
|
|
97
|
+
{% endif %}
|
|
84
98
|
<tr class="tablehead" title="Click to sort">
|
|
85
|
-
<th id="file" class="name
|
|
99
|
+
<th id="file" class="name" aria-sort="none" data-shortcut="f">File<span class="arrows"></span></th>
|
|
86
100
|
{% if region_noun %}
|
|
87
|
-
<th id="region" class="name
|
|
101
|
+
<th id="region" class="name" aria-sort="none" data-default-sort-order="ascending" data-shortcut="n">{{ region_noun }}<span class="arrows"></span></th>
|
|
102
|
+
{% endif %}
|
|
103
|
+
<th class="spacer"> </th>
|
|
104
|
+
{% if has_arcs %}
|
|
105
|
+
<th id="statements_coverage" aria-sort="none" data-default-sort-order="descending">coverage<span class="arrows"></span></th>
|
|
88
106
|
{% endif %}
|
|
89
107
|
<th id="statements" aria-sort="none" data-default-sort-order="descending" data-shortcut="s">statements<span class="arrows"></span></th>
|
|
90
108
|
<th id="missing" aria-sort="none" data-default-sort-order="descending" data-shortcut="m">missing<span class="arrows"></span></th>
|
|
91
109
|
<th id="excluded" aria-sort="none" data-default-sort-order="descending" data-shortcut="x">excluded<span class="arrows"></span></th>
|
|
92
110
|
{% if has_arcs %}
|
|
111
|
+
<th class="spacer"> </th>
|
|
112
|
+
<th id="branches_coverage" aria-sort="none" data-default-sort-order="descending">coverage<span class="arrows"></span></th>
|
|
93
113
|
<th id="branches" aria-sort="none" data-default-sort-order="descending" data-shortcut="b">branches<span class="arrows"></span></th>
|
|
94
114
|
<th id="partial" aria-sort="none" data-default-sort-order="descending" data-shortcut="p">partial<span class="arrows"></span></th>
|
|
95
115
|
{% endif %}
|
|
96
|
-
<th
|
|
116
|
+
<th class="spacer"> </th>
|
|
117
|
+
<th id="coverage" aria-sort="none" data-shortcut="c">coverage<span class="arrows"></span></th>
|
|
97
118
|
</tr>
|
|
98
119
|
</thead>
|
|
99
120
|
<tbody>
|
|
100
121
|
{% for region in regions %}
|
|
101
122
|
<tr class="region">
|
|
102
|
-
<td class="name
|
|
123
|
+
<td class="name"><a href="{{region.url}}">{{region.file|escape|pretty_file}}</a></td>
|
|
103
124
|
{% if region_noun %}
|
|
104
|
-
<td class="name
|
|
125
|
+
<td class="name"><a href="{{region.url}}">{{region.description}}</a></td>
|
|
126
|
+
{% endif %}
|
|
127
|
+
<td class="spacer"> </td>
|
|
128
|
+
{% if has_arcs %}
|
|
129
|
+
<td data-ratio="{{region.nums.ratio_statements|pair}}">{{region.nums.pc_statements_str}}%</td>
|
|
105
130
|
{% endif %}
|
|
106
131
|
<td>{{region.nums.n_statements}}</td>
|
|
107
132
|
<td>{{region.nums.n_missing}}</td>
|
|
108
133
|
<td>{{region.nums.n_excluded}}</td>
|
|
109
134
|
{% if has_arcs %}
|
|
135
|
+
<td class="spacer"> </td>
|
|
136
|
+
<td data-ratio="{{region.nums.ratio_branches|pair}}">{{region.nums.pc_branches_str}}%</td>
|
|
110
137
|
<td>{{region.nums.n_branches}}</td>
|
|
111
138
|
<td>{{region.nums.n_partial_branches}}</td>
|
|
112
139
|
{% endif %}
|
|
113
|
-
<td class="
|
|
140
|
+
<td class="spacer"> </td>
|
|
141
|
+
<td data-ratio="{{region.nums.ratio_covered|pair}}">{{region.nums.pc_covered_str}}%</td>
|
|
114
142
|
</tr>
|
|
115
143
|
{% endfor %}
|
|
116
144
|
</tbody>
|
|
117
145
|
<tfoot>
|
|
118
146
|
<tr class="total">
|
|
119
|
-
<td class="name
|
|
147
|
+
<td class="name">Total</td>
|
|
120
148
|
{% if region_noun %}
|
|
121
|
-
<td class="name
|
|
149
|
+
<td class="name"> </td>
|
|
150
|
+
{% endif %}
|
|
151
|
+
<td class="spacer"> </td>
|
|
152
|
+
{% if has_arcs %}
|
|
153
|
+
<td data-ratio="{{totals.ratio_statements|pair}}">{{totals.pc_statements_str}}%</td>
|
|
122
154
|
{% endif %}
|
|
123
155
|
<td>{{totals.n_statements}}</td>
|
|
124
156
|
<td>{{totals.n_missing}}</td>
|
|
125
157
|
<td>{{totals.n_excluded}}</td>
|
|
126
158
|
{% if has_arcs %}
|
|
159
|
+
<td class="spacer"> </td>
|
|
160
|
+
<td data-ratio="{{totals.ratio_branches|pair}}">{{totals.pc_branches_str}}%</td>
|
|
127
161
|
<td>{{totals.n_branches}}</td>
|
|
128
162
|
<td>{{totals.n_partial_branches}}</td>
|
|
129
163
|
{% endif %}
|
|
130
|
-
<td class="
|
|
164
|
+
<td class="spacer"> </td>
|
|
165
|
+
<td data-ratio="{{totals.ratio_covered|pair}}">{{totals.pc_covered_str}}%</td>
|
|
131
166
|
</tr>
|
|
132
167
|
</tfoot>
|
|
133
168
|
</table>
|
coverage/htmlfiles/pyfile.html
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{# Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 #}
|
|
2
|
-
{# For details: https://github.com/
|
|
2
|
+
{# For details: https://github.com/coveragepy/coveragepy/blob/main/NOTICE.txt #}
|
|
3
3
|
|
|
4
4
|
<!DOCTYPE html>
|
|
5
5
|
<html lang="en">
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
<header>
|
|
26
26
|
<div class="content">
|
|
27
27
|
<h1>
|
|
28
|
-
<span class="text">Coverage for </span><b>{{relative_filename|escape}}</b>:
|
|
28
|
+
<span class="text">Coverage for </span><b>{{relative_filename|escape|pretty_file}}</b>:
|
|
29
29
|
<span class="pc_cov">{{nums.pc_covered_str}}%</span>
|
|
30
30
|
</h1>
|
|
31
31
|
|