xcodebuild-axi 0.1.10 → 0.1.11

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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,79 @@ Notable changes to `xcodebuild-axi`. Versions follow
4
4
  [semver](https://semver.org/); the format follows
5
5
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [0.1.11] - 2026-09-21
8
+
9
+ ### Added
10
+
11
+ - **`result --export <what>` gets files out of a bundle**: `attachments`,
12
+ `diagnostics`, `metrics`, or `evaluations`. A UI test's screenshots, the
13
+ diagnostics report `test --diagnostics` collects, and the CSVs behind a
14
+ performance measurement were all in the bundle and reachable only by shelling
15
+ out to `xcresulttool`.
16
+
17
+ It writes under `~/Library/Caches/xcodebuild-axi/exports/` by default, or
18
+ wherever `--to` says — never into the working tree, so asking what is in a CI
19
+ artifact cannot dirty the checkout it sits in. `--test`, `--filter '*.png'`
20
+ and `--failures` narrow what comes out, and each is **refused** where it does
21
+ not apply rather than ignored: a diagnostics report covers the whole run, so
22
+ `--test` beside it would answer a different question than the one asked.
23
+
24
+ The report is what landed and where, with each exported file tied back to the
25
+ test that produced it — a screenshot's filename is a UUID, and the manifest
26
+ is the only thing that makes it findable. `xcresulttool` narrates an
27
+ attachment export with one "Skipped export for <test>: no matching
28
+ attachments" per test, which is dropped.
29
+
30
+ - **`result --against <baseline.xcresult>`** answers the question CI actually
31
+ asks — not "did this run fail" but "did it fail in a way the last one did
32
+ not". It reports the counts on both sides and their direction
33
+ (`failures: 0 → 1 (+1 -0)`), then the tests that newly fail, the ones that
34
+ now pass, the ones added and removed, and any new warning. A failure the
35
+ baseline did not have is listed first, because it is the only part of a
36
+ comparison that stops a merge.
37
+
38
+ Two bundles with nothing in common — a build against a test run — get an
39
+ explicit answer saying so. `xcresulttool` prints a bare `null` there, which
40
+ is otherwise indistinguishable from a clean comparison.
41
+
42
+ - **`result <a> <b> [...] --merge`** combines the bundles of a sharded test run
43
+ into one, which is the only way that run gets a single verdict. The merged
44
+ bundle is read back and reported from, rather than described from its
45
+ inputs. It lands under `~/Library/Caches` unless `--to` says otherwise, and a
46
+ `--to` that already exists is refused rather than written over — the bundle
47
+ under our own cache directory is ours to clear, one the caller named is not.
48
+
49
+ With these two, **`xcresulttool` coverage is 81% of its leaves** and every
50
+ remaining one is deprecated by Xcode or superseded here.
51
+
52
+ - **`coverage` sees everything `xccov` sees.** Five leaves, and the command's
53
+ path argument now also takes an `.xccovreport` or an `.xccovarchive`:
54
+
55
+ - `--functions <file>` is per-function coverage, which is where an uncovered
56
+ branch finally gets a name. A file at 60% says a test is missing; it does
57
+ not say which one.
58
+ - `--lines <file>` is how many times each line ran, read out of the archive
59
+ rather than the report. Consecutive lines that never ran collapse into one
60
+ range — a 400-line file was 400 rows to find the eight that matter.
61
+ - `--against <path>` answers "did coverage drop" from two bundles this tool
62
+ wrote, reporting which way it moved overall and then per file, worst
63
+ first.
64
+ - `--merge` combines the coverage of a sharded run. `xccov merge` takes
65
+ report/archive **pairs** rather than result bundles, so each bundle is
66
+ unpacked with `xcresulttool export coverage` first — that two-step is the
67
+ reason merging coverage across shards is something people give up on.
68
+
69
+ With them, **`xccov` coverage is 100%** and companion coverage overall is
70
+ 43.7%.
71
+
72
+ ### Fixed
73
+
74
+ - `xccov`'s refusals are no longer wrapped in NSError. A bad path came back as
75
+ `Error: Error Domain=XCCovErrorDomain Code=0 "Failed to load result bundle"
76
+ UserInfo={NSLocalizedDescription=Failed to load result bundle,
77
+ NSUnderlyingError=0x… {…}}` — 400 characters around a five-word answer that
78
+ was already in there.
79
+
7
80
  ## [0.1.10] - 2026-09-21
8
81
 
9
82
  ### Added
@@ -370,7 +443,8 @@ First release.
370
443
  - 100% of the 117 options `xcodebuild -help` lists are covered, declared in
371
444
  `src/surface.ts` and checked against the installed Xcode in CI.
372
445
 
373
- [unreleased]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.10...HEAD
446
+ [unreleased]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.11...HEAD
447
+ [0.1.11]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.10...v0.1.11
374
448
  [0.1.10]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.9...v0.1.10
375
449
  [0.1.9]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.8...v0.1.9
376
450
  [0.1.8]: https://github.com/alexrrouse/xcodebuild-axi/compare/v0.1.7...v0.1.8
package/README.md CHANGED
@@ -204,44 +204,33 @@ The one action left out is `installsrc` — it copies sources into `SRCROOT` as
204
204
 
205
205
  ### Companion tools
206
206
 
207
- `xcresulttool`, `xccov` and `simctl` are not xcodebuild, so they are not in the number above — but this tool wraps all three, and an agent that has to shell out to one directly has dropped back down. **28.2% of 71 leaves**, counted the same way:
207
+ `xcresulttool`, `xccov` and `simctl` are not xcodebuild, so they are not in the number above — but this tool wraps all three, and an agent that has to shell out to one directly has dropped back down. **43.7% of 71 leaves**, counted the same way:
208
208
 
209
- | Tool | Leaves | Covered |
210
- | -------------- | ------ | ---------- |
211
- | `xcresulttool` | 21 | 11 (52.4%) |
212
- | `xccov` | 9 | 4 (44.4%) |
213
- | `simctl` | 41 | 5 (12.2%) |
209
+ | Tool | Leaves | Covered |
210
+ | -------------- | ------ | --------- |
211
+ | `xcresulttool` | 21 | 17 (81%) |
212
+ | `xccov` | 9 | 9 (100%) |
213
+ | `simctl` | 41 | 5 (12.2%) |
214
214
 
215
215
  ### Still open
216
216
 
217
- 24 leaves are known gaps rather than decisions — each one a reason someone would still reach for the raw tool:
218
-
219
- | Leaf | Unreachable from | What that costs |
220
- | ------------------------------------------ | ---------------- | --------------------------------------------------------------------------- |
221
- | `xcresulttool export diagnostics` | `result` | `test --diagnostics` collects a diagnostics report that cannot be extracted |
222
- | `xcresulttool export attachments` | `result` | UI test screenshots and attachments cannot be got out of the bundle |
223
- | `xcresulttool export metrics` | `result` | performance measurements cannot be exported as CSV |
224
- | `xcresulttool export evaluations` | `result` | evaluation attachments cannot be exported |
225
- | `xcresulttool compare` | `result` | two runs cannot be diffed, which is the question CI asks most |
226
- | `xcresulttool merge` | `result` | the bundles of a sharded test run cannot be combined |
227
- | `xccov view --report --functions-for-file` | `coverage` | coverage stops at the file, so the uncovered function has no name |
228
- | `xccov view --archive` | `coverage` | a standalone .xccovarchive cannot be read, only a result bundle |
229
- | `xccov view --file` | `coverage` | the per-line coverage of one file cannot be printed |
230
- | `xccov diff` | `coverage` | 'did coverage drop' cannot be answered from two bundles this tool wrote |
231
- | `xccov merge` | `coverage` | the coverage of a sharded run cannot be combined |
232
- | `simctl install` | `sim` | a built .app cannot be put on the simulator it was built for |
233
- | `simctl launch` | `sim` | the app a build just produced cannot be run |
234
- | `simctl terminate` | `?` | a running app cannot be stopped |
235
- | `simctl uninstall` | `?` | an installed app cannot be removed |
236
- | `simctl listapps` | `sim` | what is installed on a simulator cannot be listed |
237
- | `simctl create` | `?` | a missing device cannot be created |
238
- | `simctl delete` | `sim` | stale devices cannot be reclaimed, and they cost gigabytes |
239
- | `simctl io` | `sim` | a screenshot of a failing UI cannot be taken |
240
- | `simctl openurl` | `sim` | a deep link cannot be opened, which is how deep links are tested |
241
- | `simctl privacy` | `sim` | a permission prompt cannot be granted ahead of a UI test |
242
- | `simctl push` | `sim` | a push notification cannot be simulated |
243
- | `simctl status_bar` | `sim` | the status bar cannot be pinned, which screenshot tests need |
244
- | `simctl ui` | `sim` | dark mode and content size cannot be set for a test run |
217
+ 13 leaves are known gaps rather than decisions — each one a reason someone would still reach for the raw tool:
218
+
219
+ | Leaf | Unreachable from | What that costs |
220
+ | ------------------- | ---------------- | ---------------------------------------------------------------- |
221
+ | `simctl install` | `sim` | a built .app cannot be put on the simulator it was built for |
222
+ | `simctl launch` | `sim` | the app a build just produced cannot be run |
223
+ | `simctl terminate` | `?` | a running app cannot be stopped |
224
+ | `simctl uninstall` | `?` | an installed app cannot be removed |
225
+ | `simctl listapps` | `sim` | what is installed on a simulator cannot be listed |
226
+ | `simctl create` | `?` | a missing device cannot be created |
227
+ | `simctl delete` | `sim` | stale devices cannot be reclaimed, and they cost gigabytes |
228
+ | `simctl io` | `sim` | a screenshot of a failing UI cannot be taken |
229
+ | `simctl openurl` | `sim` | a deep link cannot be opened, which is how deep links are tested |
230
+ | `simctl privacy` | `sim` | a permission prompt cannot be granted ahead of a UI test |
231
+ | `simctl push` | `sim` | a push notification cannot be simulated |
232
+ | `simctl status_bar` | `sim` | the status bar cannot be pinned, which screenshot tests need |
233
+ | `simctl ui` | `sim` | dark mode and content size cannot be set for a test run |
245
234
 
246
235
  The denominator is read from `xcodebuild -help` rather than hand-maintained, and this table is written against **Xcode 27.0** — the option list moves between releases. `npm run coverage:check` fails on that Xcode if an option here is unclassified or has been dropped, and reports the difference without failing on any other.
247
236
 
@@ -1,3 +1,14 @@
1
- export declare const COVERAGE_HELP = "usage: xcodebuild-axi coverage <path.xcresult> [flags]\nReads code coverage out of a result bundle \u2014 one percentage per target, not the\nthousands of per-file lines xccov prints by default.\nflags[4]:\n --files list per-file coverage as well as per-target\n --target <name> only this target\n --below <percent> only files under this coverage, e.g. --below 50\n --max <n> rows to list before summarizing the rest (default: 25)\nnote:\n Coverage has to have been collected. Run the tests with\n `xcodebuild-axi test --coverage` if the bundle has none.\nexamples:\n xcodebuild-axi coverage ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-test.xcresult\n xcodebuild-axi coverage build/MyApp.xcresult --files --below 50\n";
2
- export declare const COVERAGE_FLAGS: readonly ["--files", "--target", "--below", "--max"];
1
+ import { type CoverageLine } from "../xccov.js";
2
+ export declare const COVERAGE_HELP = "usage: xcodebuild-axi coverage <path> [flags]\nReads code coverage out of a result bundle \u2014 one percentage per target, not the\nthousands of per-file lines xccov prints by default. The path can also be an\n.xccovreport or .xccovarchive.\nflags[9]:\n --files list per-file coverage as well as per-target\n --target <name> only this target\n --below <percent> only files under this coverage, e.g. --below 50\n --functions <file> per-function coverage for one file, so an uncovered\n branch has a name\n --lines <file> how many times each line of one file ran\n --against <path> compare coverage with an earlier run: what moved, and\n which files went with it\n --merge combine the coverage of two or more runs into one report\n --to <path> where --merge writes (default: under ~/Library/Caches)\n --max <n> rows to list before summarizing the rest (default: 25)\nnote:\n Coverage has to have been collected. Run the tests with\n `xcodebuild-axi test --coverage` if the bundle has none.\n\n --lines reads the archive rather than the report: the report knows what\n percentage of a file ran, the archive knows how many times each line did.\nexamples:\n xcodebuild-axi coverage ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-test.xcresult\n xcodebuild-axi coverage build/MyApp.xcresult --files --below 50\n xcodebuild-axi coverage build/MyApp.xcresult --functions Checkout.swift\n xcodebuild-axi coverage build/MyApp.xcresult --against build/baseline.xcresult\n";
3
+ export declare const COVERAGE_FLAGS: readonly ["--files", "--target", "--below", "--functions", "--lines", "--against", "--merge", "--to", "--max"];
3
4
  export declare function coverageCommand(args: string[]): Promise<string>;
5
+ /** Consecutive lines that never ran, as ranges rather than one row each. */
6
+ export declare function uncoveredRanges(lines: CoverageLine[]): Array<Record<string, unknown>>;
7
+ /**
8
+ * Which way coverage moved, in words.
9
+ *
10
+ * Words rather than a signed percentage because TOON quotes any scalar that
11
+ * starts with `-`, and `"-2.1%"` costs more in quotes than `down 2.1%` costs
12
+ * in letters.
13
+ */
14
+ export declare function movement(delta: number): string;
@@ -1,31 +1,59 @@
1
- import { resolve } from "node:path";
1
+ import { existsSync, mkdirSync, rmSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
2
3
  import { AxiError } from "../errors.js";
3
- import { percent, readCoverage } from "../xccov.js";
4
+ import { coverageSource, mergeCoverage, percent, readArchiveFiles, readCoverage, readCoverageDiff, readFileLines, readFunctions, } from "../xccov.js";
4
5
  import { relativize } from "../xcresult.js";
6
+ import { mergedCoveragePath } from "../xcodebuild.js";
5
7
  import { renderFields, renderHelp, renderList, renderOutput, tildePath, } from "../toon.js";
6
8
  import { getFlag, getIntFlag, hasFlag, positionals, rejectUnknownFlags, } from "../args.js";
7
- export const COVERAGE_HELP = `usage: xcodebuild-axi coverage <path.xcresult> [flags]
9
+ export const COVERAGE_HELP = `usage: xcodebuild-axi coverage <path> [flags]
8
10
  Reads code coverage out of a result bundle — one percentage per target, not the
9
- thousands of per-file lines xccov prints by default.
10
- flags[4]:
11
+ thousands of per-file lines xccov prints by default. The path can also be an
12
+ .xccovreport or .xccovarchive.
13
+ flags[9]:
11
14
  --files list per-file coverage as well as per-target
12
15
  --target <name> only this target
13
16
  --below <percent> only files under this coverage, e.g. --below 50
17
+ --functions <file> per-function coverage for one file, so an uncovered
18
+ branch has a name
19
+ --lines <file> how many times each line of one file ran
20
+ --against <path> compare coverage with an earlier run: what moved, and
21
+ which files went with it
22
+ --merge combine the coverage of two or more runs into one report
23
+ --to <path> where --merge writes (default: under ~/Library/Caches)
14
24
  --max <n> rows to list before summarizing the rest (default: 25)
15
25
  note:
16
26
  Coverage has to have been collected. Run the tests with
17
27
  \`xcodebuild-axi test --coverage\` if the bundle has none.
28
+
29
+ --lines reads the archive rather than the report: the report knows what
30
+ percentage of a file ran, the archive knows how many times each line did.
18
31
  examples:
19
32
  xcodebuild-axi coverage ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-test.xcresult
20
33
  xcodebuild-axi coverage build/MyApp.xcresult --files --below 50
34
+ xcodebuild-axi coverage build/MyApp.xcresult --functions Checkout.swift
35
+ xcodebuild-axi coverage build/MyApp.xcresult --against build/baseline.xcresult
21
36
  `;
22
37
  export const COVERAGE_FLAGS = [
23
38
  "--files",
24
39
  "--target",
25
40
  "--below",
41
+ "--functions",
42
+ "--lines",
43
+ "--against",
44
+ "--merge",
45
+ "--to",
46
+ "--max",
47
+ ];
48
+ const VALUE_FLAGS = [
49
+ "--target",
50
+ "--below",
51
+ "--functions",
52
+ "--lines",
53
+ "--against",
54
+ "--to",
26
55
  "--max",
27
56
  ];
28
- const VALUE_FLAGS = ["--target", "--below", "--max"];
29
57
  export async function coverageCommand(args) {
30
58
  rejectUnknownFlags(args, "coverage", COVERAGE_FLAGS, VALUE_FLAGS);
31
59
  const [rawPath] = positionals(args, VALUE_FLAGS);
@@ -38,6 +66,14 @@ export async function coverageCommand(args) {
38
66
  const path = resolve(expandTilde(rawPath));
39
67
  const below = getIntFlag(args, "--below");
40
68
  const max = getIntFlag(args, "--max") ?? 25;
69
+ const mode = soleMode(args);
70
+ if (mode)
71
+ return readMode(mode, path, args, max);
72
+ // An archive holds raw execution counts and no report at all, so the
73
+ // percentages the default view is made of do not exist for one. What it can
74
+ // answer is which files it covers, which is also what --lines takes.
75
+ if (coverageSource(path) === "archive")
76
+ return reportArchive(path, max);
41
77
  const targetFilter = getFlag(args, "--target")?.toLowerCase();
42
78
  // A --below filter is meaningless without the files it filters, so imply it
43
79
  // rather than returning an empty list the agent has to debug.
@@ -100,7 +136,7 @@ export async function coverageCommand(args) {
100
136
  : "files", shown));
101
137
  }
102
138
  }
103
- blocks.push(renderFields({ bundle: tildePath(path) }));
139
+ blocks.push(renderFields(sourceField(path)));
104
140
  const hints = [];
105
141
  if (!wantFiles) {
106
142
  hints.push(`Run \`xcodebuild-axi coverage ${rawPath} --files\` for per-file coverage`);
@@ -109,6 +145,277 @@ export async function coverageCommand(args) {
109
145
  blocks.push(renderHelp(hints));
110
146
  return renderOutput(blocks);
111
147
  }
148
+ /**
149
+ * Name the path by what it is. A `.xccovreport` reported as `bundle:` is a
150
+ * path the next call cannot reuse the same way.
151
+ */
152
+ function sourceField(path) {
153
+ const kind = coverageSource(path);
154
+ return { [kind]: tildePath(path) };
155
+ }
156
+ /** What an .xccovarchive can answer on its own: which files it holds. */
157
+ async function reportArchive(path, max) {
158
+ const files = await readArchiveFiles(path);
159
+ const shown = files.slice(0, max).map((file) => ({ file: relativize(file) }));
160
+ return renderOutput([
161
+ renderFields({ archive: tildePath(path), files: files.length }),
162
+ files.length > 0
163
+ ? renderList(files.length > shown.length
164
+ ? `files (${shown.length} of ${files.length})`
165
+ : "files", shown)
166
+ : renderFields({ files: "none — this archive holds no coverage" }),
167
+ renderHelp([
168
+ `Run \`xcodebuild-axi coverage ${tildePath(path)} --lines <file>\` for the lines that never ran`,
169
+ ]),
170
+ ]);
171
+ }
172
+ /** The one coverage question this invocation is for, if not the default. */
173
+ const MODES = ["--functions", "--lines", "--against", "--merge"];
174
+ function soleMode(args) {
175
+ const asked = MODES.filter((mode) => args.includes(mode));
176
+ if (asked.length > 1) {
177
+ throw new AxiError(`${asked.join(" and ")} ask different questions — pass one`, "VALIDATION_ERROR", [`each is its own read of the coverage data: ${MODES.join(", ")}`]);
178
+ }
179
+ return asked[0];
180
+ }
181
+ async function readMode(mode, path, args, max) {
182
+ switch (mode) {
183
+ case "--functions":
184
+ return reportFunctions(path, requireValue(args, "--functions"), max);
185
+ case "--lines":
186
+ return reportLines(path, requireValue(args, "--lines"), max);
187
+ case "--against":
188
+ return reportDiff(path, resolve(expandTilde(requireValue(args, "--against"))), max);
189
+ case "--merge":
190
+ return runMerge(path, args, max);
191
+ }
192
+ }
193
+ function requireValue(args, flag) {
194
+ const value = getFlag(args, flag);
195
+ if (value === undefined) {
196
+ throw new AxiError(`${flag} needs a value`, "VALIDATION_ERROR", [
197
+ `xcodebuild-axi coverage <path> ${flag} <value>`,
198
+ ]);
199
+ }
200
+ return value;
201
+ }
202
+ /**
203
+ * Per-function coverage, which is where an uncovered branch gets a name.
204
+ *
205
+ * A file at 60% says a test is missing; it does not say which one. The
206
+ * function list does, and it is sorted with the least covered first because
207
+ * that is the row someone is looking for.
208
+ */
209
+ async function reportFunctions(path, file, max) {
210
+ const found = await readFunctions(path, file);
211
+ const functions = found.flatMap((entry) => entry.functions);
212
+ if (functions.length === 0) {
213
+ return renderOutput([
214
+ renderFields({
215
+ functions: `no coverage recorded for a file named '${file}'`,
216
+ }),
217
+ renderFields(sourceField(path)),
218
+ renderHelp([
219
+ `Run \`xcodebuild-axi coverage ${tildePath(path)} --files\` to see which files the report covers`,
220
+ "The name is matched against the path the file had when the report was written",
221
+ ]),
222
+ ]);
223
+ }
224
+ const sorted = functions
225
+ .slice()
226
+ .sort((a, b) => a.lineCoverage - b.lineCoverage);
227
+ const shown = sorted.slice(0, max).map(functionRow);
228
+ return renderOutput([
229
+ renderFields({
230
+ file: relativize(found[0]?.file ?? file),
231
+ functions: functions.length,
232
+ uncovered: functions.filter((fn) => fn.executionCount === 0).length,
233
+ }),
234
+ renderList(sorted.length > shown.length
235
+ ? `functions (${shown.length} of ${sorted.length})`
236
+ : "functions", shown),
237
+ renderFields(sourceField(path)),
238
+ ]);
239
+ }
240
+ function functionRow(fn) {
241
+ return {
242
+ function: fn.name,
243
+ line: fn.lineNumber,
244
+ coverage: percent(fn.lineCoverage),
245
+ lines: `${fn.coveredLines}/${fn.executableLines}`,
246
+ runs: fn.executionCount,
247
+ };
248
+ }
249
+ /**
250
+ * Per-line execution counts, reported as the ranges that did not run.
251
+ *
252
+ * Printing one row per line is the thing `xccov` already does and the thing
253
+ * nobody can read: a 400-line file is 400 rows to find the eight that matter.
254
+ * Consecutive uncovered lines collapse into a range instead.
255
+ */
256
+ async function reportLines(path, file, max) {
257
+ const lines = await readFileLines(path, file).catch(async (error) => {
258
+ // xccov answers an unknown file with a load failure rather than a list, so
259
+ // the list is what the refusal carries.
260
+ const candidates = await readArchiveFiles(path).catch(() => []);
261
+ if (candidates.length === 0)
262
+ throw error;
263
+ const near = candidates.filter((candidate) => candidate.includes(file));
264
+ throw new AxiError(`No coverage recorded for '${file}'`, "VALIDATION_ERROR", near.length > 0
265
+ ? [`did you mean: ${near.slice(0, 5).join(", ")}`]
266
+ : [
267
+ `Run \`xcodebuild-axi coverage ${tildePath(path)} --files\` to see the files this archive holds`,
268
+ ]);
269
+ });
270
+ const executable = lines.filter((line) => line.isExecutable);
271
+ const covered = executable.filter((line) => line.executionCount > 0).length;
272
+ const gaps = uncoveredRanges(executable);
273
+ const shown = gaps.slice(0, max);
274
+ return renderOutput([
275
+ renderFields({
276
+ file: relativize(file),
277
+ coverage: percent(executable.length > 0 ? covered / executable.length : 0),
278
+ lines: `${covered} of ${executable.length} covered`,
279
+ }),
280
+ gaps.length === 0
281
+ ? renderFields({ uncovered: "none — every executable line ran" })
282
+ : renderList(gaps.length > shown.length
283
+ ? `uncovered (${shown.length} of ${gaps.length})`
284
+ : "uncovered", shown),
285
+ renderFields(sourceField(path)),
286
+ ]);
287
+ }
288
+ /** Consecutive lines that never ran, as ranges rather than one row each. */
289
+ export function uncoveredRanges(lines) {
290
+ const ranges = [];
291
+ for (const line of lines) {
292
+ if (line.executionCount > 0)
293
+ continue;
294
+ const last = ranges[ranges.length - 1];
295
+ if (last && line.line === last.to + 1)
296
+ last.to = line.line;
297
+ else
298
+ ranges.push({ from: line.line, to: line.line });
299
+ }
300
+ return ranges.map((range) => ({
301
+ lines: range.from === range.to ? `${range.from}` : `${range.from}-${range.to}`,
302
+ count: range.to - range.from + 1,
303
+ }));
304
+ }
305
+ /**
306
+ * `--against`, which answers "did coverage drop" without a spreadsheet.
307
+ *
308
+ * The headline is the delta, because a coverage check in CI is a comparison
309
+ * rather than a threshold most of the time.
310
+ */
311
+ async function reportDiff(path, baseline, max) {
312
+ // xccov's argument order is before, after; the bundle being asked about is
313
+ // the after, so the baseline goes first.
314
+ const diff = await readCoverageDiff(baseline, path);
315
+ const overall = diff.lineCoverageDelta ?? {};
316
+ const blocks = [
317
+ renderFields({
318
+ coverage: movement(overall.lineCoverageDelta ?? 0),
319
+ covered_lines: overall.coveredLinesDelta ?? 0,
320
+ executable_lines: overall.executableLinesDelta ?? 0,
321
+ }),
322
+ ];
323
+ // Sorted on the number and rendered afterwards: "down 45.5%" does not
324
+ // compare as a number, and the biggest drop is the row being looked for.
325
+ const moved = (diff.targetDeltas ?? [])
326
+ .flatMap((target) => (target.fileDeltas ?? []).map((file) => ({
327
+ target: target.name ?? "",
328
+ file: relativize(file.documentLocation ?? ""),
329
+ delta: file.lineCoverageDelta?.lineCoverageDelta ?? 0,
330
+ lines: file.lineCoverageDelta?.coveredLinesDelta ?? 0,
331
+ })))
332
+ .sort((a, b) => a.delta - b.delta);
333
+ if (moved.length > 0) {
334
+ const shown = moved.slice(0, max).map(({ target, file, delta, lines }) => ({
335
+ target,
336
+ file,
337
+ coverage: movement(delta),
338
+ lines,
339
+ }));
340
+ blocks.push(renderList(moved.length > shown.length
341
+ ? `files (${shown.length} of ${moved.length})`
342
+ : "files", shown));
343
+ }
344
+ const added = namesOf(diff, "addedFiles");
345
+ const removed = namesOf(diff, "removedFiles");
346
+ if (added.length > 0)
347
+ blocks.push(renderFields({ added_files: added }));
348
+ if (removed.length > 0)
349
+ blocks.push(renderFields({ removed_files: removed }));
350
+ if (blocks.length === 1 && moved.length === 0) {
351
+ blocks.push(renderFields({ difference: "none — coverage matches the baseline" }));
352
+ }
353
+ return renderOutput([
354
+ ...blocks,
355
+ renderFields({ baseline: tildePath(baseline), ...sourceField(path) }),
356
+ ]);
357
+ }
358
+ function namesOf(diff, key) {
359
+ return (diff.targetDeltas ?? []).flatMap((target) => (target[key] ?? []).map((file) => relativize(file.documentLocation ?? "")));
360
+ }
361
+ /**
362
+ * Which way coverage moved, in words.
363
+ *
364
+ * Words rather than a signed percentage because TOON quotes any scalar that
365
+ * starts with `-`, and `"-2.1%"` costs more in quotes than `down 2.1%` costs
366
+ * in letters.
367
+ */
368
+ export function movement(delta) {
369
+ if (delta === 0)
370
+ return "unchanged";
371
+ return `${delta > 0 ? "up" : "down"} ${percent(Math.abs(delta))}`;
372
+ }
373
+ /**
374
+ * `--merge`, which combines the coverage of a sharded run.
375
+ *
376
+ * `xccov merge` wants report/archive pairs rather than result bundles, so the
377
+ * bundles are unpacked first — the two-step that makes this something people
378
+ * give up on doing in CI.
379
+ */
380
+ async function runMerge(first, args, max) {
381
+ const paths = positionals(args, VALUE_FLAGS).map((path) => resolve(expandTilde(path)));
382
+ if (paths.length < 2) {
383
+ throw new AxiError(`--merge combines the coverage of two or more runs, and ${paths.length} was given`, "VALIDATION_ERROR", [
384
+ "xcodebuild-axi coverage shard1.xcresult shard2.xcresult --merge",
385
+ "`xcodebuild-axi test --coverage` prints the bundle path it wrote",
386
+ ]);
387
+ }
388
+ const requested = getFlag(args, "--to");
389
+ const outReport = requested
390
+ ? resolve(expandTilde(requested))
391
+ : mergedCoveragePath(paths);
392
+ const outArchive = outReport.replace(/\.xccovreport$/, "") + ".xccovarchive";
393
+ if (requested && (existsSync(outReport) || existsSync(outArchive))) {
394
+ throw new AxiError(`Something is already at ${tildePath(outReport)}`, "VALIDATION_ERROR", [
395
+ "Pass a path that does not exist yet, or drop --to to write under ~/Library/Caches",
396
+ ]);
397
+ }
398
+ if (!requested) {
399
+ rmSync(outReport, { recursive: true, force: true });
400
+ rmSync(outArchive, { recursive: true, force: true });
401
+ }
402
+ mkdirSync(dirname(outReport), { recursive: true });
403
+ await mergeCoverage(paths, outReport, outArchive);
404
+ const merged = await readCoverage(outReport);
405
+ return renderOutput([
406
+ renderFields({
407
+ merged: paths.length,
408
+ coverage: percent(merged.lineCoverage),
409
+ lines: `${merged.coveredLines} of ${merged.executableLines} covered`,
410
+ report: tildePath(outReport),
411
+ archive: tildePath(outArchive),
412
+ }),
413
+ renderList("from", paths.slice(0, max).map((path) => ({ bundle: tildePath(path) }))),
414
+ renderHelp([
415
+ `Run \`xcodebuild-axi coverage ${tildePath(outReport)} --files\` to report on the merged coverage`,
416
+ ]),
417
+ ]);
418
+ }
112
419
  function expandTilde(path) {
113
420
  const home = process.env["HOME"];
114
421
  return home && path.startsWith("~/") ? `${home}${path.slice(1)}` : path;
@@ -1 +1 @@
1
- {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../../src/commands/coverage.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"coverage.js","sourceRoot":"","sources":["../../../src/commands/coverage.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,15 @@
1
- import { type BuildResults, type TestNode } from "../xcresult.js";
2
- export declare const RESULT_HELP = "usage: xcodebuild-axi result <path.xcresult> [flags]\nRe-reads a result bundle that a previous run wrote, without rebuilding.\nflags[12]:\n --failures failures and errors only\n --warnings include the full warning list\n --tests every test the run recorded, as the tree Xcode groups them into\n --insights Xcode's own diagnosis: what failed together, and what was slow\n --activities what one test did, step by step; needs --test\n --metrics what the performance tests measured; --test narrows it\n --log <type> the stored build, action, or console log, as timed sections\n --available what this bundle holds at all: coverage, logs, test results\n --metadata when the bundle was written, and in what format\n --test <id> the test --activities or --metrics is about\n --max <n> rows to list before summarizing the rest (default: 20)\n --full untruncated messages\nnote:\n Everything here reads the bundle a run already wrote, so none of it rebuilds\n anything. --available is the cheapest first question about a bundle from\n somewhere else: it says whether there is coverage or a log to ask for,\n instead of letting you find out by failing to read one.\nexamples:\n xcodebuild-axi result ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-iPhone-17-Pro-test.xcresult\n xcodebuild-axi result build/MyApp.xcresult --failures --full\n xcodebuild-axi result build/MyApp.xcresult --log build --max 10\n xcodebuild-axi result build/MyApp.xcresult --activities --test MyAppTests/CheckoutTests/testTotal\n";
3
- export declare const RESULT_FLAGS: readonly ["--failures", "--warnings", "--tests", "--insights", "--activities", "--metrics", "--log", "--available", "--metadata", "--test", "--max", "--full"];
1
+ import { type BuildResults, type CountDelta, type ExportKind, type TestFailureDelta, type TestNode } from "../xcresult.js";
2
+ export declare const RESULT_HELP = "usage: xcodebuild-axi result <path.xcresult> [flags]\nRe-reads a result bundle that a previous run wrote, without rebuilding.\nflags[17]:\n --failures failures and errors only\n --warnings include the full warning list\n --tests every test the run recorded, as the tree Xcode groups them into\n --insights Xcode's own diagnosis: what failed together, and what was slow\n --activities what one test did, step by step; needs --test\n --metrics what the performance tests measured; --test narrows it\n --log <type> the stored build, action, or console log, as timed sections\n --available what this bundle holds at all: coverage, logs, test results\n --metadata when the bundle was written, and in what format\n --export <what> write part of the bundle out: attachments, diagnostics,\n metrics, or evaluations\n --against <path> compare this run to a baseline bundle: what broke, what\n got fixed, which tests came and went\n --merge combine two or more bundles into one, for a sharded run\n --to <path> where --export or --merge writes (default: under\n ~/Library/Caches)\n --test <id> the test --activities, --metrics or --export is about\n --filter <glob> with --export attachments: filenames to keep, e.g. '*.png'\n --max <n> rows to list before summarizing the rest (default: 20)\n --full untruncated messages\nnote:\n Everything here reads the bundle a run already wrote, so none of it rebuilds\n anything. --available is the cheapest first question about a bundle from\n somewhere else: it says whether there is coverage or a log to ask for,\n instead of letting you find out by failing to read one.\n\n --export writes files rather than printing them, so it reports what landed\n and where. --failures narrows attachments and evaluations to what a failing\n test produced, which is usually all anyone wants out of a green run's\n hundreds of screenshots.\n\n --against answers 'is this worse than before' in one call: a failure the\n baseline did not have is what a CI check is looking for, and it is reported\n ahead of everything else.\nexamples:\n xcodebuild-axi result ~/Library/Caches/xcodebuild-axi/MyApps-1a2b3c4d/MyApp-iPhone-17-Pro-test.xcresult\n xcodebuild-axi result build/MyApp.xcresult --failures --full\n xcodebuild-axi result build/MyApp.xcresult --log build --max 10\n xcodebuild-axi result build/MyApp.xcresult --activities --test MyAppTests/CheckoutTests/testTotal\n xcodebuild-axi result build/MyApp.xcresult --export attachments --failures\n xcodebuild-axi result build/MyApp.xcresult --against build/baseline.xcresult\n xcodebuild-axi result shard1.xcresult shard2.xcresult --merge\n";
3
+ export declare const RESULT_FLAGS: readonly ["--failures", "--warnings", "--tests", "--insights", "--activities", "--metrics", "--log", "--available", "--metadata", "--export", "--against", "--merge", "--to", "--test", "--filter", "--max", "--full"];
4
4
  export declare function resultCommand(args: string[]): Promise<string>;
5
+ /** "0 → 2 (+2 -0)", which says both the level and the direction. */
6
+ export declare function deltaField(delta: CountDelta | undefined): string;
7
+ export declare function failureDeltaRows(deltas: TestFailureDelta[]): Array<Record<string, unknown>>;
8
+ /**
9
+ * The manifest `xcresulttool` writes beside the files it exported, which is
10
+ * the only thing that ties an exported filename back to the test that made it.
11
+ */
12
+ export declare function manifestRows(outputPath: string, kind: ExportKind): Array<Record<string, unknown>>;
5
13
  /**
6
14
  * The test tree, flattened to one row per test case.
7
15
  *