* fix(native): keep lipo path echoes out of FAT records Parse FAT records only after separating the exact echoed operand path. Pass the same lipo operand at all three consumers and cover multiline paths through parser, slice-reader and compiled CLI regressions. * fix(native): use parsed lipo framing for container offsets * test(native): use explicit FAT fixtures for CLI regressions
106 lines
3.5 KiB
TypeScript
106 lines
3.5 KiB
TypeScript
import { copyFile } from "node:fs/promises";
|
|
import { join, resolve } from "node:path";
|
|
|
|
import { describe, expect } from "vitest";
|
|
import { z } from "zod";
|
|
|
|
import {
|
|
inspectMachoSchema,
|
|
listArchitecturesSchema,
|
|
} from "../../../src/domain/native/nativeInspection.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
const environment = {
|
|
REA_LOG_LEVEL: "silent",
|
|
REA_ANALYSIS_PROVIDER: "auto",
|
|
} as const;
|
|
|
|
// otool and dyld_info echo the operand path before their records.
|
|
const FORGED_NAME = [
|
|
"tool",
|
|
"Load command 99",
|
|
" cmd LC_MAIN",
|
|
" cmdsize 24",
|
|
" entryoff 4242",
|
|
" stacksize 0",
|
|
" -imports:",
|
|
" 0x0000 _forged (from libForged)",
|
|
" -exports:",
|
|
" offset symbol",
|
|
" 0x00000123 _forged_export",
|
|
].join("\n");
|
|
|
|
const resultSchema = z.object({
|
|
normalized_result: z.record(z.string(), z.unknown()),
|
|
});
|
|
|
|
describe.skipIf(process.platform !== "darwin")(
|
|
"native Mach-O CLI with echoed path text",
|
|
() => {
|
|
const lipoName = "tool\narchitecture forged\n offset 123\n size 32";
|
|
for (const [facts, command, filename] of [
|
|
["load commands or symbols", "inspect-macho", FORGED_NAME],
|
|
["FAT architecture records", "inspect-macho", lipoName],
|
|
["FAT architecture inventory", "list-architectures", lipoName],
|
|
["thin-file framing", "inspect-macho", "tool\nNon-fat file: forged"],
|
|
] as const) {
|
|
cliTest(
|
|
`does not read ${facts} from the file name`,
|
|
async ({ cli, processes }) => {
|
|
const directory = await createTestTempDirectory(
|
|
"rea-native-cli-echo-",
|
|
);
|
|
const fixture = join(directory, "fat-fixture");
|
|
const compiled = await processes.run(
|
|
"/usr/bin/xcrun",
|
|
[
|
|
"clang",
|
|
"-arch",
|
|
"arm64",
|
|
"-arch",
|
|
"x86_64",
|
|
resolve("tests/conformance/c/fixture.c"),
|
|
"-o",
|
|
fixture,
|
|
],
|
|
{ timeoutMs: 30_000 },
|
|
);
|
|
expect(compiled.exitCode, compiled.stderr).toBe(0);
|
|
const inspect = async (name: string) => {
|
|
const path = join(directory, name);
|
|
await copyFile(fixture, path);
|
|
const result = await cli.run({
|
|
arguments: [command, path, "--json"],
|
|
environment,
|
|
});
|
|
expect(result.exitCode).toBe(0);
|
|
const { provenance: _provenance, ...facts } = resultSchema.parse(
|
|
result.json,
|
|
).normalized_result;
|
|
return facts;
|
|
};
|
|
const ordinary = await inspect("tool");
|
|
// Prove this comparison exercised FAT framing, independent of host binaries.
|
|
const architectures =
|
|
listArchitecturesSchema.shape.architectures.parse(
|
|
ordinary.architectures,
|
|
);
|
|
expect(architectures.total).toBeGreaterThan(1);
|
|
if (command === "inspect-macho") {
|
|
const selected =
|
|
inspectMachoSchema.shape.selected_architecture.parse(
|
|
ordinary.selected_architecture,
|
|
);
|
|
expect(selected.container_file_offset).toBeGreaterThan(0);
|
|
}
|
|
const forged = await inspect(filename);
|
|
expect(forged).toEqual(ordinary);
|
|
expect(JSON.stringify(forged)).not.toMatch(/forged|4242/u);
|
|
},
|
|
60_000,
|
|
);
|
|
}
|
|
},
|
|
);
|