differential-coverage 1.2.0__tar.gz → 1.2.1__tar.gz
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.
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/PKG-INFO +1 -1
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/pyproject.toml +1 -1
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/cli.py +17 -11
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/output.py +36 -11
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage.egg-info/PKG-INFO +1 -1
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_cli.py +12 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_output.py +29 -2
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/README.md +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/setup.cfg +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/__init__.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/api.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/approach_data.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/fs.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/readers/__init__.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/readers/afl_showmap.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/readers/libfuzzer_merge.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/readers/llvm_cov.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/readers/registry.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/types.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage.egg-info/SOURCES.txt +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage.egg-info/dependency_links.txt +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage.egg-info/entry_points.txt +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage.egg-info/requires.txt +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage.egg-info/top_level.txt +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_api.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_approach_data.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_docs.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_libfuzzer_merge.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_llvm_cov.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_readers.py +0 -0
- {differential_coverage-1.2.0 → differential_coverage-1.2.1}/tests/test_reducers.py +0 -0
{differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/cli.py
RENAMED
|
@@ -87,8 +87,9 @@ def cmd_relscore(args: argparse.Namespace) -> int:
|
|
|
87
87
|
print_scores(
|
|
88
88
|
scores,
|
|
89
89
|
output=args.output,
|
|
90
|
-
colormap=
|
|
91
|
-
latex_enable_color=
|
|
90
|
+
colormap=args.colormap,
|
|
91
|
+
latex_enable_color=args.latex_enable_color,
|
|
92
|
+
digits=args.digits,
|
|
92
93
|
)
|
|
93
94
|
return 0
|
|
94
95
|
|
|
@@ -97,11 +98,6 @@ def cmd_relcov_performance_over_approach(args: argparse.Namespace) -> int:
|
|
|
97
98
|
campaign = _load_campaign(args)
|
|
98
99
|
|
|
99
100
|
dc = DifferentialCoverage(campaign)
|
|
100
|
-
output = args.output
|
|
101
|
-
colormap = getattr(args, "colormap", "viridis")
|
|
102
|
-
latex_rotate_headers = getattr(args, "latex_rotate_headers", None)
|
|
103
|
-
latex_enable_color = getattr(args, "latex_enable_color", False)
|
|
104
|
-
|
|
105
101
|
ref_approaches = list(dc.approaches.keys())
|
|
106
102
|
table = {
|
|
107
103
|
f: {ref: dc.approaches[f].relcov(dc.approaches[ref]) for ref in ref_approaches}
|
|
@@ -110,10 +106,11 @@ def cmd_relcov_performance_over_approach(args: argparse.Namespace) -> int:
|
|
|
110
106
|
print_relcov_corpus_table(
|
|
111
107
|
ref_approaches,
|
|
112
108
|
table,
|
|
113
|
-
output=output,
|
|
114
|
-
colormap=colormap,
|
|
115
|
-
latex_rotate_headers=latex_rotate_headers,
|
|
116
|
-
latex_enable_color=latex_enable_color,
|
|
109
|
+
output=args.output,
|
|
110
|
+
colormap=args.colormap,
|
|
111
|
+
latex_rotate_headers=args.latex_rotate_headers,
|
|
112
|
+
latex_enable_color=args.latex_enable_color,
|
|
113
|
+
digits=args.digits,
|
|
117
114
|
)
|
|
118
115
|
return 0
|
|
119
116
|
|
|
@@ -192,6 +189,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
192
189
|
'latex for LaTeX tabular (requires "latex" optional dependencies)'
|
|
193
190
|
),
|
|
194
191
|
)
|
|
192
|
+
parser.add_argument(
|
|
193
|
+
"--digits",
|
|
194
|
+
type=int,
|
|
195
|
+
default=3,
|
|
196
|
+
metavar="N",
|
|
197
|
+
help=(
|
|
198
|
+
"Number of digits after the decimal point for numeric results (default: 3)."
|
|
199
|
+
),
|
|
200
|
+
)
|
|
195
201
|
parser.add_argument(
|
|
196
202
|
"--latex-rotate-headers",
|
|
197
203
|
type=float,
|
{differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/output.py
RENAMED
|
@@ -30,12 +30,22 @@ class _PrinterIO:
|
|
|
30
30
|
return len(s)
|
|
31
31
|
|
|
32
32
|
|
|
33
|
+
# Default decimal places for numeric output.
|
|
34
|
+
_DEFAULT_DIGITS = 3
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _format_float(value: float, digits: int) -> str:
|
|
38
|
+
"""Format a float with a fixed number of digits after the decimal point."""
|
|
39
|
+
return f"{value:.{digits}f}"
|
|
40
|
+
|
|
41
|
+
|
|
33
42
|
def print_scores(
|
|
34
43
|
scores: Mapping[ApproachId, float],
|
|
35
44
|
*,
|
|
36
45
|
output: OutputFormat = "stdout",
|
|
37
46
|
colormap: str = "viridis",
|
|
38
47
|
latex_enable_color: bool = False,
|
|
48
|
+
digits: int = _DEFAULT_DIGITS,
|
|
39
49
|
printer: Callable[[str], None] = print,
|
|
40
50
|
) -> None:
|
|
41
51
|
"""Print one score per approach, in the requested output format."""
|
|
@@ -43,14 +53,15 @@ def print_scores(
|
|
|
43
53
|
performance_sorted = sorted(name_sorted, key=lambda x: x[1], reverse=True)
|
|
44
54
|
|
|
45
55
|
if output == "stdout":
|
|
46
|
-
_print_scores_plain(performance_sorted, printer=printer)
|
|
56
|
+
_print_scores_plain(performance_sorted, digits=digits, printer=printer)
|
|
47
57
|
elif output == "csv":
|
|
48
|
-
_print_scores_csv(performance_sorted, printer=printer)
|
|
58
|
+
_print_scores_csv(performance_sorted, digits=digits, printer=printer)
|
|
49
59
|
elif output == "latex":
|
|
50
60
|
_print_scores_latex(
|
|
51
61
|
performance_sorted,
|
|
52
62
|
enable_color=latex_enable_color,
|
|
53
63
|
colormap=colormap,
|
|
64
|
+
digits=digits,
|
|
54
65
|
printer=printer,
|
|
55
66
|
)
|
|
56
67
|
else:
|
|
@@ -60,21 +71,23 @@ def print_scores(
|
|
|
60
71
|
def _print_scores_plain(
|
|
61
72
|
performance_sorted: Sequence[tuple[ApproachId, float]],
|
|
62
73
|
*,
|
|
74
|
+
digits: int,
|
|
63
75
|
printer: Callable[[str], None],
|
|
64
76
|
) -> None:
|
|
65
77
|
for approach, score in performance_sorted:
|
|
66
|
-
printer(f"{approach}: {score
|
|
78
|
+
printer(f"{approach}: {_format_float(score, digits)}")
|
|
67
79
|
|
|
68
80
|
|
|
69
81
|
def _print_scores_csv(
|
|
70
82
|
performance_sorted: Sequence[tuple[ApproachId, float]],
|
|
71
83
|
*,
|
|
84
|
+
digits: int,
|
|
72
85
|
printer: Callable[[str], None],
|
|
73
86
|
) -> None:
|
|
74
87
|
writer = csv.writer(_PrinterIO(printer))
|
|
75
88
|
writer.writerow(("approach", "score"))
|
|
76
89
|
for approach, score in performance_sorted:
|
|
77
|
-
writer.writerow((approach,
|
|
90
|
+
writer.writerow((approach, _format_float(score, digits)))
|
|
78
91
|
|
|
79
92
|
|
|
80
93
|
def _norm_minmax(values: Sequence[float]) -> tuple[float, float]:
|
|
@@ -96,6 +109,7 @@ def _print_scores_latex(
|
|
|
96
109
|
*,
|
|
97
110
|
enable_color: bool = False,
|
|
98
111
|
colormap: str = "viridis",
|
|
112
|
+
digits: int,
|
|
99
113
|
printer: Callable[[str], None],
|
|
100
114
|
) -> None:
|
|
101
115
|
"""LaTeX table (optionally with score cells colored by value)."""
|
|
@@ -110,12 +124,13 @@ def _print_scores_latex(
|
|
|
110
124
|
values = [s for _, s in performance_sorted]
|
|
111
125
|
min_v, max_v = _norm_minmax(values)
|
|
112
126
|
for approach, score in performance_sorted:
|
|
127
|
+
formatted = _format_float(score, digits)
|
|
113
128
|
if enable_color:
|
|
114
129
|
n = _norm_value(score, min_v, max_v)
|
|
115
130
|
hex_color = _colormap_light_hex(n, colormap=colormap)
|
|
116
|
-
printer(rf"{approach} & \cellcolor[HTML]{{{hex_color}}}{{{
|
|
131
|
+
printer(rf"{approach} & \cellcolor[HTML]{{{hex_color}}}{{{formatted}}} \\")
|
|
117
132
|
else:
|
|
118
|
-
printer(f"{approach} & {
|
|
133
|
+
printer(f"{approach} & {formatted} \\\\")
|
|
119
134
|
printer(r"\end{tabular}")
|
|
120
135
|
|
|
121
136
|
|
|
@@ -127,6 +142,7 @@ def print_relcov_corpus_table(
|
|
|
127
142
|
colormap: str = "viridis",
|
|
128
143
|
latex_rotate_headers: Optional[float] = None,
|
|
129
144
|
latex_enable_color: bool = False,
|
|
145
|
+
digits: int = _DEFAULT_DIGITS,
|
|
130
146
|
printer: Callable[[str], None] = print,
|
|
131
147
|
) -> None:
|
|
132
148
|
"""Print a table of relcov performance: rows = approaches, columns = corpus approaches."""
|
|
@@ -138,12 +154,14 @@ def print_relcov_corpus_table(
|
|
|
138
154
|
_print_relcov_corpus_table_plain(
|
|
139
155
|
corpus_approaches,
|
|
140
156
|
table,
|
|
157
|
+
digits=digits,
|
|
141
158
|
printer=printer,
|
|
142
159
|
)
|
|
143
160
|
elif output == "csv":
|
|
144
161
|
_print_relcov_corpus_table_csv(
|
|
145
162
|
corpus_approaches,
|
|
146
163
|
table,
|
|
164
|
+
digits=digits,
|
|
147
165
|
printer=printer,
|
|
148
166
|
)
|
|
149
167
|
elif output == "latex":
|
|
@@ -153,6 +171,7 @@ def print_relcov_corpus_table(
|
|
|
153
171
|
rotate_headers=latex_rotate_headers,
|
|
154
172
|
enable_color=latex_enable_color,
|
|
155
173
|
colormap=colormap,
|
|
174
|
+
digits=digits,
|
|
156
175
|
printer=printer,
|
|
157
176
|
)
|
|
158
177
|
else:
|
|
@@ -163,6 +182,7 @@ def _print_relcov_corpus_table_plain(
|
|
|
163
182
|
corpus_approaches: Sequence[ApproachId],
|
|
164
183
|
table: Mapping[ApproachId, Mapping[ApproachId, float]],
|
|
165
184
|
*,
|
|
185
|
+
digits: int,
|
|
166
186
|
printer: Callable[[str], None],
|
|
167
187
|
) -> None:
|
|
168
188
|
row_labels = sorted(table.keys())
|
|
@@ -171,7 +191,8 @@ def _print_relcov_corpus_table_plain(
|
|
|
171
191
|
(len(str(c)) for c in col_labels + ["approach"]),
|
|
172
192
|
default=7,
|
|
173
193
|
)
|
|
174
|
-
|
|
194
|
+
# Width for aligned numeric columns: sign + integer digit + '.' + fraction + padding.
|
|
195
|
+
num_width = max(10, digits + 4)
|
|
175
196
|
header = "approach".ljust(col_width)
|
|
176
197
|
for c in col_labels:
|
|
177
198
|
header += str(c).rjust(num_width)
|
|
@@ -181,7 +202,7 @@ def _print_relcov_corpus_table_plain(
|
|
|
181
202
|
for c in col_labels:
|
|
182
203
|
val = table[row].get(c)
|
|
183
204
|
if val is not None:
|
|
184
|
-
line += f"{val:>{num_width}.
|
|
205
|
+
line += f"{val:>{num_width}.{digits}f}"
|
|
185
206
|
else:
|
|
186
207
|
line += " " * num_width
|
|
187
208
|
printer(line)
|
|
@@ -191,6 +212,7 @@ def _print_relcov_corpus_table_csv(
|
|
|
191
212
|
corpus_approaches: Sequence[ApproachId],
|
|
192
213
|
table: Mapping[ApproachId, Mapping[ApproachId, float]],
|
|
193
214
|
*,
|
|
215
|
+
digits: int,
|
|
194
216
|
printer: Callable[[str], None],
|
|
195
217
|
) -> None:
|
|
196
218
|
row_labels = sorted(table.keys())
|
|
@@ -201,7 +223,7 @@ def _print_relcov_corpus_table_csv(
|
|
|
201
223
|
cells: list[str] = [str(row)]
|
|
202
224
|
for c in col_labels:
|
|
203
225
|
val = table[row].get(c)
|
|
204
|
-
cells.append(
|
|
226
|
+
cells.append(_format_float(val, digits) if val is not None else "")
|
|
205
227
|
writer.writerow(cells)
|
|
206
228
|
|
|
207
229
|
|
|
@@ -255,6 +277,7 @@ def _print_relcov_corpus_table_latex(
|
|
|
255
277
|
rotate_headers: Optional[float] = None,
|
|
256
278
|
enable_color: bool = False,
|
|
257
279
|
colormap: str = "viridis",
|
|
280
|
+
digits: int,
|
|
258
281
|
printer: Callable[[str], None],
|
|
259
282
|
) -> None:
|
|
260
283
|
try:
|
|
@@ -288,9 +311,11 @@ def _print_relcov_corpus_table_latex(
|
|
|
288
311
|
elif enable_color:
|
|
289
312
|
n = _norm_value(val, min_v, max_v)
|
|
290
313
|
hex_color = _colormap_light_hex(n, colormap=colormap)
|
|
291
|
-
cells.append(
|
|
314
|
+
cells.append(
|
|
315
|
+
rf"\cellcolor[HTML]{{{hex_color}}}{{{_format_float(val, digits)}}}"
|
|
316
|
+
)
|
|
292
317
|
else:
|
|
293
|
-
cells.append(
|
|
318
|
+
cells.append(_format_float(val, digits))
|
|
294
319
|
printer("\t" + " & ".join(cells) + r" \\")
|
|
295
320
|
printer(r"\end{tabular}")
|
|
296
321
|
|
|
@@ -275,3 +275,15 @@ def test_cli_latex_color_no_cell_colors() -> None:
|
|
|
275
275
|
assert any(line.startswith(r"\begin{tabular}") for line in lines)
|
|
276
276
|
# Table should not contain any colored cells
|
|
277
277
|
assert not any(r"\cellcolor" in line for line in lines)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def test_cli_digits_relscore() -> None:
|
|
281
|
+
"""CLI --digits controls decimal places in relscore output."""
|
|
282
|
+
code, out, _ = _run_cli(["--digits", "4", "relscore", str(SAMPLE_SHOWMAP_DIR)])
|
|
283
|
+
assert code == 0
|
|
284
|
+
lines = [s.strip() for s in out.strip().splitlines()]
|
|
285
|
+
# Each score should have exactly 4 digits after the decimal point
|
|
286
|
+
for line in lines:
|
|
287
|
+
score = line.split(":", 1)[1].strip()
|
|
288
|
+
assert "." in score
|
|
289
|
+
assert len(score.split(".", 1)[1]) == 4
|
|
@@ -75,8 +75,8 @@ def test_print_scores_uses_printer_stdout() -> None:
|
|
|
75
75
|
|
|
76
76
|
# Scores should be sorted by value descending, then by name
|
|
77
77
|
joined = "\n".join(captured)
|
|
78
|
-
assert "a: 1.
|
|
79
|
-
assert "b: 0.
|
|
78
|
+
assert "a: 1.000" in joined
|
|
79
|
+
assert "b: 0.100" in joined
|
|
80
80
|
# Ensure our custom printer was used (no direct writes to sys.stdout here)
|
|
81
81
|
assert joined.count("\n") >= 0
|
|
82
82
|
|
|
@@ -97,3 +97,30 @@ def test_print_scores_uses_printer_csv() -> None:
|
|
|
97
97
|
# Order should be by score descending
|
|
98
98
|
assert rows[1][0] == "a"
|
|
99
99
|
assert rows[2][0] == "b"
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_print_scores_respects_digits() -> None:
|
|
103
|
+
"""print_scores formats scores with the requested number of digits."""
|
|
104
|
+
scores = {"a": 1.0 / 3.0}
|
|
105
|
+
captured, printer = _capture_printer()
|
|
106
|
+
|
|
107
|
+
print_scores(scores, output="stdout", digits=4, printer=printer)
|
|
108
|
+
|
|
109
|
+
assert captured == ["a: 0.3333"]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def test_relcov_table_respects_digits_csv() -> None:
|
|
113
|
+
"""Relcov CSV cells use the requested digit precision."""
|
|
114
|
+
captured, printer = _capture_printer()
|
|
115
|
+
print_relcov_corpus_table(
|
|
116
|
+
["a"],
|
|
117
|
+
{"a": {"a": 1.0 / 3.0}},
|
|
118
|
+
output="csv",
|
|
119
|
+
digits=4,
|
|
120
|
+
printer=printer,
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
csv_text = "\n".join(captured)
|
|
124
|
+
reader = csv.reader(io.StringIO(csv_text))
|
|
125
|
+
rows = list(reader)
|
|
126
|
+
assert rows[1][1] == "0.3333"
|
|
File without changes
|
|
File without changes
|
{differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/__init__.py
RENAMED
|
File without changes
|
{differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{differential_coverage-1.2.0 → differential_coverage-1.2.1}/src/differential_coverage/types.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|