main 98594d0

engines.mjs RawHistoryFolder

1// SPDX-License-Identifier: MIT
2//
3// Runs the oracle corpus, the fromJSON corpus, the number corpus and the string
4// comparison corpus through both expression engines of actions/runner and lists
5// where they differ:
6//   legacy  src/Sdk/DTExpressions2 (driver ../runner)
7//   newer   src/Sdk/Expressions    (driver ./runner-new)
8//
9//   dotnet build -c Release expr/gen/runner     -p:RunnerSrc=$RUNNER_CLONE/src/Sdk
10//   dotnet build -c Release expr/gen/runner-new -p:RunnerSrc=$RUNNER_CLONE/src/Sdk
11//   RUNNER_CLONE=<clone> node expr/gen/runner-new/engines.mjs
12//
13// The exception message of a fromJSON failure is not compared (the engines word
14// it differently); everything else must be identical. Exit code 1 if anything
15// differs beyond the known difference: negative zero (the newer engine prints 0).
16
17import { execFileSync } from "node:child_process";
18import { readFileSync, writeFileSync, mkdtempSync, rmSync } from "node:fs";
19import { tmpdir } from "node:os";
20import { join, dirname } from "node:path";
21import { fileURLToPath } from "node:url";
22import { gunzipSync } from "node:zlib";
23import { checkEnvironment } from "../runner/env.mjs";
24
25const here = dirname(fileURLToPath(import.meta.url));
26const testdata = join(here, "../../testdata");
27checkEnvironment(process.argv[2]);
28
29const dec = h => (h === "-" ? "" : h.split(".").map(u => String.fromCharCode(parseInt(u, 16))).join(""));
30const lit = s => "'" + s.replaceAll("'", "''") + "'";
31const hasLone = s => /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/.test(s);
32
33const oracle = JSON.parse(gunzipSync(readFileSync(join(testdata, "oracle.json.gz"))));
34const corpora = { oracle: { contexts: oracle.contexts, cases: oracle.cases.map(c => c.e) } };
35const exprs = [];
36for (const c of JSON.parse(readFileSync(join(testdata, "fromjson_cases.json"), "utf8"))) exprs.push(`toJSON(fromJSON(${lit(c.in)}))`);
37for (const l of readFileSync(join(testdata, "number_cases.txt"), "utf8").trim().split("\n")) {
38  const s = dec(l.split(" ")[0]);
39  if (hasLone(s)) continue;
40  for (const op of ["== 0", "== 1", "> 255", "< 0"]) exprs.push(`${lit(s)} ${op}`);
41}
42for (const l of readFileSync(join(testdata, "ordinal_cases.txt"), "utf8").trim().split("\n")) {
43  const [a, b] = l.split(" ");
44  const A = dec(a), B = dec(b);
45  if (hasLone(A + B)) continue;
46  exprs.push(`${lit(A)} == ${lit(B)}`, `${lit(A)} < ${lit(B)}`, `contains(${lit(A)}, ${lit(B)})`, `startsWith(${lit(A)}, ${lit(B)})`, `endsWith(${lit(A)}, ${lit(B)})`);
47}
48corpora.strings = { contexts: {}, cases: exprs };
49
50const run = (dll, input) => execFileSync("dotnet", [dll, input], { maxBuffer: 1 << 30 }).toString("utf8").split("\n").filter(Boolean).map(l => JSON.parse(l));
51const sig = r => (r.err ? JSON.stringify(["err", r.err.t, r.err.x === "JsonReaderException" ? r.err.x : r.err.m]) : JSON.stringify(r));
52
53const tmp = mkdtempSync(join(tmpdir(), "engines-"));
54let unexpected = 0, total = 0;
55for (const [name, c] of Object.entries(corpora)) {
56  const input = join(tmp, name + ".json");
57  writeFileSync(input, JSON.stringify({ contexts: c.contexts, cases: c.cases.map(e => ({ e })) }));
58  const a = run(join(here, "../runner/bin/Release/net9.0/runner.dll"), input);
59  const b = run(join(here, "bin/Release/net9.0/runner-new.dll"), input);
60  let diffs = 0;
61  a.forEach((r, i) => {
62    total++;
63    if (sig(r) === sig(b[i])) return;
64    diffs++;
65    const known = /^(toJSON\(-0\)|-0|toJSON\(fromJSON\('-\.0'\)\))$/.test(c.cases[i]);
66    if (!known) unexpected++;
67    console.log(`${known ? "known " : "DIFF  "} ${name}: ${JSON.stringify(c.cases[i].slice(0, 80))}\n   legacy ${sig(r).slice(0, 100)}\n   newer  ${sig(b[i]).slice(0, 100)}`);
68  });
69  console.log(`${name}: ${a.length} expressions, ${diffs} differences`);
70}
71rmSync(tmp, { recursive: true });
72console.log(`${total} expressions, ${unexpected} unexpected differences`);
73process.exit(unexpected ? 1 : 0);

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 3

↑↓ moveenter openesc close