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
9 changes: 8 additions & 1 deletion apps/sandbox-image/src/builds.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
Expand All @@ -30,8 +31,9 @@ import {
setupScriptRunIfDue,
} from "@cloudpid/sandbox-image/toolchains";

/** Processes a test started, killed after it whatever became of it. */
/** Processes a test started, killed after it whatever became of it; then its directories are removed. */
const started: number[] = [];
const roots: string[] = [];

afterEach(() => {
for (const pid of started.splice(0)) {
Expand All @@ -41,11 +43,16 @@ afterEach(() => {
// Gone already.
}
}

for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/** A scratch root, a Setup Script state, a fake `mise`, and bare remotes `app` and `api` whose default branch is `main`. */
const container = () => {
const base = mkdtempSync(join(tmpdir(), "builds-"));

roots.push(base);

const root = join(base, "build");
const state = join(base, "state");
const home = join(base, "home");
Expand Down
10 changes: 9 additions & 1 deletion apps/sandbox-image/src/changes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { spawnSync } from "node:child_process";
import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import { hasGnuCoreutils } from "./host.fixture.ts";
import {
changesScript,
Expand All @@ -23,11 +23,19 @@ const env = {
GIT_CONFIG_NOSYSTEM: "1",
};

const roots: string[] = [];

afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/** A Workspace root with repo `app`, whose files are committed on `main`. */
const workspace = (files: Readonly<Record<string, string | Uint8Array>>) => {
const root = mkdtempSync(join(tmpdir(), "changes-"));
const dir = join(root, "app");

roots.push(root);

const sh = (script: string, cwd: string = root) => {
const result = spawnSync("bash", ["-c", script], { cwd, encoding: "utf8", env });

Expand Down
20 changes: 18 additions & 2 deletions apps/sandbox-image/src/comparison.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,18 @@
// clones of it in a throwaway root.
// @effect-diagnostics nodeBuiltinImport:off preferEffectArray:off preferEffectRecord:off processEnv:off
import { spawnSync } from "node:child_process";
import { existsSync, mkdirSync, mkdtempSync, readFileSync, statSync, writeFileSync } from "node:fs";
import {
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
statSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import { hasGnuCoreutils } from "./host.fixture.ts";
import {
blobsScript,
Expand All @@ -27,6 +35,12 @@ const env = {
const lines = (count: number, prefix = "line") =>
Array.from({ length: count }, (_, index) => `${prefix} ${index}\n`).join("");

const roots: string[] = [];

afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/**
* A Workspace root with a bare remote `origin.git` (main: one commit) and repo `app`, a clone of it on `main`. Other
* clones push to the remote from `other`.
Expand All @@ -35,6 +49,8 @@ const workspace = () => {
const root = mkdtempSync(join(tmpdir(), "comparison-"));
const dir = join(root, "app");

roots.push(root);

const sh = (script: string, cwd: string = root) => {
const result = spawnSync("bash", ["-c", script], { cwd, encoding: "utf8", env });

Expand Down
12 changes: 10 additions & 2 deletions apps/sandbox-image/src/docker.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,30 @@
// real, under bash, around a fake `dockerd` and `docker`.
// @effect-diagnostics nodeBuiltinImport:off preferEffectArray:off processEnv:off asyncFunction:off newPromise:off
import { spawn } from "node:child_process";
import { chmodSync, existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { chmodSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import { hasFlock, hasSetsid } from "./host.fixture.ts";
import { sandboxImageContext } from "@cloudpid/sandbox-image";

const helper = join(sandboxImageContext, "cloudpid-dockerd");

const roots: string[] = [];

afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/**
* A directory with a fake `dockerd`, which records its arguments and `DOCKER_ALLOW_IPV6_ON_IPV4_INTERFACE`, then
* answers (`ready`) after `startSeconds` and stays up, and a fake `docker`, whose `info` succeeds once it answers.
*/
const fakes = (startSeconds: number) => {
const dir = mkdtempSync(join(tmpdir(), "cloudpid-dockerd-"));

roots.push(dir);

writeFileSync(
join(dir, "dockerd"),
`#!/usr/bin/env bash
Expand Down
20 changes: 17 additions & 3 deletions apps/sandbox-image/src/memory-guard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
mkdtempSync,
readdirSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
Expand All @@ -27,7 +28,18 @@ import {
const guardScript = join(sandboxImageContext, "cloudpid-memory-guard");
const commandBash = join(sandboxImageContext, "command.bash");

const notesDirectory = () => join(mkdtempSync(join(tmpdir(), "memory-notes-")), "notes");
const roots: string[] = [];

/** A new directory under the system's temporary one, removed after the test, once its processes are stopped. */
const temporaryDirectory = (prefix: string) => {
const dir = mkdtempSync(join(tmpdir(), prefix));

roots.push(dir);

return dir;
};

const notesDirectory = () => join(temporaryDirectory("memory-notes-"), "notes");

/** What earlyoom 1.7 prints just before it sends a process the signal. */
const sending = (signal: string, pid: number, name: string, mib: number) =>
Expand All @@ -38,7 +50,7 @@ const sending = (signal: string, pid: number, name: string, mib: number) =>
* without one), and runs until it is stopped, as earlyoom does.
*/
const fakeEarlyoom = (lines: readonly string[], go?: string) => {
const dir = mkdtempSync(join(tmpdir(), "earlyoom-"));
const dir = temporaryDirectory("earlyoom-");
const path = join(dir, "earlyoom");
const args = join(dir, "args");
const pid = join(dir, "pid");
Expand Down Expand Up @@ -78,6 +90,8 @@ const running: ChildProcess[] = [];

afterEach(() => {
for (const child of running.splice(0)) child.kill("SIGTERM");

for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/** The guard, as the supervisor starts it, with its stdout collected. */
Expand Down Expand Up @@ -318,7 +332,7 @@ describe("a command while the guard stops a process", () => {
// The guard announces the stop while the command runs; the command ends a little after, as one whose process
// was stopped does once the process has died.
const notes = notesDirectory();
const go = join(mkdtempSync(join(tmpdir(), "go-")), "go");
const go = join(temporaryDirectory("go-"), "go");
const earlyoom = fakeEarlyoom([sending("SIGTERM", 99, "python3", 5108)], go);
const guard = startGuard(earlyoom.path, notes);

Expand Down
23 changes: 20 additions & 3 deletions apps/sandbox-image/src/notify.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,33 @@
// FUSE mount was checked by hand in Docker (the doc section).
// @effect-diagnostics nodeBuiltinImport:off processEnv:off preferEffectArray:off
import { spawnSync } from "node:child_process";
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
import { afterAll, afterEach, describe, expect, it } from "vitest";
import { sandboxImageContext } from "@cloudpid/sandbox-image";
import { NOTIFY_ATTRIBUTE, NOTIFY_ROOT_VARIABLE } from "@cloudpid/sandbox-image/notify";

/** The helper's build directory, removed after the file; the roots, after each test. */
const builds: string[] = [];
const roots: string[] = [];

afterAll(() => {
for (const dir of builds.splice(0)) rmSync(dir, { recursive: true, force: true });
});

afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/** The helper built from the image's source, when this machine can build and run it (Linux, with `cc`). */
const built = ((): string | undefined => {
if (process.platform !== "linux") return undefined;

const program = join(mkdtempSync(join(tmpdir(), "cloudpid-notify-")), "cloudpid-notify");
const dir = mkdtempSync(join(tmpdir(), "cloudpid-notify-"));
const program = join(dir, "cloudpid-notify");

builds.push(dir);

const compiled = spawnSync(
"cc",
Expand All @@ -39,6 +54,8 @@ const built = ((): string | undefined => {
const root = () => {
const dir = mkdtempSync(join(tmpdir(), "notify-root-"));

roots.push(dir);

mkdirSync(join(dir, "src"));
mkdirSync(join(dir, "keep"));
writeFileSync(join(dir, "src", "a.js"), "a");
Expand Down
31 changes: 22 additions & 9 deletions apps/sandbox-image/src/supervisor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { createServer, type IncomingMessage } from "node:http";
Expand All @@ -32,11 +33,23 @@ const script = join(sandboxImageContext, "supervise");
type Report = { readonly method: string; readonly path: string; readonly body: string };

const closers: Array<() => void> = [];
const roots: string[] = [];

afterEach(() => {
for (const close of closers.splice(0)) close();

for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/** A new directory under the system's temporary one, removed after the test. */
const temporaryDirectory = (prefix: string) => {
const dir = mkdtempSync(join(tmpdir(), prefix));

roots.push(dir);

return dir;
};

/** A server that records what it is sent, as the Workspace DO's intercept would receive it. */
const reportServer = async () => {
const reports: Report[] = [];
Expand All @@ -63,7 +76,7 @@ const reportServer = async () => {

/** A fake computerd: a bash script doing `behaviour`. */
const fakeComputerd = (behaviour: string) => {
const dir = mkdtempSync(join(tmpdir(), "supervise-"));
const dir = temporaryDirectory("supervise-");
const path = join(dir, "computerd");

writeFileSync(path, `#!/bin/bash\n${behaviour}\n`);
Expand Down Expand Up @@ -96,7 +109,7 @@ type SuperviseOptions = {
const supervise = (computerd: string, port: number, options: SuperviseOptions = {}) =>
new Promise<Run>((resolve) => {
const { signal, env = {}, gate } = options;
const opened = join(mkdtempSync(join(tmpdir(), "gate-")), "opened");
const opened = join(temporaryDirectory("gate-"), "opened");

const child = spawn("bash", [script], {
env: {
Expand All @@ -107,8 +120,8 @@ const supervise = (computerd: string, port: number, options: SuperviseOptions =
// No memory guard unless a test gives one, whatever the machine has installed.
CLOUDPID_MEMORY_GUARD: "/nonexistent/cloudpid-memory-guard",
CLOUDPID_EARLYOOM: "/nonexistent/earlyoom",
CLOUDPID_MEMORY_NOTES: join(mkdtempSync(join(tmpdir(), "memory-notes-")), "notes"),
CLOUDPID_START_MARKER: join(mkdtempSync(join(tmpdir(), "start-marker-")), "started"),
CLOUDPID_MEMORY_NOTES: join(temporaryDirectory("memory-notes-"), "notes"),
CLOUDPID_START_MARKER: join(temporaryDirectory("start-marker-"), "started"),
TEST_GATE: opened,
TEST_GATE_LINE: gate?.trim() ?? "",
...env,
Expand Down Expand Up @@ -233,7 +246,7 @@ describe("the supervisor", () => {

/** A computerd store, with SQLite's write-ahead log and shared memory files beside it, and a computerd that lists them. */
const storeOnDisk = () => {
const dir = mkdtempSync(join(tmpdir(), "computerd-store-"));
const dir = temporaryDirectory("computerd-store-");
const db = join(dir, "state.db");

for (const file of [db, `${db}-wal`, `${db}-shm`]) writeFileSync(file, "sync state");
Expand Down Expand Up @@ -269,7 +282,7 @@ describe("the supervisor", () => {

it("makes the Container's start marker anew before computerd starts, whatever a snapshot kept of it", async () => {
const { port } = await reportServer();
const dir = mkdtempSync(join(tmpdir(), "start-marker-"));
const dir = temporaryDirectory("start-marker-");
const marker = join(dir, "started");

// A snapshot's marker: an older start's, with a content and times from long ago.
Expand Down Expand Up @@ -304,7 +317,7 @@ describe("the supervisor", () => {

/** A fake earlyoom: it records its arguments and its pid, says it is up, and runs until it is stopped, as earlyoom does. */
const fakeEarlyoom = () => {
const dir = mkdtempSync(join(tmpdir(), "earlyoom-"));
const dir = temporaryDirectory("earlyoom-");
const path = join(dir, "earlyoom");
const args = join(dir, "args");
const pid = join(dir, "pid");
Expand Down Expand Up @@ -390,7 +403,7 @@ describe("the supervisor's memory guard", () => {

it("clears the notes a snapshot kept before computerd starts: they are not this Container start's", async () => {
const { port } = await reportServer();
const notes = join(mkdtempSync(join(tmpdir(), "memory-notes-")), "notes");
const notes = join(temporaryDirectory("memory-notes-"), "notes");

mkdirSync(notes);
writeFileSync(join(notes, "1700000000000000"), "an old note");
Expand All @@ -413,7 +426,7 @@ describe("the supervisor's memory guard", () => {
* `launchEnv` is a launch's: the bridge with a token.
*/
const fakeBridgeNode = () => {
const dir = mkdtempSync(join(tmpdir(), "bridge-"));
const dir = temporaryDirectory("bridge-");
const node = join(dir, "node");
const record = join(dir, "record");
const pid = join(dir, "pid");
Expand Down
14 changes: 13 additions & 1 deletion apps/sandbox-image/src/toolchains.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
} from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { describe, expect, it } from "vitest";
import { afterEach, describe, expect, it } from "vitest";
import { hasSetsid } from "./host.fixture.ts";
import { variableNameProblem } from "@cloudpid/config";
import { sandboxImageContext } from "@cloudpid/sandbox-image";
Expand Down Expand Up @@ -102,9 +102,18 @@ describe("a command's environment", () => {
});
});

const roots: string[] = [];

afterEach(() => {
for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true });
});

/** A throwaway Workspace root and HOME, and a `mise` on PATH that logs its calls and the directory they ran in. */
const sandbox = (options: { readonly miseExit?: number; readonly installs?: string } = {}) => {
const base = mkdtempSync(join(tmpdir(), "bootstrap-"));

roots.push(base);

const root = join(base, "workspace");
const home = join(base, "home");
const bin = join(base, "bin");
Expand Down Expand Up @@ -750,6 +759,9 @@ describe("git after the bootstrap", () => {
/** A Workspace root with repos, a Container state directory, and the two Setup Script commands run for real. */
const setupSandbox = () => {
const base = mkdtempSync(join(tmpdir(), "setup-"));

roots.push(base);

const root = join(base, "workspace");
const state = join(base, "state");

Expand Down
Loading
Loading