-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(append-only-progress): Implement new reporter (#213)
* feat(append-only-progress): Implement new reporter * Create a new progress reporter, one which only appends. It prints progress every 10 seconds. * When the "progress" reporter is not used when the console does not support it. * refactor(progress-reporter-spec): Remove duplicate code
- Loading branch information
Showing
9 changed files
with
288 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { MatchedMutant, MutantResult, MutantStatus } from 'stryker-api/report'; | ||
import * as chalk from 'chalk'; | ||
import * as os from 'os'; | ||
import ProgressKeeper from './ProgressKeeper'; | ||
import Timer from '../utils/Timer'; | ||
|
||
export default class ProgressAppendOnlyReporter extends ProgressKeeper { | ||
private intervalReference: NodeJS.Timer; | ||
private timer: Timer; | ||
|
||
onAllMutantsMatchedWithTests(matchedMutants: ReadonlyArray<MatchedMutant>): void { | ||
super.onAllMutantsMatchedWithTests(matchedMutants); | ||
this.timer = new Timer(); | ||
this.intervalReference = setInterval(() => this.render(), 10000); | ||
} | ||
|
||
onAllMutantsTested(): void { | ||
clearInterval(this.intervalReference); | ||
} | ||
|
||
private render() { | ||
process.stdout.write(`Mutation testing ${this.procent()} (ETC ${this.etc()}) ` + | ||
`[${this.progress.killed} ${this.progress.killedLabel}] ` + | ||
`[${this.progress.survived} ${this.progress.survivedLabel}] ` + | ||
`[${this.progress.noCoverage} ${this.progress.noCoverageLabel}] ` + | ||
`[${this.progress.timeout} ${this.progress.timeoutLabel}] ` + | ||
`[${this.progress.error} ${this.progress.errorLabel}]` + | ||
os.EOL); | ||
} | ||
|
||
private procent() { | ||
return Math.floor(this.progress.testedCount / this.progress.totalCount * 100) + '%'; | ||
} | ||
|
||
private etc() { | ||
const etcSeconds = Math.floor(this.timer.elapsedSeconds() / this.progress.testedCount * (this.progress.totalCount - this.progress.testedCount)); | ||
if (isFinite(etcSeconds)) { | ||
return etcSeconds + 's'; | ||
} else { | ||
return 'n/a'; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
import * as chalk from 'chalk'; | ||
import { MatchedMutant, Reporter, MutantResult, MutantStatus } from 'stryker-api/report'; | ||
|
||
abstract class ProgressKeeper implements Reporter { | ||
|
||
// progress contains Labels, because on initation of the ProgressBar the width is determined based on the amount of characters of the progressBarContent inclusive ASCII-codes for colors | ||
protected progress = { | ||
error: 0, | ||
survived: 0, | ||
killed: 0, | ||
timeout: 0, | ||
noCoverage: 0, | ||
testedCount: 0, | ||
totalCount: 0, | ||
killedLabel: chalk.green.bold('killed'), | ||
survivedLabel: chalk.red.bold('survived'), | ||
noCoverageLabel: chalk.red.bold('no coverage'), | ||
timeoutLabel: chalk.yellow.bold('timeout'), | ||
errorLabel: chalk.yellow.bold('error') | ||
}; | ||
|
||
onAllMutantsMatchedWithTests(matchedMutants: ReadonlyArray<MatchedMutant>): void { | ||
this.progress.totalCount = matchedMutants.filter(m => m.scopedTestIds.length > 0).length; | ||
} | ||
|
||
onMutantTested(result: MutantResult): void { | ||
this.progress.testedCount++; | ||
switch (result.status) { | ||
case MutantStatus.NoCoverage: | ||
this.progress.testedCount--; // correct for not tested, because no coverage | ||
this.progress.noCoverage++; | ||
break; | ||
case MutantStatus.Killed: | ||
this.progress.killed++; | ||
break; | ||
case MutantStatus.Survived: | ||
this.progress.survived++; | ||
break; | ||
case MutantStatus.TimedOut: | ||
this.progress.timeout++; | ||
break; | ||
case MutantStatus.Error: | ||
this.progress.error++; | ||
break; | ||
} | ||
} | ||
|
||
} | ||
export default ProgressKeeper; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { MutantStatus, MatchedMutant, MutantResult } from 'stryker-api/report'; | ||
|
||
export function mutantResult(status: MutantStatus): MutantResult { | ||
return { | ||
location: null, | ||
mutatedLines: null, | ||
mutatorName: null, | ||
originalLines: null, | ||
replacement: null, | ||
sourceFilePath: null, | ||
testsRan: null, | ||
status, | ||
range: null | ||
}; | ||
} | ||
|
||
export function matchedMutant(numberOfTests: number): MatchedMutant { | ||
let scopedTestIds: number[] = []; | ||
for (let i = 0; i < numberOfTests; i++) { | ||
scopedTestIds.push(1); | ||
} | ||
return { | ||
mutatorName: null, | ||
scopedTestIds: scopedTestIds, | ||
timeSpentScopedTests: null, | ||
filename: null, | ||
replacement: null | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import * as sinon from 'sinon'; | ||
import { expect } from 'chai'; | ||
import * as broadcastReporter from '../../src/reporters/BroadcastReporter'; | ||
import ReporterOrchestrator from '../../src/ReporterOrchestrator'; | ||
import { ReporterFactory } from 'stryker-api/report'; | ||
|
||
describe('ReporterOrchestrator', () => { | ||
let sandbox: sinon.SinonSandbox; | ||
let sut: ReporterOrchestrator; | ||
let isTTY: boolean; | ||
let broadcastReporterMock: sinon.SinonStub; | ||
|
||
beforeEach(() => { | ||
sandbox = sinon.sandbox.create(); | ||
broadcastReporterMock = sandbox.stub(broadcastReporter, 'default'); | ||
captureTTY(); | ||
}); | ||
|
||
afterEach(() => { | ||
sandbox.restore(); | ||
restoreTTY(); | ||
}); | ||
|
||
it('should register default reporters', () => { | ||
expect(ReporterFactory.instance().knownNames()).to.have.lengthOf(5); | ||
}); | ||
|
||
describe('createBroadcastReporter()', () => { | ||
|
||
// https://github.com/stryker-mutator/stryker/issues/212 | ||
it('should create "progress-append-only" instead of "progress" reporter if process.stdout is not a tty', () => { | ||
setTTY(false); | ||
sut = new ReporterOrchestrator({ reporter: 'progress' }); | ||
sut.createBroadcastReporter(); | ||
expect(broadcastReporterMock).to.have.been.calledWithNew; | ||
expect(broadcastReporterMock).to.have.been.calledWith(sinon.match( | ||
(reporters: broadcastReporter.NamedReporter[]) => reporters[0].name === 'progress-append-only')); | ||
}); | ||
|
||
it('should create the correct reporters', () => { | ||
setTTY(true); | ||
sut = new ReporterOrchestrator({ reporter: ['progress', 'progress-append-only'] }); | ||
sut.createBroadcastReporter(); | ||
expect(broadcastReporterMock).to.have.been.calledWith(sinon.match( | ||
(reporters: broadcastReporter.NamedReporter[]) => reporters[0].name === 'progress' && reporters[1].name === 'progress-append-only')); | ||
}); | ||
}); | ||
|
||
function captureTTY() { | ||
isTTY = (process.stdout as any)['isTTY']; | ||
} | ||
|
||
function restoreTTY() { | ||
(process.stdout as any)['isTTY'] = isTTY; | ||
} | ||
|
||
function setTTY(val: boolean) { | ||
(process.stdout as any)['isTTY'] = val; | ||
} | ||
}); |
Oops, something went wrong.