differential-coverage 1.0.2__tar.gz → 1.0.4__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.0.2 → differential_coverage-1.0.4}/PKG-INFO +1 -1
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/pyproject.toml +1 -1
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/fs.py +5 -1
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/output.py +70 -27
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage.egg-info/PKG-INFO +1 -1
- differential_coverage-1.0.4/tests/test_output.py +99 -0
- differential_coverage-1.0.2/tests/test_output.py +0 -64
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/README.md +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/setup.cfg +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/__init__.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/api.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/approach_data.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/cli.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/types.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage.egg-info/SOURCES.txt +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage.egg-info/dependency_links.txt +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage.egg-info/entry_points.txt +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage.egg-info/requires.txt +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage.egg-info/top_level.txt +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/tests/test_api.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/tests/test_approach_data.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/tests/test_cli.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/tests/test_docs.py +0 -0
- {differential_coverage-1.0.2 → differential_coverage-1.0.4}/tests/test_reducers.py +0 -0
|
@@ -37,7 +37,11 @@ def read_campaign_dir(
|
|
|
37
37
|
campaigns: dict[str, dict[str, set[str]]] = {}
|
|
38
38
|
for approach_dir in path.iterdir():
|
|
39
39
|
if approach_dir.is_dir():
|
|
40
|
-
|
|
40
|
+
approach_data = read_approach_dir(approach_dir)
|
|
41
|
+
if len(approach_data) == 0:
|
|
42
|
+
print(f"Warning: No coverage data in {approach_dir}. Skipping.")
|
|
43
|
+
continue
|
|
44
|
+
campaigns[approach_dir.name] = approach_data
|
|
41
45
|
else:
|
|
42
46
|
raise ValueError(f"Invalid file: {approach_dir}")
|
|
43
47
|
return campaigns
|
{differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/output.py
RENAMED
|
@@ -3,8 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import csv
|
|
6
|
-
import
|
|
7
|
-
from collections.abc import Mapping, Sequence
|
|
6
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
8
7
|
from typing import Literal, Optional
|
|
9
8
|
|
|
10
9
|
|
|
@@ -13,26 +12,46 @@ from differential_coverage.types import ApproachId
|
|
|
13
12
|
OutputFormat = Literal["stdout", "csv", "latex"]
|
|
14
13
|
|
|
15
14
|
|
|
15
|
+
class _PrinterIO:
|
|
16
|
+
"""Minimal file-like wrapper that forwards writes to a printer function."""
|
|
17
|
+
|
|
18
|
+
def __init__(self, printer: Callable[[str], None]) -> None:
|
|
19
|
+
self._printer = printer
|
|
20
|
+
|
|
21
|
+
def write(self, s: str) -> int: # pragma: no cover - trivial adapter
|
|
22
|
+
# csv.writer may call write multiple times per row; strip trailing newlines
|
|
23
|
+
# and avoid printing empty chunks.
|
|
24
|
+
if not s:
|
|
25
|
+
return 0
|
|
26
|
+
if s.endswith("\n"):
|
|
27
|
+
s = s[:-1]
|
|
28
|
+
if s:
|
|
29
|
+
self._printer(s)
|
|
30
|
+
return len(s)
|
|
31
|
+
|
|
32
|
+
|
|
16
33
|
def print_scores(
|
|
17
34
|
scores: Mapping[ApproachId, float],
|
|
18
35
|
*,
|
|
19
36
|
output: OutputFormat = "stdout",
|
|
20
37
|
colormap: str = "viridis",
|
|
21
38
|
latex_enable_color: bool = False,
|
|
39
|
+
printer: Callable[[str], None] = print,
|
|
22
40
|
) -> None:
|
|
23
41
|
"""Print one score per approach, in the requested output format."""
|
|
24
42
|
name_sorted = sorted(scores.items(), key=lambda x: x[0])
|
|
25
43
|
performance_sorted = sorted(name_sorted, key=lambda x: x[1], reverse=True)
|
|
26
44
|
|
|
27
45
|
if output == "stdout":
|
|
28
|
-
_print_scores_plain(performance_sorted)
|
|
46
|
+
_print_scores_plain(performance_sorted, printer=printer)
|
|
29
47
|
elif output == "csv":
|
|
30
|
-
_print_scores_csv(performance_sorted)
|
|
48
|
+
_print_scores_csv(performance_sorted, printer=printer)
|
|
31
49
|
elif output == "latex":
|
|
32
50
|
_print_scores_latex(
|
|
33
51
|
performance_sorted,
|
|
34
52
|
enable_color=latex_enable_color,
|
|
35
53
|
colormap=colormap,
|
|
54
|
+
printer=printer,
|
|
36
55
|
)
|
|
37
56
|
else:
|
|
38
57
|
raise ValueError(f"Invalid output format: {output}")
|
|
@@ -40,15 +59,19 @@ def print_scores(
|
|
|
40
59
|
|
|
41
60
|
def _print_scores_plain(
|
|
42
61
|
performance_sorted: Sequence[tuple[ApproachId, float]],
|
|
62
|
+
*,
|
|
63
|
+
printer: Callable[[str], None],
|
|
43
64
|
) -> None:
|
|
44
65
|
for approach, score in performance_sorted:
|
|
45
|
-
|
|
66
|
+
printer(f"{approach}: {score:.2f}")
|
|
46
67
|
|
|
47
68
|
|
|
48
69
|
def _print_scores_csv(
|
|
49
70
|
performance_sorted: Sequence[tuple[ApproachId, float]],
|
|
71
|
+
*,
|
|
72
|
+
printer: Callable[[str], None],
|
|
50
73
|
) -> None:
|
|
51
|
-
writer = csv.writer(
|
|
74
|
+
writer = csv.writer(_PrinterIO(printer))
|
|
52
75
|
writer.writerow(("approach", "score"))
|
|
53
76
|
for approach, score in performance_sorted:
|
|
54
77
|
writer.writerow((approach, f"{score:.2f}"))
|
|
@@ -73,14 +96,15 @@ def _print_scores_latex(
|
|
|
73
96
|
*,
|
|
74
97
|
enable_color: bool = False,
|
|
75
98
|
colormap: str = "viridis",
|
|
99
|
+
printer: Callable[[str], None],
|
|
76
100
|
) -> None:
|
|
77
101
|
"""LaTeX table (optionally with score cells colored by value)."""
|
|
78
|
-
|
|
79
|
-
|
|
102
|
+
printer(r"\begin{tabular}{lr}")
|
|
103
|
+
printer(r"approach & score \\")
|
|
80
104
|
if not performance_sorted:
|
|
81
|
-
|
|
105
|
+
printer(r"\end{tabular}")
|
|
82
106
|
return
|
|
83
|
-
|
|
107
|
+
printer(r"\hline")
|
|
84
108
|
min_v, max_v = (0.0, 0.0)
|
|
85
109
|
if enable_color:
|
|
86
110
|
values = [s for _, s in performance_sorted]
|
|
@@ -89,10 +113,10 @@ def _print_scores_latex(
|
|
|
89
113
|
if enable_color:
|
|
90
114
|
n = _norm_value(score, min_v, max_v)
|
|
91
115
|
hex_color = _colormap_light_hex(n, colormap=colormap)
|
|
92
|
-
|
|
116
|
+
printer(rf"{approach} & \cellcolor[HTML]{{{hex_color}}}{{{score:.2f}}} \\")
|
|
93
117
|
else:
|
|
94
|
-
|
|
95
|
-
|
|
118
|
+
printer(f"{approach} & {score:.2f} \\\\")
|
|
119
|
+
printer(r"\end{tabular}")
|
|
96
120
|
|
|
97
121
|
|
|
98
122
|
def print_relcov_corpus_table(
|
|
@@ -103,16 +127,25 @@ def print_relcov_corpus_table(
|
|
|
103
127
|
colormap: str = "viridis",
|
|
104
128
|
latex_rotate_headers: Optional[float] = None,
|
|
105
129
|
latex_enable_color: bool = False,
|
|
130
|
+
printer: Callable[[str], None] = print,
|
|
106
131
|
) -> None:
|
|
107
132
|
"""Print a table of relcov performance: rows = approaches, columns = corpus approaches."""
|
|
108
133
|
if not corpus_approaches:
|
|
109
|
-
|
|
134
|
+
printer("No corpus approaches (single-trial subdirs) found.")
|
|
110
135
|
return
|
|
111
136
|
|
|
112
137
|
if output == "stdout":
|
|
113
|
-
_print_relcov_corpus_table_plain(
|
|
138
|
+
_print_relcov_corpus_table_plain(
|
|
139
|
+
corpus_approaches,
|
|
140
|
+
table,
|
|
141
|
+
printer=printer,
|
|
142
|
+
)
|
|
114
143
|
elif output == "csv":
|
|
115
|
-
_print_relcov_corpus_table_csv(
|
|
144
|
+
_print_relcov_corpus_table_csv(
|
|
145
|
+
corpus_approaches,
|
|
146
|
+
table,
|
|
147
|
+
printer=printer,
|
|
148
|
+
)
|
|
116
149
|
elif output == "latex":
|
|
117
150
|
_print_relcov_corpus_table_latex(
|
|
118
151
|
corpus_approaches,
|
|
@@ -120,6 +153,7 @@ def print_relcov_corpus_table(
|
|
|
120
153
|
rotate_headers=latex_rotate_headers,
|
|
121
154
|
enable_color=latex_enable_color,
|
|
122
155
|
colormap=colormap,
|
|
156
|
+
printer=printer,
|
|
123
157
|
)
|
|
124
158
|
else:
|
|
125
159
|
raise ValueError(f"Invalid output format: {output}")
|
|
@@ -128,6 +162,8 @@ def print_relcov_corpus_table(
|
|
|
128
162
|
def _print_relcov_corpus_table_plain(
|
|
129
163
|
corpus_approaches: Sequence[ApproachId],
|
|
130
164
|
table: Mapping[ApproachId, Mapping[ApproachId, float]],
|
|
165
|
+
*,
|
|
166
|
+
printer: Callable[[str], None],
|
|
131
167
|
) -> None:
|
|
132
168
|
row_labels = sorted(table.keys())
|
|
133
169
|
col_labels = sorted(corpus_approaches)
|
|
@@ -139,7 +175,7 @@ def _print_relcov_corpus_table_plain(
|
|
|
139
175
|
header = "approach".ljust(col_width)
|
|
140
176
|
for c in col_labels:
|
|
141
177
|
header += str(c).rjust(num_width)
|
|
142
|
-
|
|
178
|
+
printer(header)
|
|
143
179
|
for row in row_labels:
|
|
144
180
|
line = str(row).ljust(col_width)
|
|
145
181
|
for c in col_labels:
|
|
@@ -148,16 +184,18 @@ def _print_relcov_corpus_table_plain(
|
|
|
148
184
|
line += f"{val:>{num_width}.5f}"
|
|
149
185
|
else:
|
|
150
186
|
line += " " * num_width
|
|
151
|
-
|
|
187
|
+
printer(line)
|
|
152
188
|
|
|
153
189
|
|
|
154
190
|
def _print_relcov_corpus_table_csv(
|
|
155
191
|
corpus_approaches: Sequence[ApproachId],
|
|
156
192
|
table: Mapping[ApproachId, Mapping[ApproachId, float]],
|
|
193
|
+
*,
|
|
194
|
+
printer: Callable[[str], None],
|
|
157
195
|
) -> None:
|
|
158
196
|
row_labels = sorted(table.keys())
|
|
159
197
|
col_labels = sorted(corpus_approaches)
|
|
160
|
-
writer = csv.writer(
|
|
198
|
+
writer = csv.writer(_PrinterIO(printer))
|
|
161
199
|
writer.writerow(["approach"] + list(col_labels))
|
|
162
200
|
for row in row_labels:
|
|
163
201
|
cells: list[str] = [str(row)]
|
|
@@ -167,7 +205,11 @@ def _print_relcov_corpus_table_csv(
|
|
|
167
205
|
writer.writerow(cells)
|
|
168
206
|
|
|
169
207
|
|
|
170
|
-
def _latex_print_rotcol_command(
|
|
208
|
+
def _latex_print_rotcol_command(
|
|
209
|
+
*,
|
|
210
|
+
angle: Optional[float] = None,
|
|
211
|
+
printer: Callable[[str], None],
|
|
212
|
+
) -> None:
|
|
171
213
|
"""Emit the \\rotcol LaTeX command definition.
|
|
172
214
|
Requires \\usepackage{graphicx} and \\usepackage{calc}.
|
|
173
215
|
Rotated text is raised so it stays in the header row; row height is \\widthof{#1}.
|
|
@@ -177,7 +219,7 @@ def _latex_print_rotcol_command(*, angle: Optional[float] = None) -> None:
|
|
|
177
219
|
|
|
178
220
|
# Raise rotated text by half its width so it doesn't extend below the baseline
|
|
179
221
|
# into the first data row; then reserve full width as row height.
|
|
180
|
-
|
|
222
|
+
printer(
|
|
181
223
|
r"""\newcolumntype{R}[2]{%
|
|
182
224
|
>{\adjustbox{angle=#1,lap=\width-(#2)}\bgroup}%
|
|
183
225
|
l%
|
|
@@ -213,6 +255,7 @@ def _print_relcov_corpus_table_latex(
|
|
|
213
255
|
rotate_headers: Optional[float] = None,
|
|
214
256
|
enable_color: bool = False,
|
|
215
257
|
colormap: str = "viridis",
|
|
258
|
+
printer: Callable[[str], None],
|
|
216
259
|
) -> None:
|
|
217
260
|
try:
|
|
218
261
|
from pylatex.utils import escape_latex # type: ignore[import-untyped] # does not provide types
|
|
@@ -229,13 +272,13 @@ def _print_relcov_corpus_table_latex(
|
|
|
229
272
|
|
|
230
273
|
num_cols = 1 + len(col_labels)
|
|
231
274
|
align_spec = "l" + "r" * (num_cols - 1)
|
|
232
|
-
_latex_print_rotcol_command(angle=rotate_headers)
|
|
233
|
-
|
|
275
|
+
_latex_print_rotcol_command(angle=rotate_headers, printer=printer)
|
|
276
|
+
printer(r"\begin{tabular}{" + align_spec + r"}")
|
|
234
277
|
header_cells = [""] + [
|
|
235
278
|
_latex_rotcol(escape_latex(str(c)), angle=rotate_headers) for c in col_labels
|
|
236
279
|
]
|
|
237
|
-
|
|
238
|
-
|
|
280
|
+
printer("\t" + " & ".join(header_cells) + r" \\")
|
|
281
|
+
printer(r"\hline")
|
|
239
282
|
for row in row_labels:
|
|
240
283
|
cells: list[str] = [escape_latex(str(row))]
|
|
241
284
|
for c in col_labels:
|
|
@@ -248,8 +291,8 @@ def _print_relcov_corpus_table_latex(
|
|
|
248
291
|
cells.append(rf"\cellcolor[HTML]{{{hex_color}}}{{{val:.3f}}}")
|
|
249
292
|
else:
|
|
250
293
|
cells.append(f"{val:.3f}")
|
|
251
|
-
|
|
252
|
-
|
|
294
|
+
printer("\t" + " & ".join(cells) + r" \\")
|
|
295
|
+
printer(r"\end{tabular}")
|
|
253
296
|
|
|
254
297
|
|
|
255
298
|
def _colormap_light_hex(t: float, *, colormap: str = "viridis") -> str:
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Unit tests for differential_coverage output helpers."""
|
|
2
|
+
|
|
3
|
+
import csv
|
|
4
|
+
import io
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
from differential_coverage.output import print_relcov_corpus_table, print_scores
|
|
8
|
+
|
|
9
|
+
# Deliberately unsorted order for both corpus (columns) and table (rows)
|
|
10
|
+
_UNSORTED_CORPUS = ["z_approach", "a_approach", "m_approach"]
|
|
11
|
+
_UNSORTED_TABLE = {
|
|
12
|
+
"z_approach": {"z_approach": 1.0, "a_approach": 0.2, "m_approach": 0.3},
|
|
13
|
+
"a_approach": {"z_approach": 0.1, "a_approach": 1.0, "m_approach": 0.4},
|
|
14
|
+
"m_approach": {"z_approach": 0.5, "a_approach": 0.6, "m_approach": 1.0},
|
|
15
|
+
}
|
|
16
|
+
_EXPECTED_ROWS = ["a_approach", "m_approach", "z_approach"]
|
|
17
|
+
_EXPECTED_COLS = ["a_approach", "m_approach", "z_approach"]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _capture_printer() -> tuple[list[str], Callable[[str], None]]:
|
|
21
|
+
lines: list[str] = []
|
|
22
|
+
|
|
23
|
+
def printer(s: str) -> None:
|
|
24
|
+
lines.append(s)
|
|
25
|
+
|
|
26
|
+
return lines, printer
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_relcov_table_sorted_stdout() -> None:
|
|
30
|
+
"""Relcov table stdout output has rows and columns sorted alphabetically."""
|
|
31
|
+
captured, printer = _capture_printer()
|
|
32
|
+
print_relcov_corpus_table(
|
|
33
|
+
_UNSORTED_CORPUS,
|
|
34
|
+
_UNSORTED_TABLE,
|
|
35
|
+
output="stdout",
|
|
36
|
+
printer=printer,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
lines = [s.strip() for s in "\n".join(captured).strip().splitlines()]
|
|
40
|
+
header = lines[0]
|
|
41
|
+
assert header.startswith("approach")
|
|
42
|
+
row_labels = [line.split()[0] for line in lines[1:]]
|
|
43
|
+
assert row_labels == _EXPECTED_ROWS
|
|
44
|
+
pos = 0
|
|
45
|
+
for col in _EXPECTED_COLS:
|
|
46
|
+
idx = header.find(col, pos)
|
|
47
|
+
assert idx >= 0, f"column {col!r} not found in header"
|
|
48
|
+
pos = idx + len(col)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_relcov_table_sorted_csv() -> None:
|
|
52
|
+
"""Relcov table CSV output has rows and columns sorted alphabetically."""
|
|
53
|
+
captured, printer = _capture_printer()
|
|
54
|
+
print_relcov_corpus_table(
|
|
55
|
+
_UNSORTED_CORPUS,
|
|
56
|
+
_UNSORTED_TABLE,
|
|
57
|
+
output="csv",
|
|
58
|
+
printer=printer,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
csv_text = "\n".join(captured)
|
|
62
|
+
reader = csv.reader(io.StringIO(csv_text))
|
|
63
|
+
rows = list(reader)
|
|
64
|
+
assert rows[0][0] == "approach"
|
|
65
|
+
assert rows[0][1:] == _EXPECTED_COLS
|
|
66
|
+
assert [r[0] for r in rows[1:]] == _EXPECTED_ROWS
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_print_scores_uses_printer_stdout() -> None:
|
|
70
|
+
"""print_scores writes human-readable scores via the provided printer."""
|
|
71
|
+
scores = {"b": 0.1, "a": 1.0}
|
|
72
|
+
captured, printer = _capture_printer()
|
|
73
|
+
|
|
74
|
+
print_scores(scores, output="stdout", printer=printer)
|
|
75
|
+
|
|
76
|
+
# Scores should be sorted by value descending, then by name
|
|
77
|
+
joined = "\n".join(captured)
|
|
78
|
+
assert "a: 1.00" in joined
|
|
79
|
+
assert "b: 0.10" in joined
|
|
80
|
+
# Ensure our custom printer was used (no direct writes to sys.stdout here)
|
|
81
|
+
assert joined.count("\n") >= 0
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_print_scores_uses_printer_csv() -> None:
|
|
85
|
+
"""print_scores CSV output is routed through the provided printer."""
|
|
86
|
+
scores = {"b": 0.1, "a": 1.0}
|
|
87
|
+
captured, printer = _capture_printer()
|
|
88
|
+
|
|
89
|
+
print_scores(scores, output="csv", printer=printer)
|
|
90
|
+
|
|
91
|
+
csv_text = "\n".join(captured)
|
|
92
|
+
reader = csv.reader(io.StringIO(csv_text))
|
|
93
|
+
rows = list(reader)
|
|
94
|
+
|
|
95
|
+
# Header + two data rows
|
|
96
|
+
assert rows[0] == ["approach", "score"]
|
|
97
|
+
# Order should be by score descending
|
|
98
|
+
assert rows[1][0] == "a"
|
|
99
|
+
assert rows[2][0] == "b"
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
"""Unit tests for differential_coverage output helpers."""
|
|
2
|
-
|
|
3
|
-
import csv
|
|
4
|
-
import io
|
|
5
|
-
import sys
|
|
6
|
-
|
|
7
|
-
from differential_coverage.output import print_relcov_corpus_table
|
|
8
|
-
|
|
9
|
-
# Deliberately unsorted order for both corpus (columns) and table (rows)
|
|
10
|
-
_UNSORTED_CORPUS = ["z_approach", "a_approach", "m_approach"]
|
|
11
|
-
_UNSORTED_TABLE = {
|
|
12
|
-
"z_approach": {"z_approach": 1.0, "a_approach": 0.2, "m_approach": 0.3},
|
|
13
|
-
"a_approach": {"z_approach": 0.1, "a_approach": 1.0, "m_approach": 0.4},
|
|
14
|
-
"m_approach": {"z_approach": 0.5, "a_approach": 0.6, "m_approach": 1.0},
|
|
15
|
-
}
|
|
16
|
-
_EXPECTED_ROWS = ["a_approach", "m_approach", "z_approach"]
|
|
17
|
-
_EXPECTED_COLS = ["a_approach", "m_approach", "z_approach"]
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def test_relcov_table_sorted_stdout() -> None:
|
|
21
|
-
"""Relcov table stdout output has rows and columns sorted alphabetically."""
|
|
22
|
-
out = io.StringIO()
|
|
23
|
-
old_stdout = sys.stdout
|
|
24
|
-
try:
|
|
25
|
-
sys.stdout = out
|
|
26
|
-
print_relcov_corpus_table(
|
|
27
|
-
_UNSORTED_CORPUS,
|
|
28
|
-
_UNSORTED_TABLE,
|
|
29
|
-
output="stdout",
|
|
30
|
-
)
|
|
31
|
-
finally:
|
|
32
|
-
sys.stdout = old_stdout
|
|
33
|
-
|
|
34
|
-
lines = [s.strip() for s in out.getvalue().strip().splitlines()]
|
|
35
|
-
header = lines[0]
|
|
36
|
-
assert header.startswith("approach")
|
|
37
|
-
row_labels = [line.split()[0] for line in lines[1:]]
|
|
38
|
-
assert row_labels == _EXPECTED_ROWS
|
|
39
|
-
pos = 0
|
|
40
|
-
for col in _EXPECTED_COLS:
|
|
41
|
-
idx = header.find(col, pos)
|
|
42
|
-
assert idx >= 0, f"column {col!r} not found in header"
|
|
43
|
-
pos = idx + len(col)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
def test_relcov_table_sorted_csv() -> None:
|
|
47
|
-
"""Relcov table CSV output has rows and columns sorted alphabetically."""
|
|
48
|
-
out = io.StringIO()
|
|
49
|
-
old_stdout = sys.stdout
|
|
50
|
-
try:
|
|
51
|
-
sys.stdout = out
|
|
52
|
-
print_relcov_corpus_table(
|
|
53
|
-
_UNSORTED_CORPUS,
|
|
54
|
-
_UNSORTED_TABLE,
|
|
55
|
-
output="csv",
|
|
56
|
-
)
|
|
57
|
-
finally:
|
|
58
|
-
sys.stdout = old_stdout
|
|
59
|
-
|
|
60
|
-
reader = csv.reader(io.StringIO(out.getvalue()))
|
|
61
|
-
rows = list(reader)
|
|
62
|
-
assert rows[0][0] == "approach"
|
|
63
|
-
assert rows[0][1:] == _EXPECTED_COLS
|
|
64
|
-
assert [r[0] for r in rows[1:]] == _EXPECTED_ROWS
|
|
File without changes
|
|
File without changes
|
{differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/__init__.py
RENAMED
|
File without changes
|
{differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/api.py
RENAMED
|
File without changes
|
|
File without changes
|
{differential_coverage-1.0.2 → differential_coverage-1.0.4}/src/differential_coverage/cli.py
RENAMED
|
File without changes
|
{differential_coverage-1.0.2 → differential_coverage-1.0.4}/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
|