From 986da08c0d2a0480f7f8d3d73547319e48962d55 Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:16:02 -0600 Subject: [PATCH 1/7] Report java-web's slowest OpenJDK tests and write every test's time to build/jtreg/times.tsv. --- scripts/jtreg/run.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/scripts/jtreg/run.ts b/scripts/jtreg/run.ts index ea891b8..342e8b4 100644 --- a/scripts/jtreg/run.ts +++ b/scripts/jtreg/run.ts @@ -446,6 +446,8 @@ console.log(`${selected.length} tests, ${queue.length} to run, ${jobs} at a time const counts: Record = {}; const regressions: string[] = []; +// java-web's time per test, to spot tests that run close to the timeout +const times: [string, number][] = []; let finished = 0; async function coolDown() { const zone = "/sys/class/thermal/thermal_zone0/temp"; @@ -464,7 +466,10 @@ async function worker() { const r: Record = { id: tc.id, ...(await runCase(tc)) }; counts[r.status as string] = (counts[r.status as string] ?? 0) + 1; finished++; - if (onlyPassing) { if (r.status !== "pass") regressions.push(`${tc.id}: ${r.status} ${r.why ?? ""}`); } + if (onlyPassing) { + if (r.status !== "pass") regressions.push(`${tc.id}: ${r.status} ${r.why ?? ""}`); + if (typeof r.ms === "number") times.push([tc.id, r.ms]); + } else appendFileSync(resultsFile, JSON.stringify(r) + "\n"); if (r.status !== "skip") console.log(`[${finished}] ${r.status.toString().padEnd(7)} ${tc.id}${r.why ? " " + r.why : ""}`); } @@ -474,6 +479,9 @@ await Promise.all(Array.from({ length: jobs }, worker)); console.log(counts); if (onlyPassing) { + times.sort((a, b) => b[1] - a[1]); + writeFileSync(join(out, "times.tsv"), times.map(([id, ms]) => `${ms}\t${id}`).join("\n") + "\n"); + console.log("slowest:\n" + times.slice(0, 15).map(([id, ms]) => `${Math.round(ms / 1000)}s ${id}`).join("\n")); if (regressions.length) { console.log("regressions:\n" + regressions.join("\n")); process.exit(1); From 3efc1335dc3d1f13da969ba6733095fbc71430ed Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:19:42 -0600 Subject: [PATCH 2/7] Run every OpenJDK test for any change to code they load, instead of guessing areas from file names. --- scripts/jtreg/select.ts | 26 +++++++++----------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/scripts/jtreg/select.ts b/scripts/jtreg/select.ts index 37f29d1..d7f741a 100644 --- a/scripts/jtreg/select.ts +++ b/scripts/jtreg/select.ts @@ -2,9 +2,10 @@ // // node scripts/jtreg/select.ts [ | --full] // -// Prints GitHub Actions outputs: `filters`, the test areas to pass to run.ts -// (empty for all of them), and `shards`, a JSON list of shard numbers ([] when -// no test is needed). A changed file this does not know runs every test. +// Prints GitHub Actions outputs: `filters`, the tests to pass to run.ts (empty +// for all of them), and `shards`, a JSON list of shard numbers ([] when no test +// is needed). Tests added to passing.txt run alone; a change to anything the +// tests load runs them all. import { execFileSync } from "node:child_process"; import { readFileSync } from "node:fs"; @@ -13,17 +14,10 @@ import { join } from "node:path"; const root = join(import.meta.dirname, "..", ".."); const passingFile = "test/jtreg/passing.txt"; -// first match wins; null runs no test -const areas: [RegExp, string[] | null][] = [ - [/\.md$|^docs\/|^LICENSE|^renovate\.json$|^\.coderabbit/, null], - [/^src\/browser\/|^src\/(bridge|library|image|http-proxy)\.ts$|^types\/|^scripts\/(build-web|serve)\.ts$/, null], - [/^test\/(?!jtreg\/)/, null], - [/^src\/natives\/zip\.ts$|^src\/inflate\.ts$/, ["java/util/zip", "java/util/jar"]], - [/^src\/natives\/(io|nio)\.ts$|^src\/fs\.ts$/, ["java/io", "java/nio", "java/util/zip", "java/util/jar"]], - [/^src\/natives\/(net|sockets)\.ts$|^src\/(sockets|net-node)\.ts$|^scripts\/tls\//, ["java/net", "java/nio/channels"]], - [/^src\/natives\/(font|java2d)[\w-]*\.ts$/, ["java/awt", "java/beans"]], - [/^src\/natives\/management\.ts$/, ["java/lang/management"]], -]; +// Files the OpenJDK tests never load. Natives are shared across the whole +// class library (file natives serve class loading and most tests), so any other +// change runs every test rather than guessing which areas it reaches. +const untested = /\.md$|^docs\/|^LICENSE|^renovate\.json$|^\.coderabbit|^types\/|^test\/(?!jtreg\/)|^src\/browser\/|^src\/(bridge|library|image|http-proxy)\.ts$|^scripts\/(build-web|serve)\.ts$/; const shardsFor = (n: number) => Array.from({ length: Math.min(4, Math.ceil(n / 400)) }, (_, i) => i + 1); const listed = readFileSync(join(root, passingFile), "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")); @@ -41,9 +35,7 @@ function select(base: string | undefined): string[] | "all" { } continue; } - const hit = areas.find(([re]) => re.test(file)); - if (!hit) return "all"; - for (const f of hit[1] ?? []) filters.add(f); + if (!untested.test(file)) return "all"; } return [...filters]; } From c735e11eaf7f278c4961c6eb001090cf67fe506a Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:20:40 -0600 Subject: [PATCH 3/7] Add one jtreg result check that branch protection can require. --- .github/workflows/jtreg.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/jtreg.yml b/.github/workflows/jtreg.yml index d2e33dd..20221db 100644 --- a/.github/workflows/jtreg.yml +++ b/.github/workflows/jtreg.yml @@ -72,3 +72,14 @@ jobs: read -ra filters <<< "$FILTERS" node scripts/jtreg/run.ts build/ref/jdk25u --only-passing --jobs 4 --timeout 600 \ --shard ${{ matrix.shard }}/${{ strategy.job-total }} "${filters[@]}" + + # one check for branch protection, whatever the number of shards + result: + needs: [plan, run] + if: always() + runs-on: ubuntu-latest + steps: + - env: + PLAN: ${{ needs.plan.result }} + RUN: ${{ needs.run.result }} + run: test "$PLAN" = success && { test "$RUN" = success || test "$RUN" = skipped; } From fb32ec289b09a51fc9973e5082f275301e5935f4 Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Sun, 1 Nov 2026 17:41:11 -0600 Subject: [PATCH 4/7] Run the OpenJDK tests on pull requests only, since master takes only up-to-date ones, and note the no-flaky rule in AGENTS.md. --- .github/workflows/test.yml | 4 ++-- AGENTS.md | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ca8872f..82086f1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -37,7 +37,7 @@ jobs: - name: Node entry on the built dist/ run: node --expose-gc --test test/image.test.ts + # master only takes up-to-date pull requests, so this already ran on the merged code jtreg: + if: github.event_name == 'pull_request' uses: ./.github/workflows/jtreg.yml - with: - full: ${{ github.event_name == 'push' }} diff --git a/AGENTS.md b/AGENTS.md index 5358489..9631b5a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -64,6 +64,10 @@ Node, which is how the tests run. needs inputs first (see docs/development.md) - `JW_JIT_STATS=1` prints what the JIT compiled, fell back on and entered through the run loop; `JW_JIT_DUMP=` prints a method's generated JS +- Pull requests run the OpenJDK tests in `test/jtreg/passing.txt` that their + changes need (`scripts/jtreg/select.ts`); releases run all of them. A test + that fails without a code change is fixed or taken out of `passing.txt`, so + a green pull request stays green on `master` and at release - `project516-review-bot` reviews every PR. Merge only once its review of the head commit has every finding fixed or answered on its thread. When it cannot read the whole diff, a subagent reviews the files it skipped From c79451fc207a7df4de1c7e5bd3b300badad830d8 Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Sun, 1 Nov 2026 18:29:04 -0600 Subject: [PATCH 5/7] Take NoSync and KeyPairGenerator/Failover out of the passing list: they run near or randomly past the CI timeout. --- test/jtreg/passing.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/jtreg/passing.txt b/test/jtreg/passing.txt index 9bd9567..49d9349 100644 --- a/test/jtreg/passing.txt +++ b/test/jtreg/passing.txt @@ -1658,7 +1658,6 @@ java/security/KeyAgreement/NegativeTest.java java/security/KeyFactory/Failover.java java/security/KeyFactory/GenerateRSAPrivateCrtKey.java java/security/KeyFactory/KeyFactoryGetKeySpecForInvalidSpec.java -java/security/KeyPairGenerator/Failover.java java/security/KeyPairGenerator/GenerateKeypair.java java/security/KeyPairGenerator/GenerateRSAKeyPair.java java/security/KeyRep/Serial.java @@ -1711,7 +1710,6 @@ java/security/SecureRandom/GetAlgorithm.java java/security/SecureRandom/GetInstanceTest.java java/security/SecureRandom/MultiThreadTest.java java/security/SecureRandom/NextBytesNull.java -java/security/SecureRandom/NoSync.java java/security/SecureRandom/Serialize.java java/security/SecureRandom/SerializedSeedTest.java java/security/SecureRandom/TestStrong.java From 55636c4eeb20d7445cdca03b5f29ddf434f33786 Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Sun, 1 Nov 2026 18:37:09 -0600 Subject: [PATCH 6/7] Pick OpenJDK tests from the import graph of the VM and the runner instead of a hand list of skipped files. --- scripts/jtreg/select.ts | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/scripts/jtreg/select.ts b/scripts/jtreg/select.ts index d7f741a..1dd67c6 100644 --- a/scripts/jtreg/select.ts +++ b/scripts/jtreg/select.ts @@ -9,15 +9,28 @@ import { execFileSync } from "node:child_process"; import { readFileSync } from "node:fs"; -import { join } from "node:path"; +import { dirname, join } from "node:path"; const root = join(import.meta.dirname, "..", ".."); const passingFile = "test/jtreg/passing.txt"; -// Files the OpenJDK tests never load. Natives are shared across the whole -// class library (file natives serve class loading and most tests), so any other -// change runs every test rather than guessing which areas it reaches. -const untested = /\.md$|^docs\/|^LICENSE|^renovate\.json$|^\.coderabbit|^types\/|^test\/(?!jtreg\/)|^src\/browser\/|^src\/(bridge|library|image|http-proxy)\.ts$|^scripts\/(build-web|serve)\.ts$/; +// TypeScript the tests load: everything src/node.ts (the VM each test runs on), +// the runner and this selection import, followed through their imports. Other .ts files under +// src/ and scripts/ (the page API, the web build) cannot change a test's result. +function loaded(): Set { + const seen = new Set(); + const visit = (file: string) => { + if (seen.has(file)) return; + seen.add(file); + const text = readFileSync(join(root, file), "utf8"); + for (const m of text.matchAll(/(?:from|import)\s*\(?\s*"(\.{1,2}\/[^"]+\.ts)"/g)) visit(join(dirname(file), m[1])); + }; + visit("src/node.ts"); + visit("scripts/jtreg/run.ts"); + visit("scripts/jtreg/select.ts"); + return seen; +} +const notLoaded = /\.md$|^docs\/|^LICENSE|^renovate\.json$|^\.coderabbit|^types\/|^test\/(?!jtreg\/)/; const shardsFor = (n: number) => Array.from({ length: Math.min(4, Math.ceil(n / 400)) }, (_, i) => i + 1); const listed = readFileSync(join(root, passingFile), "utf8").split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("#")); @@ -27,6 +40,7 @@ function select(base: string | undefined): string[] | "all" { if (!base) return "all"; const git = (...args: string[]) => execFileSync("git", args, { cwd: root, encoding: "utf8" }); const filters = new Set(); + let graph: Set | undefined; for (const file of git("diff", "--name-only", "--no-renames", `${base}...HEAD`).split("\n").filter(Boolean)) { if (file === passingFile) { // the tests it adds @@ -35,7 +49,8 @@ function select(base: string | undefined): string[] | "all" { } continue; } - if (!untested.test(file)) return "all"; + const ts = /^(src|scripts)\/.*\.ts$/.test(file); + if (ts ? (graph ??= loaded()).has(file) : !notLoaded.test(file)) return "all"; } return [...filters]; } From b6d2387f8907f923eedf96c39cbe057f1c7b89be Mon Sep 17 00:00:00 2001 From: Project516 <138796702+Project516@users.noreply.github.com> Date: Sun, 1 Nov 2026 18:42:38 -0600 Subject: [PATCH 7/7] Follow single-quoted imports too when picking OpenJDK tests. --- scripts/jtreg/select.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/jtreg/select.ts b/scripts/jtreg/select.ts index 1dd67c6..5d3fdd5 100644 --- a/scripts/jtreg/select.ts +++ b/scripts/jtreg/select.ts @@ -23,7 +23,7 @@ function loaded(): Set { if (seen.has(file)) return; seen.add(file); const text = readFileSync(join(root, file), "utf8"); - for (const m of text.matchAll(/(?:from|import)\s*\(?\s*"(\.{1,2}\/[^"]+\.ts)"/g)) visit(join(dirname(file), m[1])); + for (const m of text.matchAll(/(?:from|import)\s*\(?\s*["'](\.{1,2}\/[^"']+\.ts)["']/g)) visit(join(dirname(file), m[1])); }; visit("src/node.ts"); visit("scripts/jtreg/run.ts");