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
15 changes: 8 additions & 7 deletions tests/browser-match-patterns/check.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import assert from "node:assert/strict";
import {spawn} from "node:child_process";
import {once} from "node:events";
import {mkdir, mkdtemp, rm, writeFile} from "node:fs/promises";
import {mkdir, mkdtemp, writeFile} from "node:fs/promises";
import {createServer} from "node:http";
import {tmpdir} from "node:os";
import {join} from "node:path";
import {createBrowserHarness, createTabFixture} from "../../dist/testing/index.js";
import {removeBrowserTemporaryDirectory} from "./cleanup.mjs";
import {browserSmokeError, inspectBrowser} from "./launcher.mjs";

// Reject unsupported binaries before opening a server, creating a profile or waiting for extension results.
Expand Down Expand Up @@ -107,6 +108,7 @@ async function probe(config) {
let browser;
let browserExit;
let timeout;
let assertions = 0;

try {
server.listen(0, "127.0.0.1");
Expand Down Expand Up @@ -193,7 +195,6 @@ try {
}, 30000);

await finished;
let assertions = 0;

for (const profile of profiles) {
const result = results.get(profile.name);
Expand Down Expand Up @@ -226,10 +227,6 @@ try {
assertions++;
}
}

console.log(
`Real Chromium smoke: ${assertions} harness/browser comparisons passed across ${profiles.length} permission profiles.`
);
} finally {
clearTimeout(timeout);

Expand All @@ -242,5 +239,9 @@ try {

server.closeAllConnections();
await new Promise(resolveClose => server.close(resolveClose));
await rm(temporary, {recursive: true, force: true});
await removeBrowserTemporaryDirectory(temporary);
}

console.log(
`Real Chromium smoke: ${assertions} harness/browser comparisons passed across ${profiles.length} permission profiles; temporary profile removed.`
);
11 changes: 11 additions & 0 deletions tests/browser-match-patterns/cleanup.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import fs from "node:fs/promises";

export const removeBrowserTemporaryDirectory = async directory => {
try {
// Chromium can still flush profile files after its main process exits.
// Let Node retry transient filesystem errors, without hiding persistent failures.
await fs.rm(directory, {recursive: true, force: true, maxRetries: 10, retryDelay: 100});
} catch (error) {
throw new Error(`Browser smoke cleanup failed after filesystem retries: ${directory}`, {cause: error});
}
};
59 changes: 59 additions & 0 deletions tests/browser-match-patterns/cleanup.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import fs from "node:fs/promises";
import {tmpdir} from "node:os";
import {join} from "node:path";
import {afterEach, describe, expect, jest, test} from "@jest/globals";
import {removeBrowserTemporaryDirectory} from "./cleanup.mjs";

afterEach(() => jest.restoreAllMocks());

describe("browser smoke cleanup", () => {
test("removes an actual temporary profile with bounded native filesystem retries enabled", async () => {
const directory = await fs.mkdtemp(join(tmpdir(), "browser-smoke-cleanup-test-"));

try {
const profile = join(directory, "profile", "Default");
await fs.mkdir(profile, {recursive: true});
await fs.writeFile(join(profile, "Preferences"), "{}");
const remove = jest.spyOn(fs, "rm");
await removeBrowserTemporaryDirectory(directory);

// ENOTEMPTY retries are implemented by Node. Guard the options at that boundary:
// force alone ignores ENOENT, but leaves maxRetries at zero.
expect(remove).toHaveBeenCalledTimes(1);

expect(remove).toHaveBeenCalledWith(directory, {
recursive: true,
force: true,
maxRetries: 10,
retryDelay: 100,
});

await expect(fs.stat(directory)).rejects.toMatchObject({code: "ENOENT"});
} finally {
jest.restoreAllMocks();
await fs.rm(directory, {recursive: true, force: true});
}
});

test("accepts an already removed temporary directory", async () => {
const directory = await fs.mkdtemp(join(tmpdir(), "browser-smoke-cleanup-test-"));

try {
await removeBrowserTemporaryDirectory(directory);
await expect(removeBrowserTemporaryDirectory(directory)).resolves.toBeUndefined();
} finally {
await fs.rm(directory, {recursive: true, force: true});
}
});

test.each(["ENOTEMPTY", "EBUSY", "EACCES"])("propagates %s after native removal fails", async code => {
const directory = join(tmpdir(), "browser-smoke-cleanup-mocked");
const failure = Object.assign(new Error(`Filesystem failure: ${code}`), {code});
jest.spyOn(fs, "rm").mockRejectedValue(failure);

await expect(removeBrowserTemporaryDirectory(directory)).rejects.toMatchObject({
message: `Browser smoke cleanup failed after filesystem retries: ${directory}`,
cause: failure,
});
});
});
Loading