diff --git a/docs-internal/registry-parity-worklist.md b/docs-internal/registry-parity-worklist.md index 86a297be21..a2385e2101 100644 --- a/docs-internal/registry-parity-worklist.md +++ b/docs-internal/registry-parity-worklist.md @@ -622,6 +622,20 @@ real e2e tests that prove Linux-parity behavior — not smoke tests. Biome is not applicable for this package test path; it reported the file is ignored by config in `2026-07-08T13-29-23-0700-item12-yq-biome-check.log`. Rev: `znlmtymu`. + - **diffutils — DONE.** Added package-local VM e2e coverage for the staged + `diff` command and fixed recursive directory output to print the compared + file pair before hunk output, matching the Linux `diff -r` shape. The suite + proves identical-file exit 0, normal and unified diffs, brief output, + ignore-case/whitespace/blank-line flags, recursive directory comparisons, + and missing-input errors through the packaged WASM command. Proof: + `2026-07-08T13-32-10-0700-item12-diffutils-toolchain-cmd-build.log`; + `2026-07-08T13-34-45-0700-item12-diffutils-package-build-after-recursive-header.log`; + `2026-07-08T13-35-04-0700-item12-diffutils-package-e2e-final.log`; + `2026-07-08T13-35-04-0700-item12-diffutils-check-types-final.log`; + `2026-07-08T13-35-04-0700-item12-diffutils-cargo-fmt-check-final.log`. + Biome is not applicable for this package test path; it reported the file is + ignored by config in + `2026-07-08T13-35-04-0700-item12-diffutils-biome-check.log`. - **jq — DONE.** Added package-local VM e2e coverage for the staged `jq` command and fixed the jaq-backed CLI wrapper to accept Linux-style file operands instead of only stdin. The suite now proves version output, diff --git a/software/diffutils/bin/diff b/software/diffutils/bin/diff index 003d074f99..ccaee62a89 100644 Binary files a/software/diffutils/bin/diff and b/software/diffutils/bin/diff differ diff --git a/software/diffutils/native/crates/diff/src/lib.rs b/software/diffutils/native/crates/diff/src/lib.rs index 04dbde97da..77e385a00e 100644 --- a/software/diffutils/native/crates/diff/src/lib.rs +++ b/software/diffutils/native/crates/diff/src/lib.rs @@ -272,6 +272,14 @@ fn diff_files(path_a: &Path, path_b: &Path, opts: &Options) -> Result { + if (!tempRoot) throw new Error("fixture root not initialized"); + const hostPath = join(tempRoot, path.replace(/^\/+/, "")); + await mkdir(dirname(hostPath), { recursive: true }); + await writeFile(hostPath, contents); +} + +async function createTestVFS(): Promise { + tempRoot = await mkdtemp(join(tmpdir(), "agentos-diff-")); + await writeFixture("/project/a.txt", "alpha\nbeta\ngamma\n"); + await writeFixture("/project/b.txt", "alpha\ndelta\ngamma\n"); + await writeFixture("/project/same.txt", "alpha\nbeta\ngamma\n"); + await writeFixture("/project/ws-a.txt", "Alpha\n\nbeta value\n"); + await writeFixture("/project/ws-b.txt", "alpha\n BETA VALUE\n"); + await writeFixture("/project/left/common.txt", "one\ntwo\nthree\n"); + await writeFixture("/project/right/common.txt", "one\n2\nthree\n"); + await writeFixture("/project/right/extra.txt", "only on right\n"); + return new NodeFileSystem({ root: tempRoot }); +} + +describeIf(hasDiffPackageBinary, "diff command", { timeout: 10_000 }, () => { + let kernel: Kernel | undefined; + + afterEach(async () => { + await kernel?.dispose(); + kernel = undefined; + if (tempRoot) { + await rm(tempRoot, { recursive: true, force: true }); + tempRoot = undefined; + } + }); + + async function mountFixture(): Promise { + const vfs = await createTestVFS(); + kernel = createKernel({ filesystem: vfs }); + await kernel.mount(createWasmVmRuntime({ commandDirs: [DIFF_COMMAND_DIR] })); + } + + async function runDiff(args: string[]) { + if (!kernel) throw new Error("kernel not mounted"); + let stdout = ""; + let stderr = ""; + const proc = kernel.spawn("diff", args, { + onStdout: (chunk) => { + stdout += Buffer.from(chunk).toString("utf8"); + }, + onStderr: (chunk) => { + stderr += Buffer.from(chunk).toString("utf8"); + }, + }); + const exitCode = await proc.wait(); + await new Promise((resolve) => setTimeout(resolve, 0)); + return { stdout, stderr, exitCode }; + } + + it("returns zero for identical files", async () => { + await mountFixture(); + + const result = await runDiff(["/project/a.txt", "/project/same.txt"]); + expect(result.exitCode, result.stderr || result.stdout).toBe(0); + expect(result.stdout).toBe(""); + }); + + it("prints normal diffs for changed files", async () => { + await mountFixture(); + + const result = await runDiff(["/project/a.txt", "/project/b.txt"]); + expect(result.exitCode).toBe(1); + expect(result.stdout).toContain("2c2"); + expect(result.stdout).toContain("< beta"); + expect(result.stdout).toContain("> delta"); + }); + + it("prints unified diffs", async () => { + await mountFixture(); + + const result = await runDiff(["-u", "/project/a.txt", "/project/b.txt"]); + expect(result.exitCode).toBe(1); + expect(result.stdout).toContain("--- /project/a.txt"); + expect(result.stdout).toContain("+++ /project/b.txt"); + expect(result.stdout).toContain("-beta"); + expect(result.stdout).toContain("+delta"); + }); + + it("prints brief output with -q", async () => { + await mountFixture(); + + const result = await runDiff(["-q", "/project/a.txt", "/project/b.txt"]); + expect(result.exitCode).toBe(1); + expect(result.stdout.trim()).toBe( + "Files /project/a.txt and /project/b.txt differ", + ); + }); + + it("honors ignore-case, whitespace, and blank-line flags", async () => { + await mountFixture(); + + const result = await runDiff([ + "-i", + "-w", + "-B", + "/project/ws-a.txt", + "/project/ws-b.txt", + ]); + expect(result.exitCode, result.stderr || result.stdout).toBe(0); + expect(result.stdout).toBe(""); + }); + + it("compares directories recursively", async () => { + await mountFixture(); + + const result = await runDiff(["-r", "/project/left", "/project/right"]); + expect(result.exitCode).toBe(1); + expect(result.stdout).toContain("Only in /project/right: extra.txt"); + expect(result.stdout).toContain("/project/left/common.txt"); + expect(result.stdout).toContain("/project/right/common.txt"); + }); + + it("returns an error for missing inputs", async () => { + await mountFixture(); + + const result = await runDiff(["/project/a.txt", "/project/missing.txt"]); + expect(result.exitCode).toBe(2); + expect(result.stderr).toContain("/project/missing.txt"); + }); +});