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
5 changes: 5 additions & 0 deletions .changeset/quiet-jj-paths.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hunkdiff": patch
---

Name the repository in every Jujutsu source-read diagnostic. A missing `jj` binary and an unreadable `jj` stream now log the repo path beside the revision and file, as a failed `jj file show` already did.
7 changes: 5 additions & 2 deletions packages/hunk-jj/src/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ export async function readJjFileSource(
});
} catch (error) {
logSourceDiagnostic(
`failed to run Jujutsu while reading source ${spec.commitId}:${spec.path}`,
`failed to run Jujutsu while reading source ${spec.commitId}:${spec.path} in ${spec.repoRoot}`,
error,
);
return null;
Expand Down Expand Up @@ -146,7 +146,10 @@ export async function readJjFileSource(
return tooLarge(error.maxBytes);
}

logSourceDiagnostic(`failed to collect Jujutsu source ${spec.commitId}:${spec.path}`, error);
logSourceDiagnostic(
`failed to collect Jujutsu source ${spec.commitId}:${spec.path} in ${spec.repoRoot}`,
error,
);
return null;
}

Expand Down
16 changes: 12 additions & 4 deletions test/pty/extensions-integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { afterEach, describe, expect, setDefaultTimeout, test } from "bun:test";
import { existsSync, readFileSync, writeFileSync } from "node:fs";
import { existsSync, readFileSync, realpathSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { createPtyHarness, dragMouse, lineIndexOf } from "./harness";
Expand Down Expand Up @@ -43,6 +43,14 @@ afterEach(() => {
harness.cleanup();
});

/**
* The key Hunk records a repo's trust under: its canonical path. A macOS temp dir reads as
* /var/folders/… and resolves to /private/var/folders/….
*/
function trustKey(repoDir: string) {
return realpathSync.native(repoDir);
}

/** Read the persisted repo-trust decisions from one isolated config home. */
function readTrustState(configHome: string): Record<string, string> {
const statePath = join(configHome, "hunk", "state.json");
Expand Down Expand Up @@ -472,7 +480,7 @@ describe("PTY extensions", () => {
expect(reloaded).not.toContain("beta.ts");
expect(reloaded).toContain("alpha.ts");

expect(readTrustState(configHome)[fixture.dir]).toBe("trusted");
expect(readTrustState(configHome)[trustKey(fixture.dir)]).toBe("trusted");
} finally {
session.close();
}
Expand Down Expand Up @@ -531,7 +539,7 @@ describe("PTY extensions", () => {
expect(dismissed).toContain("beta.ts");
expect(dismissed).not.toContain("REPO EXTENSION ACTIVE");

expect(readTrustState(configHome)[fixture.dir]).toBeUndefined();
expect(readTrustState(configHome)[trustKey(fixture.dir)]).toBeUndefined();
} finally {
session.close();
}
Expand Down Expand Up @@ -563,7 +571,7 @@ describe("PTY extensions", () => {
expect(denied).toContain("beta.ts");
expect(denied).not.toContain("REPO EXTENSION ACTIVE");

expect(readTrustState(configHome)[fixture.dir]).toBe("denied");
expect(readTrustState(configHome)[trustKey(fixture.dir)]).toBe("denied");
} finally {
session.close();
}
Expand Down
17 changes: 17 additions & 0 deletions test/pty/lifecycle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@ async function waitForPtyOutput(fd: number, pattern: RegExp, timeoutMs = 20_000)
throw new Error(`Timed out waiting for ${pattern} on the PTY. Saw:\n${text}`);
}

/**
* Keep reading the PTY until it closes. A PTY buffers little output, so once the test stops
* reading, Hunk's teardown writes fill it and block, and Hunk never exits.
*/
function drainPty(fd: number) {
const buffer = Buffer.alloc(64 * 1024);
const next = () => {
read(fd, buffer, 0, buffer.length, null, (error, bytesRead) => {
if (!error && bytesRead > 0) {
next();
}
});
};
next();
}

describe("PTY lifecycle", () => {
test.skipIf(process.platform === "win32")(
"restores a directly launched renderer when SIGTSTP is discarded",
Expand Down Expand Up @@ -292,6 +308,7 @@ describe("PTY lifecycle", () => {

try {
await waitForPtyOutput(master, /this is a very long wrapped line/);
drainPty(master);
process.kill(child.pid!, signal);

await expect(waitForChildExit(child)).resolves.toEqual({ code: 0, signal: null });
Expand Down
Loading