Skip to content

Commit 5bd970d

Browse files
authored
Merge pull request #317 from recca0120/fix/id
Fix/id
2 parents 9fc5b14 + 74eb874 commit 5bd970d

File tree

10 files changed

+137
-79
lines changed

10 files changed

+137
-79
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"displayName": "PHPUnit Test Explorer",
55
"icon": "img/icon.png",
66
"publisher": "recca0120",
7-
"version": "3.7.5",
7+
"version": "3.7.6",
88
"private": true,
99
"license": "MIT",
1010
"repository": {

src/PHPUnit/ProblemMatcher/PHPUnitProblemMatcher.test.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -248,9 +248,9 @@ describe('PHPUnit ProblemMatcher Text', () => {
248248
expect.objectContaining({
249249
event: TeamcityEvent.testFailed,
250250
name: 'testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
251-
// locationHint: 'php_qn:///srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::\\App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
251+
locationHint: 'php_qn:///srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::\\App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
252252
flowId: 5161,
253-
id: '/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
253+
id: 'Price Synchronizer (App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizer)::Product need update returns false when price sync not enabled',
254254
file: '/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php',
255255
message: 'Error: Class "App\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizer" not found',
256256
details: [{
@@ -279,13 +279,9 @@ describe('PHPUnit ProblemMatcher Text', () => {
279279
name: 'test_permission',
280280
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::\\Tests\\Feature\\ChatControllerTest::test_permission',
281281
flowId: 22946,
282-
id: '/var/www/html/tests/Feature/ChatControllerTest.php::test_permission',
282+
id: 'Chat Controller (Tests\\Feature\\ChatController)::Permission',
283283
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
284284
message: 'ChatControllerTest uses PlayerService',
285-
details: [{
286-
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
287-
line: 1,
288-
}],
289285
duration: 0,
290286
}),
291287
);
@@ -296,13 +292,9 @@ describe('PHPUnit ProblemMatcher Text', () => {
296292
name: 'test_grant_chat_token',
297293
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::\\Tests\\Feature\\ChatControllerTest::test_grant_chat_token',
298294
flowId: 22946,
299-
id: '/var/www/html/tests/Feature/ChatControllerTest.php::test_grant_chat_token',
295+
id: 'Chat Controller (Tests\\Feature\\ChatController)::Grant chat token',
300296
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
301297
message: 'ChatControllerTest uses PlayerService',
302-
details: [{
303-
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
304-
line: 1,
305-
}],
306298
duration: 0,
307299
}),
308300
);

src/PHPUnit/ProblemMatcher/PestProblemMatcher.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,4 +754,18 @@ describe('Pest ProblemMatcher Text', () => {
754754
it('testFinished without TestStarted', () => {
755755
resultShouldBe('##teamcity[testFinished name=\'`before each` → example\' duration=\'12\' flowId=\'97972\']', undefined);
756756
});
757+
758+
it('PHPUnit without TestStarted', () => {
759+
resultShouldBe(`##teamcity[testSuiteStarted name='Tests\\Feature\\AuthenticationTest' locationHint='pest_qn://Authentication (Tests\\Feature\\Authentication)' flowId='6611']`, {});
760+
resultShouldBe(`##teamcity[testCount count='1' flowId='6611']`, {});
761+
resultShouldBe(`##teamcity[testIgnored name='Login screen can be rendered' message='This test was ignored.' details='' flowId='6611']`, {
762+
event: TeamcityEvent.testIgnored,
763+
flowId: 6611,
764+
id: 'Authentication (Tests\\Feature\\Authentication)::Login screen can be rendered',
765+
name: 'Login screen can be rendered',
766+
message: 'This test was ignored.',
767+
duration: 0,
768+
});
769+
resultShouldBe(`##teamcity[testSuiteFinished name='Tests\\Feature\\AuthenticationTest' flowId='6611']`, {});
770+
});
757771
});

src/PHPUnit/ProblemMatcher/ProblemMatcher.ts

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import {
22
TeamcityEvent, TestFailed, TestFinished, TestIgnored, TestResult, TestResultParser, TestStarted, TestSuiteFinished,
33
TestSuiteStarted,
44
} from '.';
5-
import { PestV1Fixer, PHPUnitFixer } from '../Transformer';
5+
import { PestFixer, PestV1Fixer, PHPUnitFixer } from '../Transformer';
66

77
export class ProblemMatcher {
8-
private results = new Map<string, TestResult>();
8+
private cache = new Map<string, TestResult>();
99

1010
private lookup: { [p: string]: (result: any) => TestResult | undefined } = {
1111
[TeamcityEvent.testSuiteStarted]: this.handleStarted,
@@ -19,8 +19,8 @@ export class ProblemMatcher {
1919
constructor(private testResultParser: TestResultParser = new TestResultParser()) { }
2020

2121
parse(input: string | Buffer): TestResult | undefined {
22-
const result = this.testResultParser.parse(input.toString());
23-
PestV1Fixer.fixFlowId(this.results, result);
22+
let result = this.testResultParser.parse(input.toString());
23+
result = PestV1Fixer.fixFlowId(this.cache, result);
2424

2525
return this.isResult(result) ? this.lookup[result!.event]?.call(this, result) : result;
2626
}
@@ -30,50 +30,50 @@ export class ProblemMatcher {
3030
}
3131

3232
private handleStarted(testResult: TestSuiteStarted | TestStarted) {
33-
const id = this.generateId(testResult);
34-
this.results.set(id, testResult);
33+
const cacheId = this.cacheId(testResult);
34+
this.cache.set(cacheId, testResult);
3535

36-
return this.results.get(id);
36+
return this.cache.get(cacheId);
3737
}
3838

3939
private handleFault(testResult: TestFailed | TestIgnored): TestResult | undefined {
40-
const id = this.generateId(testResult);
41-
let prevData = this.results.get(id) as (TestFailed | TestIgnored);
40+
const cacheId = this.cacheId(testResult);
41+
let prevTestResult = this.cache.get(cacheId) as (TestFailed | TestIgnored);
4242

43-
if (!prevData) {
44-
PHPUnitFixer.fixDetails(this.results, testResult);
45-
const file = testResult.details[0].file;
43+
if (!prevTestResult) {
44+
PHPUnitFixer.fixNoTestStarted(this.cache, testResult);
45+
PestFixer.fixNoTestStarted(this.cache, testResult);
4646

47-
return { ...testResult, id: [file, testResult.name].join('::'), file, duration: 0 };
47+
return { ...testResult, duration: 0 };
4848
}
4949

50-
if (prevData.event === TeamcityEvent.testStarted) {
51-
this.results.set(id, { ...(prevData ?? {}), ...testResult });
50+
if (prevTestResult.event === TeamcityEvent.testStarted) {
51+
this.cache.set(cacheId, { ...(prevTestResult ?? {}), ...testResult });
5252

5353
return;
5454
}
5555

5656
if (testResult.message) {
57-
prevData.message += '\n\n' + testResult.message;
57+
prevTestResult.message += '\n\n' + testResult.message;
5858
}
59-
prevData.details.push(...testResult.details);
59+
prevTestResult.details.push(...testResult.details);
6060

61-
this.results.set(id, prevData);
61+
this.cache.set(cacheId, prevTestResult);
6262

6363
return undefined;
6464
}
6565

6666
private handleFinished(testResult: TestSuiteFinished | TestFinished) {
67-
const id = this.generateId(testResult);
67+
const cacheId = this.cacheId(testResult);
6868

69-
if (!this.results.has(id)) {
69+
if (!this.cache.has(cacheId)) {
7070
return;
7171
}
7272

73-
const prevData = this.results.get(id)!;
74-
const event = this.isFault(prevData) ? prevData.event : testResult.event;
75-
const result = { ...prevData, ...testResult, event };
76-
this.results.delete(id);
73+
const prevTestResult = this.cache.get(cacheId)!;
74+
const event = this.isFault(prevTestResult) ? prevTestResult.event : testResult.event;
75+
const result = { ...prevTestResult, ...testResult, event };
76+
this.cache.delete(cacheId);
7777

7878
return result;
7979
}
@@ -82,7 +82,7 @@ export class ProblemMatcher {
8282
return [TeamcityEvent.testFailed, TeamcityEvent.testIgnored].includes(testResult.event);
8383
}
8484

85-
private generateId(testResult: TestSuiteStarted | TestStarted | TestFailed | TestIgnored | TestSuiteFinished | TestFinished) {
85+
private cacheId(testResult: TestSuiteStarted | TestStarted | TestFailed | TestIgnored | TestSuiteFinished | TestFinished) {
8686
return `${testResult.name}-${testResult.flowId}`;
8787
}
8888
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { TestFailed, TestIgnored, TestResult } from '../ProblemMatcher';
2+
import { TransformerFactory } from './TransformerFactory';
3+
import { getPrevTestResult } from './utils';
4+
5+
export class PHPUnitFixer {
6+
static fixNoTestStarted(cache: Map<string, TestResult>, testResult: TestFailed | TestIgnored) {
7+
if (testResult.id) {
8+
return testResult;
9+
}
10+
11+
const prevTestResult = getPrevTestResult(new RegExp('^(php_qn):\/\/'), cache, testResult);
12+
if (!prevTestResult) {
13+
return testResult;
14+
}
15+
16+
if (!testResult.locationHint) {
17+
const parts = prevTestResult.locationHint?.split('::') ?? [];
18+
const locationHint = parts.slice(0, Math.max(2, parts.length - 1)).join('::');
19+
testResult.locationHint = [locationHint, testResult.name]
20+
.filter(value => !!value)
21+
.join('::');
22+
}
23+
24+
const transformer = TransformerFactory.factory(testResult.locationHint);
25+
const { id, file } = transformer.fromLocationHit(testResult.locationHint, testResult.name);
26+
testResult.id = id;
27+
testResult.file = file;
28+
29+
return testResult;
30+
}
31+
}
Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,45 @@
1-
import { TeamcityEvent, TestResult, TestStarted, TestSuiteStarted } from '../ProblemMatcher';
1+
import { TeamcityEvent, TestFailed, TestIgnored, TestResult } from '../ProblemMatcher';
22
import { capitalize } from '../utils';
3+
import { getPrevTestResult } from './utils';
34

4-
export class PHPUnitFixer {
5-
static fixDetails(results = new Map<string, TestResult>(), testResult: TestResult & {
6-
name: string,
7-
locationHint?: string,
8-
file?: string,
9-
details?: Array<{ file: string, line: number }>,
10-
}) {
11-
if (testResult.details && testResult.file) {
12-
return testResult;
13-
}
5+
class Str {
6+
static prefix = '__pest_evaluable_';
147

15-
const result = Array.from(results.values()).reverse().find((result) => {
16-
return [TeamcityEvent.testSuiteStarted, TeamcityEvent.testStarted].includes(result.event);
17-
}) as (TestSuiteStarted | TestStarted | undefined);
8+
static evaluable(code: string) {
9+
return this.prefix + code.replace(/_/g, '__').replace(/\s/g, '_').replace(/[^a-zA-Z0-9_\u0080-\uFFFF]/g, '_');
10+
}
11+
}
1812

19-
if (!result) {
13+
export class PestFixer {
14+
static fixNoTestStarted(cache: Map<string, TestResult>, testResult: TestFailed | TestIgnored) {
15+
if (testResult.id) {
2016
return testResult;
2117
}
2218

23-
const file = result.file!;
24-
if (!testResult.file) {
25-
testResult.file = file;
19+
if (!testResult.duration) {
20+
testResult.duration = 0;
2621
}
2722

28-
if (!testResult.details) {
29-
testResult.details = [{ file: file, line: 1 }];
30-
}
23+
if ('details' in testResult && testResult.details.length > 0) {
24+
const file = testResult.details[0].file;
25+
testResult.id = [file, testResult.name].join('::');
26+
testResult.file = file;
3127

32-
if (!testResult.locationHint) {
33-
const locationHint = result.locationHint?.split('::').slice(0, 2).join('::');
34-
testResult.locationHint = [locationHint, testResult.name]
35-
.filter(value => !!value)
36-
.join('::');
28+
return testResult;
3729
}
3830

39-
return testResult;
40-
}
41-
}
31+
const pattern = new RegExp('^(pest_qn|file):\/\/');
32+
const prevTestResult = getPrevTestResult(pattern, cache, testResult);
33+
if (prevTestResult) {
34+
testResult.id = [
35+
prevTestResult.locationHint?.replace(pattern, ''),
36+
testResult.name,
37+
].filter(v => !!v).join('::');
4238

43-
class Str {
44-
static prefix = '__pest_evaluable_';
39+
return testResult;
40+
}
4541

46-
static evaluable(code: string) {
47-
return this.prefix + code.replace(/_/g, '__').replace(/\s/g, '_').replace(/[^a-zA-Z0-9_\u0080-\uFFFF]/g, '_');
42+
return testResult;
4843
}
4944
}
5045

@@ -53,7 +48,7 @@ export class PestV1Fixer {
5348
return this.fixDataSet(/^tests\//.test(locationHint) ? locationHint : locationHint.substring(locationHint.lastIndexOf('tests/')));
5449
}
5550

56-
static fixFlowId(results = new Map<string, TestResult>(), testResult?: TestResult) {
51+
static fixFlowId(cache: Map<string, TestResult>, testResult?: TestResult) {
5752
if (!testResult) {
5853
return testResult;
5954
}
@@ -63,7 +58,7 @@ export class PestV1Fixer {
6358
return testResult;
6459
}
6560

66-
const result = Array.from(results.values()).reverse().find((result: TestResult) => {
61+
const result = Array.from(cache.values()).reverse().find((result: TestResult) => {
6762
if (testResult.event !== TeamcityEvent.testStarted) {
6863
return result.event === TeamcityEvent.testStarted && (result as any).name === (testResult as any).name;
6964
}
@@ -118,4 +113,4 @@ export class PestV2Fixer {
118113

119114
return Str.evaluable(methodName) + dataset;
120115
}
121-
}
116+
}

src/PHPUnit/Transformer/PestTransFormer.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { TestType } from '../types';
2-
import { PestV2Fixer } from './Fixer';
2+
import { PestV2Fixer } from './PestFixer';
33
import { PestTransformer } from './PestTransformer';
44

55
describe('PestTransformer', () => {

src/PHPUnit/Transformer/PestTransformer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TestDefinition, TestType } from '../types';
22
import { uncapitalize } from '../utils';
3-
import { PestV1Fixer, PestV2Fixer } from './Fixer';
3+
import { PestV1Fixer, PestV2Fixer } from './PestFixer';
44
import { PHPUnitTransformer } from './PHPUnitTransformer';
55
import { TransformerFactory } from './TransformerFactory';
66

src/PHPUnit/Transformer/index.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
export * from './Fixer';
2-
export * from './TransformerFactory';
31
export * from './Transformer';
2+
export * from './TransformerFactory';
3+
export * from './PHPUnitFixer';
44
export * from './PHPUnitTransformer';
5-
export * from './PestTransformer';
5+
export * from './PestFixer';
6+
export * from './PestTransformer';
7+
export { getPrevTestResult } from './utils';

src/PHPUnit/Transformer/utils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { TeamcityEvent, TestFailed, TestIgnored, TestResult, TestStarted, TestSuiteStarted } from '../ProblemMatcher';
2+
3+
export const getPrevTestResult = (pattern: RegExp, cache: Map<string, TestResult>, testResult: TestFailed | TestIgnored) => {
4+
for (const prevTestResult of Array.from(cache.values()).reverse()) {
5+
if (isTestStarted(pattern, prevTestResult)) {
6+
return prevTestResult as TestStarted | TestSuiteStarted;
7+
}
8+
9+
if (prevTestResult.event === TeamcityEvent.testCount) {
10+
continue;
11+
}
12+
13+
if (prevTestResult.event !== testResult.event) {
14+
break;
15+
}
16+
}
17+
18+
return undefined;
19+
};
20+
21+
const isTestStarted = (pattern: RegExp, testResult: TestResult & { locationHint?: string }) => {
22+
return [TeamcityEvent.testStarted, TeamcityEvent.testSuiteStarted].includes(testResult.event)
23+
&& pattern.test(testResult.locationHint ?? '');
24+
};

0 commit comments

Comments
 (0)