forked from microsoft/vscode
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
testing: add temporary failure tracker to the selfhost test runner (m…
…icrosoft#212134) For /fixTestFailures, I want to get more 'real world' tests and test fixes. This makes a change in the selfhost test provider such that when a test fails and is then fixed, we record the code changes into a JSON file in the `.build` directory. In a few days I'll follow up with team members to collect their test failures and use them as evaluation tests for copilot. The FailureTracker will be removed when I've gotten enough data.
- Loading branch information
1 parent
3f91c9b
commit 26120e5
Showing
5 changed files
with
151 additions
and
6 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
.vscode/extensions/vscode-selfhost-test-provider/.vscode/launch.json
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,11 @@ | ||
{ | ||
"configurations": [ | ||
{ | ||
"args": ["--extensionDevelopmentPath=${workspaceFolder}"], | ||
"name": "Launch Extension", | ||
"outFiles": ["${workspaceFolder}/out/**/*.js"], | ||
"request": "launch", | ||
"type": "extensionHost" | ||
} | ||
] | ||
} |
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
126 changes: 126 additions & 0 deletions
126
.vscode/extensions/vscode-selfhost-test-provider/src/failureTracker.ts
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,126 @@ | ||
/*--------------------------------------------------------------------------------------------- | ||
* Copyright (c) Microsoft Corporation. All rights reserved. | ||
* Licensed under the MIT License. See License.txt in the project root for license information. | ||
*--------------------------------------------------------------------------------------------*/ | ||
|
||
import { spawn } from 'child_process'; | ||
import { readFile, writeFile } from 'fs/promises'; | ||
import { join } from 'path'; | ||
import * as vscode from 'vscode'; | ||
|
||
interface IGitState { | ||
commitId: string; | ||
tracked: string; | ||
untracked: Record<string, string>; | ||
} | ||
|
||
interface ITrackedRemediation { | ||
snapshot: vscode.TestResultSnapshot; | ||
failing: IGitState; | ||
passing: IGitState; | ||
} | ||
|
||
const MAX_FAILURES = 10; | ||
|
||
export class FailureTracker { | ||
private readonly disposables: vscode.Disposable[] = []; | ||
private readonly lastFailed = new Map< | ||
string, | ||
{ snapshot: vscode.TestResultSnapshot; failing: IGitState } | ||
>(); | ||
|
||
private readonly logFile = join(this.rootDir, '.build/vscode-test-failures.json'); | ||
private logs?: ITrackedRemediation[]; | ||
|
||
constructor(private readonly rootDir: string) { | ||
this.disposables.push( | ||
vscode.tests.onDidChangeTestResults(() => { | ||
const last = vscode.tests.testResults[0]; | ||
if (!last) { | ||
return; | ||
} | ||
|
||
let gitState: Promise<IGitState> | undefined; | ||
const getGitState = () => gitState ?? (gitState = this.captureGitState()); | ||
|
||
const queue = [last.results]; | ||
for (let i = 0; i < queue.length; i++) { | ||
for (const snapshot of queue[i]) { | ||
// only interested in states of leaf tests | ||
if (snapshot.children.length) { | ||
queue.push(snapshot.children); | ||
continue; | ||
} | ||
|
||
const key = `${snapshot.uri}/${snapshot.id}`; | ||
const prev = this.lastFailed.get(key); | ||
if (snapshot.taskStates.some(s => s.state === vscode.TestResultState.Failed)) { | ||
// unset the parent to avoid a circular JSON structure: | ||
getGitState().then(s => this.lastFailed.set(key, { snapshot: { ...snapshot, parent: undefined }, failing: s })); | ||
} else if (prev) { | ||
this.lastFailed.delete(key); | ||
getGitState().then(s => this.append({ ...prev, passing: s })); | ||
} | ||
} | ||
} | ||
}) | ||
); | ||
} | ||
|
||
private async append(log: ITrackedRemediation) { | ||
if (!this.logs) { | ||
try { | ||
this.logs = JSON.parse(await readFile(this.logFile, 'utf-8')); | ||
} catch { | ||
this.logs = []; | ||
} | ||
} | ||
|
||
const logs = this.logs!; | ||
logs.push(log); | ||
if (logs.length > MAX_FAILURES) { | ||
logs.splice(0, logs.length - MAX_FAILURES); | ||
} | ||
|
||
await writeFile(this.logFile, JSON.stringify(logs, undefined, 2)); | ||
} | ||
|
||
private async captureGitState() { | ||
const [commitId, tracked, untracked] = await Promise.all([ | ||
this.exec('git', ['rev-parse', 'HEAD']), | ||
this.exec('git', ['diff', 'HEAD']), | ||
this.exec('git', ['ls-files', '--others', '--exclude-standard']).then(async output => { | ||
const mapping: Record<string, string> = {}; | ||
await Promise.all( | ||
output | ||
.trim() | ||
.split('\n') | ||
.map(async f => { | ||
mapping[f] = await readFile(join(this.rootDir, f), 'utf-8'); | ||
}) | ||
); | ||
return mapping; | ||
}), | ||
]); | ||
return { commitId, tracked, untracked }; | ||
} | ||
|
||
public dispose() { | ||
this.disposables.forEach(d => d.dispose()); | ||
} | ||
|
||
private exec(command: string, args: string[]): Promise<string> { | ||
return new Promise((resolve, reject) => { | ||
const child = spawn(command, args, { stdio: 'pipe', cwd: this.rootDir }); | ||
let output = ''; | ||
child.stdout.setEncoding('utf-8').on('data', b => (output += b)); | ||
child.stderr.setEncoding('utf-8').on('data', b => (output += b)); | ||
child.on('error', reject); | ||
child.on('exit', code => | ||
code === 0 | ||
? resolve(output) | ||
: reject(new Error(`Failed with error code ${code}\n${output}`)) | ||
); | ||
}); | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
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