Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/frontdoor/watchdog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const BOUNDS_TO_WAIT_FOR_SILENCE = 4;
const BOUNDS_TO_BLOCK = 3;
/** The wait for the main thread to pause for the tests that do not read its frames: none, so a stall is reported as soon as it is seen. */
const NO_PAUSE_WAIT_MS = 0;
/** The wait for the test that does read them: a guard against a pause that never comes, not an expectation of how long one takes, so a loaded machine cannot make it flaky. */
/** The wait for the test that does read them, and the deadline a sampler is given by the tests that wait for what the sampler itself does (it exits, or it cannot start): a guard against a pause or a sampler that never ends, not an expectation of how long one takes, so a loaded machine cannot make it flaky. Starting a child process is slow enough on a loaded machine to pass any fixed figure, and the watchdog would then report that the sampler did not finish instead of how it ended. The tests of the deadline itself set their own. */
const PAUSE_WAIT_GUARD_MS = 60_000;

/** The processor count of the fixed load readings the thread tests judge by, so the machine running the tests (which may itself be loaded) never decides whether a stall is reported. */
Expand Down Expand Up @@ -315,7 +315,7 @@ describe("startThreadWatchdog", () => {
fs.writeFileSync(path.join(samples, old), "older");
fs.utimesSync(path.join(samples, old), new Date(0), new Date(Number(old.slice(0, 1)) * MS_PER_SECOND));
}
const nativeSample = { command: process.execPath, args: ["-e", "require('node:fs').writeFileSync(process.argv[1], 'sampled ' + process.argv[2] + 's')", "{file}", "{seconds}"], directory: samples, seconds: 1, overheadMs: PROBE_TIMEOUT_MS, keep: 2 };
const nativeSample = { command: process.execPath, args: ["-e", "require('node:fs').writeFileSync(process.argv[1], 'sampled ' + process.argv[2] + 's')", "{file}", "{seconds}"], directory: samples, seconds: 1, overheadMs: PAUSE_WAIT_GUARD_MS, keep: 2 };
const log = await stallWith({ nativeSample });
await vi.waitFor(
() => {
Expand All @@ -340,7 +340,7 @@ describe("startThreadWatchdog", () => {

it("says the sampler wrote nothing when it fails, passing on what it said, rather than naming a file that is not there", async () => {
const samples = path.join(directory, "stall-samples");
const nativeSample = { command: process.execPath, args: ["-e", "process.stderr.write('cannot examine process'); process.exit(1)"], directory: samples, seconds: 1, overheadMs: PROBE_TIMEOUT_MS, keep: 1 };
const nativeSample = { command: process.execPath, args: ["-e", "process.stderr.write('cannot examine process'); process.exit(1)"], directory: samples, seconds: 1, overheadMs: PAUSE_WAIT_GUARD_MS, keep: 1 };
await stallWith({ nativeSample });
await vi.waitFor(
() => {
Expand Down Expand Up @@ -382,7 +382,7 @@ describe("startThreadWatchdog", () => {
});

it("says so when the native sampler cannot be run, and still ends the stall", async () => {
const nativeSample = { command: path.join(directory, "no-such-sampler"), args: [], directory: path.join(directory, "stall-samples"), seconds: 1, overheadMs: PROBE_TIMEOUT_MS, keep: 1 };
const nativeSample = { command: path.join(directory, "no-such-sampler"), args: [], directory: path.join(directory, "stall-samples"), seconds: 1, overheadMs: PAUSE_WAIT_GUARD_MS, keep: 1 };
const log = await stallWith({ nativeSample });
await vi.waitFor(
() => {
Expand Down
19 changes: 12 additions & 7 deletions src/statusline/elements/activityFit.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { describe, expect, it } from "vitest";

import { resolveStatusline, type StatuslineSettings } from "../../config/statusline";
import { resolveStatusline, type ResolvedStatusline, type StatuslineSettings } from "../../config/statusline";
import { visibleLength } from "../ansi";
import { STATUSLINE_LANGUAGES } from "../messages/languages";
import { createPalette } from "../palette";
import { createPalette, type Palette } from "../palette";
import { fakeRenderContext } from "../testing";
import { EMPTY_ACTIVITY_FACTS, type ActivityFacts, type AgentFact, type NamesFact, type ToolCallFact } from "../transcript/activity";
import type { RenderContext, RowOutput } from "./context";
Expand Down Expand Up @@ -71,11 +71,11 @@ function session(seed: number): ActivityFacts {
};
}

function contextOf(activity: ActivityFacts, width: number, settings: StatuslineSettings): RenderContext {
const resolved = resolveStatusline(settings);
/** The context a row draws from, for `activity` at `width`. The resolved settings and the palette are the caller's, built once per settings choice: they depend on neither the session nor the width, and resolving them parses the whole settings schema. */
function contextOf(activity: ActivityFacts, width: number, resolved: ResolvedStatusline, palette: Palette): RenderContext {
return fakeRenderContext({
settings: resolved,
palette: createPalette(resolved.colours, true),
palette,
width,
nowMs: NOW_MS,
transcript: () => ({
Expand Down Expand Up @@ -104,12 +104,15 @@ describe("the activity rows at every width", () => {
for (const language of STATUSLINE_LANGUAGES) {
for (const [choice, settings] of SETTINGS) {
it(`never draw a line wider than the width, in ${language} with ${choice}`, () => {
const resolved = resolveStatusline({ ...settings, language });
const palette = createPalette(resolved.colours, true);
const overflowing: string[] = [];
for (let index = 0; index < SESSIONS; index += 1) {
const activity = session(SEED + index);
for (let width = MIN_WIDTH; width <= MAX_WIDTH; width += 1) {
const context = contextOf(activity, width, resolved, palette);
for (const [name, row] of Object.entries(ROWS)) {
for (const line of linesOf(row(contextOf(activity, width, { ...settings, language })))) {
for (const line of linesOf(row(context))) {
if (visibleLength(line) > width) {
overflowing.push(`${name} session ${String(index)} width ${String(width)}: ${String(visibleLength(line))} columns`);
}
Expand All @@ -123,7 +126,9 @@ describe("the activity rows at every width", () => {
}

it("draw something for the sessions the test generates, so the widths it checks are not all empty rows", () => {
const drawn = Object.values(ROWS).map((row) => Array.from({ length: SESSIONS }, (_, index) => linesOf(row(contextOf(session(SEED + index), MAX_WIDTH, {}))).length).reduce((total, count) => total + count, 0));
const resolved = resolveStatusline({});
const palette = createPalette(resolved.colours, true);
const drawn = Object.values(ROWS).map((row) => Array.from({ length: SESSIONS }, (_, index) => linesOf(row(contextOf(session(SEED + index), MAX_WIDTH, resolved, palette))).length).reduce((total, count) => total + count, 0));
expect(drawn.every((count) => count > 0)).toBe(true);
});
});
Loading