runTest
uses node:fs
Runs tcm test.
import { slotWaits, waitTime } from "#codebase/tcmts/cli/budget"; import { resolveBuild } from "#codebase/tcmts/cli/builds"; import { routeTest } from "#codebase/tcmts/cli/commands"; import { execute } from "#codebase/tcmts/cli/main"; import type { Context } from "#codebase/tcmts/cli/run"; import { bold, dim } from "#codebase/tcmts/cli/style"; import { runSuites, suitesIn } from "#codebase/tcmts/cli/suites"; import { isTestPath, printOutcomes, runCached, summary } from "#codebase/tcmts/cli/testing"; import { UsageError } from "#codebase/tcmts/cli/ui"; import { parse } from "@bomb.sh/router"; import fs from "node:fs"; import path_1 from "node:path"; export async function runTest({ cb, cmd, rest, cwd, kept }: Context): Promise<number> { const { model } = execute(cmd, parse(routeTest(), { argv: rest })); if (model.definitions && model.suites) throw new UsageError( "--definitions and --suites leave each other nothing to run: give one, or neither for both", ); // the tests named, or under the namespaces named (all of them, with none) const wanted = model.names.length ? model.names : [""]; const ns = wanted.join(", "); const under = (p: string) => wanted.some((w) => !w || p === w || p.startsWith(w + ".")); // test definitions a package runs its command with are run as programs of their own, with the suites const declared = [ ...new Map(wanted.flatMap((w) => suitesIn(cb, w)).map((s) => [s.path, s])).values(), ]; const commandTests = new Set(suitesIn(cb, "").map((s) => s.path)); const names = model.suites ? [] : cb.store .names() .filter( (n) => n.kind !== "type" && isTestPath(n.path) && !commandTests.has(n.path) && under(n.path), ); const suites = model.definitions ? [] : declared; if (!names.length && !suites.length) { console.log( dim( model.suites ? `no suites${ns ? ` under ${ns}` : ""} (a package's manifest declares them: suites: { <command>: [<namespaces>] })` : `no tests${ns ? ` under ${ns}` : ""} (tests are definitions named test… or under a \`tests\` namespace)`, ), ); return 0; } let ok = true; if (names.length) { const outcomes = runCached( cb, names.map((n) => ({ path: n.path, rec: cb.store.getDef(n.hash)! })), cb.source(), model.fresh, ); printOutcomes(outcomes); console.log(`\n${summary(outcomes)}`); ok = outcomes.every((o) => o.result.ok); } // suites: test files running a package's command, as a program if (suites.length) { console.log( `${names.length ? "\n" : ""}${bold("suites")} ${dim(`(running ${[...new Set(suites.map((s) => s.bin))].join(", ")})`)}`, ); // each as it finishes: a run can take minutes const builds = new Map( (model.bin ?? []).map((b) => { const at = b.indexOf("="); if (at < 1) throw new UsageError(`--bin takes cmd=<path>, not '${b}'`); // (tcm's: a kept build's name too, `tcm=previous`) const command = b.slice(0, at); const file = b.slice(at + 1); return [ command, command === "tcm" && !fs.existsSync(path_1.resolve(cwd, file)) ? resolveBuild(file, kept, cwd) : path_1.resolve(cwd, file), ] as const; }), ); // (and how long it waited for the machine's suite slots, if long enough to matter) const waits = slotWaits((l) => console.error(dim(l))); const outcomes = await runSuites( cb, suites, model.fresh, (o) => printOutcomes([o]), builds, waits, ); const waited = waits.waited(); console.log( `\nsuites: ${summary(outcomes)}${waited >= 1000 ? dim(`, ${waitTime(waited)} waiting for slots`) : ""}`, ); ok &&= outcomes.every((o) => o.result.ok); } return ok ? 0 : 1; }
uses
- tcmts.cli.budget.slotWaits
- tcmts.cli.budget.waitTime
- tcmts.cli.builds.resolveBuild
- tcmts.cli.commands.routeTest
- tcmts.cli.main.execute
- tcmts.cli.run.Context
- tcmts.cli.style.bold
- tcmts.cli.style.dim
- tcmts.cli.suites.runSuites
- tcmts.cli.suites.suitesIn
- tcmts.cli.testing.isTestPath
- tcmts.cli.testing.printOutcomes
- tcmts.cli.testing.runCached
- tcmts.cli.testing.summary
- tcmts.cli.ui.UsageError