Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"displayName": "PHPUnit Test Explorer",
"icon": "img/icon.png",
"publisher": "recca0120",
"version": "3.7.5",
"version": "3.7.6",
"private": true,
"license": "MIT",
"repository": {
Expand Down
16 changes: 4 additions & 12 deletions src/PHPUnit/ProblemMatcher/PHPUnitProblemMatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,9 +248,9 @@ describe('PHPUnit ProblemMatcher Text', () => {
expect.objectContaining({
event: TeamcityEvent.testFailed,
name: 'testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
// locationHint: 'php_qn:///srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::\\App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
locationHint: 'php_qn:///srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::\\App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizerTest::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
flowId: 5161,
id: '/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php::testProductNeedUpdateReturnsFalseWhenPriceSyncNotEnabled',
id: 'Price Synchronizer (App\\Tests\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizer)::Product need update returns false when price sync not enabled',
file: '/srv/app/tests/Ecommerce/Offer/Synchronizer/PriceSynchronizerTest.php',
message: 'Error: Class "App\\Ecommerce\\Offer\\Synchronizer\\PriceSynchronizer" not found',
details: [{
Expand Down Expand Up @@ -279,13 +279,9 @@ describe('PHPUnit ProblemMatcher Text', () => {
name: 'test_permission',
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::\\Tests\\Feature\\ChatControllerTest::test_permission',
flowId: 22946,
id: '/var/www/html/tests/Feature/ChatControllerTest.php::test_permission',
id: 'Chat Controller (Tests\\Feature\\ChatController)::Permission',
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
message: 'ChatControllerTest uses PlayerService',
details: [{
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
line: 1,
}],
duration: 0,
}),
);
Expand All @@ -296,13 +292,9 @@ describe('PHPUnit ProblemMatcher Text', () => {
name: 'test_grant_chat_token',
locationHint: 'php_qn:///var/www/html/tests/Feature/ChatControllerTest.php::\\Tests\\Feature\\ChatControllerTest::test_grant_chat_token',
flowId: 22946,
id: '/var/www/html/tests/Feature/ChatControllerTest.php::test_grant_chat_token',
id: 'Chat Controller (Tests\\Feature\\ChatController)::Grant chat token',
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
message: 'ChatControllerTest uses PlayerService',
details: [{
file: '/var/www/html/tests/Feature/ChatControllerTest.php',
line: 1,
}],
duration: 0,
}),
);
Expand Down
14 changes: 14 additions & 0 deletions src/PHPUnit/ProblemMatcher/PestProblemMatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -754,4 +754,18 @@ describe('Pest ProblemMatcher Text', () => {
it('testFinished without TestStarted', () => {
resultShouldBe('##teamcity[testFinished name=\'`before each` → example\' duration=\'12\' flowId=\'97972\']', undefined);
});

it('PHPUnit without TestStarted', () => {
resultShouldBe(`##teamcity[testSuiteStarted name='Tests\\Feature\\AuthenticationTest' locationHint='pest_qn://Authentication (Tests\\Feature\\Authentication)' flowId='6611']`, {});
resultShouldBe(`##teamcity[testCount count='1' flowId='6611']`, {});
resultShouldBe(`##teamcity[testIgnored name='Login screen can be rendered' message='This test was ignored.' details='' flowId='6611']`, {
event: TeamcityEvent.testIgnored,
flowId: 6611,
id: 'Authentication (Tests\\Feature\\Authentication)::Login screen can be rendered',
name: 'Login screen can be rendered',
message: 'This test was ignored.',
duration: 0,
});
resultShouldBe(`##teamcity[testSuiteFinished name='Tests\\Feature\\AuthenticationTest' flowId='6611']`, {});
});
});
50 changes: 25 additions & 25 deletions src/PHPUnit/ProblemMatcher/ProblemMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
TeamcityEvent, TestFailed, TestFinished, TestIgnored, TestResult, TestResultParser, TestStarted, TestSuiteFinished,
TestSuiteStarted,
} from '.';
import { PestV1Fixer, PHPUnitFixer } from '../Transformer';
import { PestFixer, PestV1Fixer, PHPUnitFixer } from '../Transformer';

export class ProblemMatcher {
private results = new Map<string, TestResult>();
private cache = new Map<string, TestResult>();

private lookup: { [p: string]: (result: any) => TestResult | undefined } = {
[TeamcityEvent.testSuiteStarted]: this.handleStarted,
Expand All @@ -19,8 +19,8 @@
constructor(private testResultParser: TestResultParser = new TestResultParser()) { }

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

return this.isResult(result) ? this.lookup[result!.event]?.call(this, result) : result;
}
Expand All @@ -30,50 +30,50 @@
}

private handleStarted(testResult: TestSuiteStarted | TestStarted) {
const id = this.generateId(testResult);
this.results.set(id, testResult);
const cacheId = this.cacheId(testResult);
this.cache.set(cacheId, testResult);

return this.results.get(id);
return this.cache.get(cacheId);
}

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

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

return { ...testResult, id: [file, testResult.name].join('::'), file, duration: 0 };
return { ...testResult, duration: 0 };
}

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

return;
}

if (testResult.message) {
prevData.message += '\n\n' + testResult.message;
prevTestResult.message += '\n\n' + testResult.message;

Check warning on line 57 in src/PHPUnit/ProblemMatcher/ProblemMatcher.ts

View check run for this annotation

Codecov / codecov/patch

src/PHPUnit/ProblemMatcher/ProblemMatcher.ts#L57

Added line #L57 was not covered by tests
}
prevData.details.push(...testResult.details);
prevTestResult.details.push(...testResult.details);

Check warning on line 59 in src/PHPUnit/ProblemMatcher/ProblemMatcher.ts

View check run for this annotation

Codecov / codecov/patch

src/PHPUnit/ProblemMatcher/ProblemMatcher.ts#L59

Added line #L59 was not covered by tests

this.results.set(id, prevData);
this.cache.set(cacheId, prevTestResult);

Check warning on line 61 in src/PHPUnit/ProblemMatcher/ProblemMatcher.ts

View check run for this annotation

Codecov / codecov/patch

src/PHPUnit/ProblemMatcher/ProblemMatcher.ts#L61

Added line #L61 was not covered by tests

return undefined;
}

private handleFinished(testResult: TestSuiteFinished | TestFinished) {
const id = this.generateId(testResult);
const cacheId = this.cacheId(testResult);

if (!this.results.has(id)) {
if (!this.cache.has(cacheId)) {
return;
}

const prevData = this.results.get(id)!;
const event = this.isFault(prevData) ? prevData.event : testResult.event;
const result = { ...prevData, ...testResult, event };
this.results.delete(id);
const prevTestResult = this.cache.get(cacheId)!;
const event = this.isFault(prevTestResult) ? prevTestResult.event : testResult.event;
const result = { ...prevTestResult, ...testResult, event };
this.cache.delete(cacheId);

return result;
}
Expand All @@ -82,7 +82,7 @@
return [TeamcityEvent.testFailed, TeamcityEvent.testIgnored].includes(testResult.event);
}

private generateId(testResult: TestSuiteStarted | TestStarted | TestFailed | TestIgnored | TestSuiteFinished | TestFinished) {
private cacheId(testResult: TestSuiteStarted | TestStarted | TestFailed | TestIgnored | TestSuiteFinished | TestFinished) {
return `${testResult.name}-${testResult.flowId}`;
}
}
31 changes: 31 additions & 0 deletions src/PHPUnit/Transformer/PHPUnitFixer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { TestFailed, TestIgnored, TestResult } from '../ProblemMatcher';
import { TransformerFactory } from './TransformerFactory';
import { getPrevTestResult } from './utils';

export class PHPUnitFixer {
static fixNoTestStarted(cache: Map<string, TestResult>, testResult: TestFailed | TestIgnored) {
if (testResult.id) {
return testResult;
}

Check warning on line 9 in src/PHPUnit/Transformer/PHPUnitFixer.ts

View check run for this annotation

Codecov / codecov/patch

src/PHPUnit/Transformer/PHPUnitFixer.ts#L8-L9

Added lines #L8 - L9 were not covered by tests

const prevTestResult = getPrevTestResult(new RegExp('^(php_qn):\/\/'), cache, testResult);
if (!prevTestResult) {
return testResult;
}

if (!testResult.locationHint) {
const parts = prevTestResult.locationHint?.split('::') ?? [];
const locationHint = parts.slice(0, Math.max(2, parts.length - 1)).join('::');
testResult.locationHint = [locationHint, testResult.name]
.filter(value => !!value)
.join('::');
}

const transformer = TransformerFactory.factory(testResult.locationHint);
const { id, file } = transformer.fromLocationHit(testResult.locationHint, testResult.name);
testResult.id = id;
testResult.file = file;

return testResult;
}
}
Original file line number Diff line number Diff line change
@@ -1,50 +1,45 @@
import { TeamcityEvent, TestResult, TestStarted, TestSuiteStarted } from '../ProblemMatcher';
import { TeamcityEvent, TestFailed, TestIgnored, TestResult } from '../ProblemMatcher';
import { capitalize } from '../utils';
import { getPrevTestResult } from './utils';

export class PHPUnitFixer {
static fixDetails(results = new Map<string, TestResult>(), testResult: TestResult & {
name: string,
locationHint?: string,
file?: string,
details?: Array<{ file: string, line: number }>,
}) {
if (testResult.details && testResult.file) {
return testResult;
}
class Str {
static prefix = '__pest_evaluable_';

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

if (!result) {
export class PestFixer {
static fixNoTestStarted(cache: Map<string, TestResult>, testResult: TestFailed | TestIgnored) {
if (testResult.id) {
return testResult;
}

const file = result.file!;
if (!testResult.file) {
testResult.file = file;
if (!testResult.duration) {
testResult.duration = 0;
}

if (!testResult.details) {
testResult.details = [{ file: file, line: 1 }];
}
if ('details' in testResult && testResult.details.length > 0) {
const file = testResult.details[0].file;
testResult.id = [file, testResult.name].join('::');
testResult.file = file;

if (!testResult.locationHint) {
const locationHint = result.locationHint?.split('::').slice(0, 2).join('::');
testResult.locationHint = [locationHint, testResult.name]
.filter(value => !!value)
.join('::');
return testResult;
}

return testResult;
}
}
const pattern = new RegExp('^(pest_qn|file):\/\/');
const prevTestResult = getPrevTestResult(pattern, cache, testResult);
if (prevTestResult) {
testResult.id = [
prevTestResult.locationHint?.replace(pattern, ''),
testResult.name,
].filter(v => !!v).join('::');

class Str {
static prefix = '__pest_evaluable_';
return testResult;
}

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

Check warning on line 42 in src/PHPUnit/Transformer/PestFixer.ts

View check run for this annotation

Codecov / codecov/patch

src/PHPUnit/Transformer/PestFixer.ts#L42

Added line #L42 was not covered by tests
}
}

Expand All @@ -53,7 +48,7 @@
return this.fixDataSet(/^tests\//.test(locationHint) ? locationHint : locationHint.substring(locationHint.lastIndexOf('tests/')));
}

static fixFlowId(results = new Map<string, TestResult>(), testResult?: TestResult) {
static fixFlowId(cache: Map<string, TestResult>, testResult?: TestResult) {
if (!testResult) {
return testResult;
}
Expand All @@ -63,7 +58,7 @@
return testResult;
}

const result = Array.from(results.values()).reverse().find((result: TestResult) => {
const result = Array.from(cache.values()).reverse().find((result: TestResult) => {
if (testResult.event !== TeamcityEvent.testStarted) {
return result.event === TeamcityEvent.testStarted && (result as any).name === (testResult as any).name;
}
Expand Down Expand Up @@ -118,4 +113,4 @@

return Str.evaluable(methodName) + dataset;
}
}
}
2 changes: 1 addition & 1 deletion src/PHPUnit/Transformer/PestTransFormer.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TestType } from '../types';
import { PestV2Fixer } from './Fixer';
import { PestV2Fixer } from './PestFixer';
import { PestTransformer } from './PestTransformer';

describe('PestTransformer', () => {
Expand Down
2 changes: 1 addition & 1 deletion src/PHPUnit/Transformer/PestTransformer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { TestDefinition, TestType } from '../types';
import { uncapitalize } from '../utils';
import { PestV1Fixer, PestV2Fixer } from './Fixer';
import { PestV1Fixer, PestV2Fixer } from './PestFixer';
import { PHPUnitTransformer } from './PHPUnitTransformer';
import { TransformerFactory } from './TransformerFactory';

Expand Down
8 changes: 5 additions & 3 deletions src/PHPUnit/Transformer/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export * from './Fixer';
export * from './TransformerFactory';
export * from './Transformer';
export * from './TransformerFactory';
export * from './PHPUnitFixer';
export * from './PHPUnitTransformer';
export * from './PestTransformer';
export * from './PestFixer';
export * from './PestTransformer';
export { getPrevTestResult } from './utils';
24 changes: 24 additions & 0 deletions src/PHPUnit/Transformer/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { TeamcityEvent, TestFailed, TestIgnored, TestResult, TestStarted, TestSuiteStarted } from '../ProblemMatcher';

export const getPrevTestResult = (pattern: RegExp, cache: Map<string, TestResult>, testResult: TestFailed | TestIgnored) => {
for (const prevTestResult of Array.from(cache.values()).reverse()) {
if (isTestStarted(pattern, prevTestResult)) {
return prevTestResult as TestStarted | TestSuiteStarted;
}

if (prevTestResult.event === TeamcityEvent.testCount) {
continue;
}

Check warning on line 11 in src/PHPUnit/Transformer/utils.ts

View check run for this annotation

Codecov / codecov/patch

src/PHPUnit/Transformer/utils.ts#L10-L11

Added lines #L10 - L11 were not covered by tests

if (prevTestResult.event !== testResult.event) {
break;
}
}

return undefined;
};

const isTestStarted = (pattern: RegExp, testResult: TestResult & { locationHint?: string }) => {
return [TeamcityEvent.testStarted, TeamcityEvent.testSuiteStarted].includes(testResult.event)
&& pattern.test(testResult.locationHint ?? '');
};