@samitouri / QOS-React-2 / commits / 46752845eb

[snap tester] QoL: help messages, cmd line validation, ts errors

--- Next PRs: skip / only tests, pretty diffing This PR: 1. Add help messages: ``` $ node packages/snap/dist/runner.js --help Options: --version Show version number [boolean] --sync Run compiler in main thread (instead of using worker threads or subprocesses). Defaults to false. [boolean] [default: true] --worker-threads Run compiler in worker threads (instead of subprocesses). Defaults to true. [boolean] [default: true] --watch Run in watch mode. Defaults to false (single run). [boolean] [default: false] --update Run in update mode. Update mode only affects the first run, subsequent runs (in watch mode) require typing `u` to update. Defaults to false. [boolean] [default: false] --help Show help [boolean] ✨ Done in 0.62s. ``` ``` ... 386 Tests, 386 Passed, 0 Failed Completed in 4434 ms Waiting for input or file changes... u - update fixtures q - quit [any] - rerun tests ``` 2. Surface typescript diagnostics; skip test fixtures if source code has errors ``` $ node packages/snap/dist/runner.js src/Optimization/ConstantPropagation.ts:87:3 - error TS1434: Unexpected keyword or identifier. src/Optimization/ConstantPropagation.ts:87:3 - error TS2304: Cannot find name 'lt'. src/Optimization/ConstantPropagation.ts:87:6 - error TS2552: Cannot find name 'hasChanges'. Did you mean 'onhashchange'? src/Optimization/ConstantPropagation.ts:135:11 - error TS2552: Cannot find name 'hasChanges'. Did you mean 'onhashchange'? src/Optimization/ConstantPropagation.ts:155:10 - error TS2552: Cannot find name 'hasChanges'. Did you mean 'onhashchange'? Compilation failed (5 errors). Found errors in Forget source code, skipping test fixtures. ✨ Done in 10.73s. ``` ``` Compiling... src/Optimization/ConstantPropagation.ts:86:39 - error TS2552: Cannot find name 'HIRFunctin'. Did you mean 'HIRFunction'? Compilation failed (1 error). Test: Found errors in Forget source code, skipping test fixtures. Waiting for input or file changes... u - update fixtures q - quit [any] - rerun tests ```

Mofei Zhang committed Apr 24, 2023 at 15:52 UTC 46752845eb5f2921869cccf00bf51c9156c818aa
1 file changed +126 -77
compiler/forget/packages/snap/src/runner.ts
+126 -77
@@ -14,8 +14,8 @@ import path from "path";
14 import process from "process";
15 import * as readline from "readline";
16 import ts from "typescript";
17 +import yargs from "yargs";
18 import { hideBin } from "yargs/helpers";
18 -import yargs from "yargs/yargs";
19 import { TestResult } from "./compiler-worker";
20 import * as compiler from "./compiler-worker.js";
21
@@ -26,26 +26,40 @@ if (process.stdin.isTTY) {
26 }
27
28 const argv: {
29 - sync?: boolean;
30 - disableWorkerThreads?: boolean;
31 - watch?: boolean;
32 - update?: boolean;
33 -} = yargs(hideBin(process.argv)).argv as any;
29 + sync: boolean;
30 + workerThreads: boolean;
31 + watch: boolean;
32 + update: boolean;
33 +} = yargs
34 + .boolean("sync")
35 + .describe(
36 + "sync",
37 + "Run compiler in main thread (instead of using worker threads or subprocesses). Defaults to false."
38 + )
39 + .default("sync", false)
40 + .boolean("worker-threads")
41 + .describe(
42 + "worker-threads",
43 + "Run compiler in worker threads (instead of subprocesses). Defaults to true."
44 + )
45 + .default("worker-threads", true)
46 + .boolean("watch")
47 + .describe("watch", "Run in watch mode. Defaults to false (single run).")
48 + .default("watch", false)
49 + .boolean("update") // Test mode by default, opt-in to update
50 + .describe(
51 + "update",
52 + "Run in update mode. Update mode only affects the first run, subsequent runs (in watch mode) require typing `u` to update. Defaults to false."
53 + )
54 + .default("update", false)
55 + .help("help")
56 + .strict()
57 + .parseSync(hideBin(process.argv));
58
35 -// Parallel by default
59 const PARALLEL = !argv.sync;
37 -
38 -// Enable worker threads by default
39 -const ENABLE_WORKER_THREADS = !argv.disableWorkerThreads;
40 -
41 -// Single-run by default, opt-in to watch mode
42 -const WATCH = !!argv.watch;
43 -
44 -// Test mode by default, opt-in to update
45 -// NOTE: update mode only affects the first run, subsequent runs (in watch mode)
46 -// require typing `u` to update
47 -const UPDATE = !!argv.update;
48 -
60 +const ENABLE_WORKER_THREADS = argv.workerThreads;
61 +const WATCH = argv.watch;
62 +const UPDATE = argv.update;
63 const WORKER_PATH = require.resolve("./compiler-worker.js");
64 const COMPILER_PATH = path.join(
65 process.cwd(),
@@ -184,7 +198,7 @@ async function update(results: Results): Promise<void> {
198
199 function watchSrc(
200 onStart: () => void,
187 - onComplete: () => void
201 + onComplete: (isSuccess: boolean) => void
202 ): ts.WatchOfConfigFile<ts.SemanticDiagnosticsBuilderProgram> {
203 const configPath = ts.findConfigFile(
204 /*searchPath*/ "./",
@@ -200,8 +214,8 @@ function watchSrc(
214 {},
215 ts.sys,
216 createProgram,
203 - reportDiagnostic,
204 - reportWatchStatusChanged
217 + () => {}, // we manually report errors in afterProgramCreate
218 + () => {} // we manually report watch status
219 );
220
221 const origCreateProgram = host.createProgram;
@@ -212,7 +226,49 @@ function watchSrc(
226 const origPostProgramCreate = host.afterProgramCreate;
227 host.afterProgramCreate = (program) => {
228 origPostProgramCreate!(program);
215 - onComplete();
229 +
230 + // syntactic diagnostics refer to javascript syntax
231 + const errors = program
232 + .getSyntacticDiagnostics()
233 + .filter((diag) => diag.category === ts.DiagnosticCategory.Error);
234 + // semantic diagnostics refer to typescript semantics
235 + errors.push(
236 + ...program
237 + .getSemanticDiagnostics()
238 + .filter((diag) => diag.category === ts.DiagnosticCategory.Error)
239 + );
240 +
241 + if (errors.length > 0) {
242 + for (const diagnostic of errors) {
243 + let fileLoc: string;
244 + if (diagnostic.file) {
245 + // https://github.com/microsoft/TypeScript/blob/ddd5084659c423f4003d2176e12d879b6a5bcf30/src/compiler/program.ts#L663-L674
246 + const { line, character } = ts.getLineAndCharacterOfPosition(
247 + diagnostic.file,
248 + diagnostic.start!
249 + );
250 + const fileName = path.relative(
251 + ts.sys.getCurrentDirectory(),
252 + diagnostic.file.fileName
253 + );
254 + fileLoc = `${fileName}:${line + 1}:${character + 1} - `;
255 + } else {
256 + fileLoc = "";
257 + }
258 + console.error(
259 + `${fileLoc}error TS${diagnostic.code}:`,
260 + ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n")
261 + );
262 + }
263 + console.error(
264 + `Compilation failed (${errors.length} ${
265 + errors.length > 1 ? "errors" : "error"
266 + }).\n`
267 + );
268 + }
269 +
270 + const isSuccess = errors.length === 0;
271 + onComplete(isSuccess);
272 };
273
274 // `createWatchProgram` creates an initial program, watches files, and updates
@@ -220,35 +276,6 @@ function watchSrc(
276 return ts.createWatchProgram(host);
277 }
278
223 -const formatHost = {
224 - getCanonicalFileName: (path: string) => path,
225 - getCurrentDirectory: ts.sys.getCurrentDirectory,
226 - getNewLine: () => ts.sys.newLine,
227 -};
228 -
229 -// Gets called if TS reported any errors with the source.
230 -// TODO: wire this up, if there are errors we should just report them and
231 -// probably not run tests.
232 -function reportDiagnostic(diagnostic: ts.Diagnostic): void {
233 - console.error(
234 - "Error",
235 - diagnostic.code,
236 - ":",
237 - ts.flattenDiagnosticMessageText(
238 - diagnostic.messageText,
239 - formatHost.getNewLine()
240 - )
241 - );
242 -}
243 -
244 -/**
245 - * Prints a diagnostic every time the watch status changes.
246 - * This is mainly for messages like "Starting compilation" or "Compilation completed".
247 - */
248 -function reportWatchStatusChanged(diagnostic: ts.Diagnostic): void {
249 - // console.info(ts.formatDiagnostic(diagnostic, formatHost));
250 -}
251 -
279 enum Mode {
280 Test = "Test",
281 Update = "Update",
@@ -258,39 +285,55 @@ enum Mode {
285 * Runs the compiler in watch or single-execution mode
286 */
287 async function main(): Promise<void> {
261 - // Monotonically increasing integer to describe the 'version' of the compiler.
262 - // This is passed to `compile()` (from compiler-worker) when compiling, so
263 - // that the worker knows when it has to reset its module cache and when its
264 - // safe to use a cached compiler version
265 - let compilerVersion = 0;
266 -
288 if (WATCH) {
289 + // Monotonically increasing integer to describe the 'version' of the compiler.
290 + // This is passed to `compile()` (from compiler-worker) when compiling, so
291 + // that the worker knows when it has to reset its module cache and when its
292 + // safe to use a cached compiler version
293 + let compilerVersion = 0;
294 + let isCompilerValid = false;
295 +
296 function onStart() {
297 // Notify the user when compilation starts but don't clear the screen yet
270 - console.log("Compiling...");
298 + console.log("\nCompiling...");
299 }
300
301 // Callback to re-run tests after some change
302 async function onChange({ mode }: { mode: Mode }) {
275 - const start = performance.now();
276 - console.clear();
277 - console.log("Running tests...");
278 - const results = await run(compilerVersion);
279 - console.clear();
280 - if (mode === Mode.Update) {
281 - update(results);
303 + if (isCompilerValid) {
304 + const start = performance.now();
305 + console.clear();
306 + console.log("Running tests...");
307 + const results = await run(compilerVersion);
308 + console.clear();
309 + if (mode === Mode.Update) {
310 + update(results);
311 + } else {
312 + report(results);
313 + }
314 + const end = performance.now();
315 + console.log(`Completed in ${Math.floor(end - start)} ms`);
316 } else {
283 - report(results);
317 + console.error(
318 + `${mode}: Found errors in Forget source code, skipping test fixtures.`
319 + );
320 }
285 - const end = performance.now();
286 - console.log(`Completed in ${end - start} ms`);
321 + console.log(
322 + "\nWaiting for input or file changes...\n" +
323 + "u - update fixtures\n" +
324 + "q - quit\n" +
325 + "[any] - rerun tests\n"
326 + );
327 }
328
329 // Run TS in incremental watch mode
290 - const _tsWatch = watchSrc(onStart, () => {
330 + const _tsWatch = watchSrc(onStart, (isSuccess) => {
331 // Bump the compiler version after a build finishes
332 // and re-run tests
293 - compilerVersion++;
333 + if (isSuccess) {
334 + compilerVersion++;
335 + }
336 + isCompilerValid = isSuccess;
337 onChange({ mode: Mode.Test });
338 });
339
@@ -331,18 +374,24 @@ async function main(): Promise<void> {
374 null;
375 tsWatch = watchSrc(
376 () => {},
334 - async () => {
335 - const results = await run(compilerVersion);
336 - if (UPDATE) {
337 - update(results);
377 + async (isSuccess: boolean) => {
378 + if (isSuccess) {
379 + const results = await run(0);
380 + if (UPDATE) {
381 + update(results);
382 + } else {
383 + report(results);
384 + }
385 } else {
339 - report(results);
386 + console.error(
387 + "Found errors in Forget source code, skipping test fixtures."
388 + );
389 }
390 if (tsWatch != null) {
391 tsWatch.close();
392 }
393 await worker.end();
345 - process.exit();
394 + process.exit(isSuccess ? 0 : -1);
395 }
396 );
397 }