Skip to content

Commit e3e04ac

Browse files
committed
fix: include test name in dedup key and capture parameterized case count
- Test failure dedup key now includes the test name when a location is present, preventing distinct failures from different tests sharing the same assertion line from being collapsed. Suite name is excluded from the location-present key because it disagrees between xcresult and live parsing sources. - ParsedTestCase gains a caseCount field populated from the 'with N test cases' suffix in Swift Testing parameterized results. Event parsers increment progress by caseCount instead of 1.
1 parent f7a685c commit e3e04ac

File tree

5 files changed

+21
-21
lines changed

5 files changed

+21
-21
lines changed

src/utils/__tests__/swift-testing-line-parsers.test.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,17 +83,6 @@ describe('Swift Testing line parsers', () => {
8383
});
8484
});
8585

86-
it('should parse a failed parameterized test', () => {
87-
const result = parseSwiftTestingResultLine(
88-
'✘ Test "Parameterized failure" with 3 test cases failed after 0.001 seconds with 1 issue.',
89-
);
90-
expect(result).toEqual({
91-
status: 'failed',
92-
rawName: 'Parameterized failure',
93-
testName: 'Parameterized failure',
94-
durationText: '0.001s',
95-
});
96-
});
9786

9887
it('should parse a skipped test (arrow format)', () => {
9988
const result = parseSwiftTestingResultLine('➜ Test disabledTest() skipped: "Not ready yet"');

src/utils/swift-testing-event-parser.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,9 @@ export function createSwiftTestingEventParser(
100100
durationMs,
101101
});
102102
lastIssueDiagnostic = null;
103-
completedCount += 1;
104-
failedCount += 1;
103+
const increment = stResult.caseCount ?? 1;
104+
completedCount += increment;
105+
failedCount += increment;
105106
emitTestProgress();
106107
return;
107108
}
@@ -122,12 +123,13 @@ export function createSwiftTestingEventParser(
122123

123124
// Swift Testing result line: ✔/✘/◇ Test "Name" passed/failed/skipped (non-failure or no pending issue)
124125
if (stResult) {
125-
completedCount += 1;
126+
const increment = stResult.caseCount ?? 1;
127+
completedCount += increment;
126128
if (stResult.status === 'failed') {
127-
failedCount += 1;
129+
failedCount += increment;
128130
}
129131
if (stResult.status === 'skipped') {
130-
skippedCount += 1;
132+
skippedCount += increment;
131133
}
132134
emitTestProgress();
133135
return;

src/utils/swift-testing-line-parsers.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
// Optional verbose suffix: (aka 'funcName()')
99
// Optional parameterized suffix: with N test cases
1010
const OPTIONAL_AKA = `(?:\\s*\\(aka '[^']*'\\))?`;
11-
const OPTIONAL_PARAMETERIZED = `(?:\\s+with \\d+ test cases?)?`;
11+
const OPTIONAL_PARAMETERIZED = `(?:\\s+with (\\d+) test cases?)?`;
1212

1313
/**
1414
* Parse a Swift Testing result line (passed/failed/skipped).
@@ -29,14 +29,16 @@ export function parseSwiftTestingResultLine(line: string): ParsedTestCase | null
2929
);
3030
const passedMatch = line.match(passedRegex);
3131
if (passedMatch) {
32-
const [, name, duration] = passedMatch;
32+
const [, name, caseCountStr, duration] = passedMatch;
3333
const { suiteName, testName } = parseRawTestName(name);
34+
const caseCount = caseCountStr ? Number(caseCountStr) : undefined;
3435
return {
3536
status: 'passed',
3637
rawName: name,
3738
suiteName,
3839
testName,
3940
durationText: `${duration}s`,
41+
...(caseCount !== undefined && { caseCount }),
4042
};
4143
}
4244

@@ -46,14 +48,16 @@ export function parseSwiftTestingResultLine(line: string): ParsedTestCase | null
4648
);
4749
const failedMatch = line.match(failedRegex);
4850
if (failedMatch) {
49-
const [, name, duration] = failedMatch;
51+
const [, name, caseCountStr, duration] = failedMatch;
5052
const { suiteName, testName } = parseRawTestName(name);
53+
const caseCount = caseCountStr ? Number(caseCountStr) : undefined;
5154
return {
5255
status: 'failed',
5356
rawName: name,
5457
suiteName,
5558
testName,
5659
durationText: `${duration}s`,
60+
...(caseCount !== undefined && { caseCount }),
5761
};
5862
}
5963

src/utils/xcodebuild-line-parsers.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface ParsedTestCase {
2626
suiteName?: string;
2727
testName: string;
2828
durationText?: string;
29+
caseCount?: number;
2930
}
3031

3132
export interface ParsedTotals {

src/utils/xcodebuild-run-state.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,16 @@ function normalizeTestFailureLocation(location: string | undefined): string | nu
5050
function normalizeTestFailureKey(event: TestFailureEvent): string {
5151
const normalizedLocation = normalizeTestFailureLocation(event.location);
5252
const normalizedMessage = event.message.trim().toLowerCase();
53+
const suite = normalizeTestIdentifier(event.suite);
54+
const test = normalizeTestIdentifier(event.test);
5355

5456
if (normalizedLocation) {
55-
return `${normalizedLocation}|${normalizedMessage}`;
57+
// Include test name but NOT suite -- suite naming disagrees between xcresult
58+
// and live parsing (e.g. 'Module.Suite' vs absent). Test name is consistent.
59+
return `${test}|${normalizedLocation}|${normalizedMessage}`;
5660
}
5761

58-
return `${normalizeTestIdentifier(event.suite)}|${normalizeTestIdentifier(event.test)}|${normalizedMessage}`;
62+
return `${suite}|${test}|${normalizedMessage}`;
5963
}
6064

6165
export interface FinalizeOptions {

0 commit comments

Comments
 (0)