vscode/build/lib/watch/watch-win32.ts
Connor Peet 26120e5bf4
testing: add temporary failure tracker to the selfhost test runner (#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.
2024-05-07 00:54:33 +02:00

109 lines
3.1 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as path from 'path';
import * as cp from 'child_process';
import * as fs from 'fs';
import * as File from 'vinyl';
import * as es from 'event-stream';
import * as filter from 'gulp-filter';
import { Stream } from 'stream';
const watcherPath = path.join(__dirname, 'watcher.exe');
function toChangeType(type: '0' | '1' | '2'): 'change' | 'add' | 'unlink' {
switch (type) {
case '0': return 'change';
case '1': return 'add';
default: return 'unlink';
}
}
function watch(root: string): Stream {
const result = es.through();
let child: cp.ChildProcess | null = cp.spawn(watcherPath, [root]);
child.stdout!.on('data', function (data) {
const lines: string[] = data.toString('utf8').split('\n');
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line.length === 0) {
continue;
}
const changeType = <'0' | '1' | '2'>line[0];
const changePath = line.substr(2);
// filter as early as possible
if (/^\.git/.test(changePath) || /(^|\\)out($|\\)/.test(changePath)) {
continue;
}
const changePathFull = path.join(root, changePath);
const file = new File({
path: changePathFull,
base: root
});
(<any>file).event = toChangeType(changeType);
result.emit('data', file);
}
});
child.stderr!.on('data', function (data) {
result.emit('error', data);
});
child.on('exit', function (code) {
result.emit('error', 'Watcher died with code ' + code);
child = null;
});
process.once('SIGTERM', function () { process.exit(0); });
process.once('SIGTERM', function () { process.exit(0); });
process.once('exit', function () { if (child) { child.kill(); } });
return result;
}
const cache: { [cwd: string]: Stream } = Object.create(null);
module.exports = function (pattern: string | string[] | filter.FileFunction, options?: { cwd?: string; base?: string; dot?: boolean }) {
options = options || {};
const cwd = path.normalize(options.cwd || process.cwd());
let watcher = cache[cwd];
if (!watcher) {
watcher = cache[cwd] = watch(cwd);
}
const rebase = !options.base ? es.through() : es.mapSync(function (f: File) {
f.base = options!.base!;
return f;
});
return watcher
.pipe(filter(['**', '!.git{,/**}'], { dot: options.dot })) // ignore all things git
.pipe(filter(pattern, { dot: options.dot }))
.pipe(es.map(function (file: File, cb) {
fs.stat(file.path, function (err, stat) {
if (err && err.code === 'ENOENT') { return cb(undefined, file); }
if (err) { return cb(); }
if (!stat.isFile()) { return cb(); }
fs.readFile(file.path, function (err, contents) {
if (err && err.code === 'ENOENT') { return cb(undefined, file); }
if (err) { return cb(); }
file.contents = contents;
file.stat = stat;
cb(undefined, file);
});
});
}))
.pipe(rebase);
};