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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# test-runner - Changelog

## 3.3.0 - 2026-02-25

* Add `numberOfResets` to `TestRunnerResult`, tracking how many times a test run was cancelled and restarted due to lack of progress.
* Included in JSON output and JUnit XML report properties.

## 3.2.0 - 2024-12-10

* Add `QueryHelper.setGlobalRetryOptions()` static method.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apexdevtools/test-runner",
"version": "3.2.0",
"version": "3.3.0",
"description": "Apex parallel test runner with reliability goodness",
"author": {
"name": "Apex Dev Tools Team",
Expand Down
2 changes: 2 additions & 0 deletions src/command/Testall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,8 @@ export class Testall {
Promise.resolve(missingTests),
store
);

store.numberOfResets = result.numberOfResets;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/results/OutputGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export interface TestRunSummary {
runIds: string[];
reruns: TestRerun[];
coverageResult?: CoverageReport;
numberOfResets: number;
}

export interface OutputGenerator {
Expand Down
20 changes: 16 additions & 4 deletions src/results/ReportGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,16 @@ export class ReportGenerator implements OutputGenerator {
fileName: string,
runSummary: TestRunSummary
): void {
const { startTime, testResults, runResult, reruns } = runSummary;
const { startTime, testResults, runResult, reruns, numberOfResets } =
runSummary;
const results = testResults as ExtendedApexTestResult[];
const summary = this.summary(startTime, results, runResult, reruns);
const summary = this.summary(
startTime,
results,
runResult,
reruns,
numberOfResets
);
logger.logOutputFile(
path.join(outputDirBase, fileName + '.xml'),
this.generateJunit(summary, results)
Expand All @@ -58,7 +65,8 @@ export class ReportGenerator implements OutputGenerator {
startTime: Date,
testResults: ExtendedApexTestResult[],
runResults: ApexTestRunResult,
reruns: TestRerun[]
reruns: TestRerun[],
numberOfResets: number
): SummaryData {
// combine test and method names for fullname
testResults.forEach(test => {
Expand Down Expand Up @@ -130,6 +138,7 @@ export class ReportGenerator implements OutputGenerator {
username: this.username,
testRunId: runResults.AsyncApexJobId,
userId: runResults.UserId,
numberOfResets,
};
}

Expand Down Expand Up @@ -180,6 +189,7 @@ export class ReportGenerator implements OutputGenerator {
junit += ` <property name="username" value="${summary.username}"/>\n`;
junit += ` <property name="testRunId" value="${summary.testRunId}"/>\n`;
junit += ` <property name="userId" value="${summary.userId}"/>\n`;
junit += ` <property name="numberOfResets" value="${summary.numberOfResets}"/>\n`;
junit += ' </properties>\n';
testResults.forEach(test => {
const success = test.Outcome === 'Pass';
Expand Down Expand Up @@ -229,7 +239,8 @@ export class ReportGenerator implements OutputGenerator {
json += ` "orgId": "${summary.orgId}",\n`;
json += ` "username": "${summary.username}",\n`;
json += ` "testRunId": "${summary.testRunId}",\n`;
json += ` "userId": "${summary.userId}"\n`;
json += ` "userId": "${summary.userId}",\n`;
json += ` "numberOfResets": ${summary.numberOfResets}\n`;
json += ' },\n';
json += ' "tests": [\n';

Expand Down Expand Up @@ -298,4 +309,5 @@ interface SummaryData {
username: string;
testRunId: string;
userId: string;
numberOfResets: number;
}
4 changes: 4 additions & 0 deletions src/results/TestResultStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export class TestResultStore {
asyncError?: TestError;
reruns: TestRerun[];
coverage?: CoverageReport;
numberOfResets: number;

get resultsArray() {
return Array.from(this.tests.values());
Expand All @@ -31,6 +32,7 @@ export class TestResultStore {
this.runIds = [];
this.tests = new Map<string, ApexTestResult>();
this.reruns = [];
this.numberOfResets = 0;
}

public saveAsyncResult(res: TestRunnerResult): void {
Expand All @@ -43,6 +45,7 @@ export class TestResultStore {
});

this.asyncError = res.error;
this.numberOfResets = res.numberOfResets;
}

public saveSyncResult(reruns: TestRerun[]): void {
Expand Down Expand Up @@ -88,6 +91,7 @@ export class TestResultStore {
runIds: this.runIds,
reruns: this.reruns,
coverageResult: this.coverage,
numberOfResets: this.numberOfResets,
};
}

Expand Down
7 changes: 6 additions & 1 deletion src/runner/TestRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export interface TestRunnerResult {
run: ApexTestRunResult;
tests: ApexTestResult[];
error?: TestError;
numberOfResets: number; // Track the number of times the test run has been reset due to hanging or cancellation
}

export interface TestRunner {
Expand All @@ -66,7 +67,7 @@ export class AsyncTestRunner implements TestRunner {
private readonly _testItems: TestItem[];
private readonly _options: TestRunnerOptions;
private readonly _testService: TestService;
private _stats;
private _stats: TestStats;

static forClasses(
logger: Logger,
Expand Down Expand Up @@ -141,6 +142,9 @@ export class AsyncTestRunner implements TestRunner {
token
);

// Add numberOfResets to the result object
result.numberOfResets = this._stats.getNumberOfTimesReset();

// Ensure result for partial reporting
try {
if (token?.isCancellationRequested) {
Expand Down Expand Up @@ -220,6 +224,7 @@ export class AsyncTestRunner implements TestRunner {
return (lastResult = {
run,
tests,
numberOfResets: this._stats.getNumberOfTimesReset(),
});
},

Expand Down
4 changes: 4 additions & 0 deletions test/command/TestDebugLogs.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ describe('TestDebugLogs', () => {
const runner = new MockTestRunner({
run: runnerResult,
tests: mockTestResults,
numberOfResets: 0,
});
const methodCollector = mockDefaultCollector(logger, mockConnection);
qhStub.query.onCall(0).resolves([]);
Expand Down Expand Up @@ -110,6 +111,7 @@ describe('TestDebugLogs', () => {
const runner = new MockTestRunner({
run: runnerResult,
tests: mockTestResults,
numberOfResets: 0,
});
const methodCollector = mockDefaultCollector(logger, mockConnection);
qhStub.query.onCall(0).resolves([{ Id: 'AnId' }]); // User Id
Expand Down Expand Up @@ -154,6 +156,7 @@ describe('TestDebugLogs', () => {
const runner = new MockTestRunner({
run: runnerResult,
tests: mockTestResults,
numberOfResets: 0,
});
const methodCollector = mockDefaultCollector(logger, mockConnection);
qhStub.query.onCall(0).resolves([{ Id: 'AnId' }]); // User Id
Expand Down Expand Up @@ -198,6 +201,7 @@ describe('TestDebugLogs', () => {
const runner = new MockTestRunner({
run: runnerResult,
tests: mockTestResults,
numberOfResets: 0,
});
const { classId, className, methodName } = defaultTestInfo;
const methodCollector = new MockTestMethodCollector(
Expand Down
12 changes: 12 additions & 0 deletions test/command/Testall.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 1,
});
const testMethods = mockDefaultCollector(logger, mockConnection);

Expand All @@ -197,6 +198,7 @@ describe('TestAll', () => {
reruns: [],
runIds: [testRunId],
coverageResult: undefined,
numberOfResets: 1,
});
});

Expand All @@ -208,6 +210,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: [],
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);

Expand Down Expand Up @@ -247,6 +250,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);

Expand Down Expand Up @@ -294,6 +298,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);
const mockTestResult = {
Expand Down Expand Up @@ -343,6 +348,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);
const mockTestResult = {
Expand Down Expand Up @@ -398,6 +404,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);
testingServiceSyncStub.rejects(new Error('Request Error'));
Expand Down Expand Up @@ -465,6 +472,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);
const mockTestResult = {
Expand Down Expand Up @@ -524,6 +532,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);

Expand Down Expand Up @@ -590,6 +599,7 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: mockTestResults,
numberOfResets: 0,
});
const testMethods = mockDefaultCollector(logger, mockConnection);

Expand Down Expand Up @@ -663,9 +673,11 @@ describe('TestAll', () => {
const runner = new MockTestRunner({
run: mockRunResult,
tests: [mockTestResults[0]],
numberOfResets: 0,
}).addNextResult({
run: mockRunResult,
tests: [mockTestResults[1]],
numberOfResets: 0,
});
const testMethods = new MockTestMethodCollector(
logger,
Expand Down
1 change: 1 addition & 0 deletions test/report/ClassTimeGenerator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ describe('ClassTimeGenerator', () => {
},
runIds: ['job Id'],
reruns: [],
numberOfResets: 0,
});

expect(logger.files.length).to.equal(2);
Expand Down
2 changes: 2 additions & 0 deletions test/report/CoverageReporter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ describe('CoverageReporter', () => {
},
],
},
numberOfResets: 0,
});

expect(executeStub.calledOnce).to.be.true;
Expand Down Expand Up @@ -99,6 +100,7 @@ describe('CoverageReporter', () => {
runIds: ['job Id'],
reruns: [],
coverageResult: undefined,
numberOfResets: 0,
});

expect(executeStub.called).to.be.false;
Expand Down
1 change: 1 addition & 0 deletions test/report/ExecutionMapGenerator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ describe('ExecutionMapGenerator', () => {
},
runIds: ['job Id'],
reruns: [],
numberOfResets: 0,
});

expect(logger.files.length).to.equal(1);
Expand Down
Loading