Files
reverse-engineering-anything/tests/boundary/process/jadxHashCleanup.test.ts
T
morluto 6e74704f93 fix(android): resolve hash cleanup through its owner
Bind the typed hash failure to its retained descriptor and resolve cleanup uncertainty only when that exact owner closes. Preserve the original failure through immediate retirement and later shutdown recovery. Controlled real-file regressions cover immediate and persistent close faults; no real JADX engine was launched. Refs #4266, #4274.
2026-10-16 09:06:19 +08:00

85 lines
3.1 KiB
TypeScript

import { expect, it, vi } from "vitest";
import { OwnedFileHandle } from "../../../src/filesystem/OwnedFileHandle.js";
import { AnalysisError } from "../../../src/domain/analysisErrorBase.js";
import {
createJadxProtocolFixture,
verifyJadxFixtureCleanup,
} from "../../fixtures/android/jadx.js";
it("resolves descriptor uncertainty after an immediate owned hash close retry", async () => {
const { service, apk, provider, launches } =
await createJadxProtocolFixture();
const cause = new Error("controlled hash close failure");
const original = OwnedFileHandle.prototype.close;
const owners: OwnedFileHandle[] = [];
const close = vi
.spyOn(OwnedFileHandle.prototype, "close")
.mockImplementation(async function (this: OwnedFileHandle) {
owners.push(this);
if (owners.length === 1) throw cause;
await original.call(this);
});
try {
const result = await service.execute("inspect_android_package", {
path: apk,
});
if (result.ok) throw new Error("Expected the original close failure");
expect(result.error.cleanupIncomplete).toBe(false);
expect(result.error.cause).toBeInstanceOf(AggregateError);
expect((result.error.cause as AggregateError).errors).toEqual(
expect.arrayContaining([expect.objectContaining({ cause })]),
);
expect(owners).toHaveLength(2);
expect(owners[0]).toBe(owners[1]);
expect(owners[0]?.handle.fd).toBe(-1);
expect(launches).toHaveLength(0);
} finally {
close.mockRestore();
await provider.close();
await verifyJadxFixtureCleanup(launches);
}
});
it("keeps hash cleanup live until the exact retained descriptor closes", async () => {
const { service, apk, provider, launches } =
await createJadxProtocolFixture();
const cause = new Error("controlled persistent hash close failure");
const original = OwnedFileHandle.prototype.close;
const owners: OwnedFileHandle[] = [];
let allowClose = false;
const close = vi
.spyOn(OwnedFileHandle.prototype, "close")
.mockImplementation(async function (this: OwnedFileHandle) {
owners.push(this);
if (!allowClose) throw cause;
await original.call(this);
});
try {
const result = await service.execute("inspect_android_package", {
path: apk,
});
if (result.ok) throw new Error("Expected retained descriptor cleanup");
expect(result.error.cleanupIncomplete).toBe(true);
const primary = result.error.cause;
if (!(primary instanceof AnalysisError))
throw new Error("Missing original typed hash failure");
const originalCause = primary.cause;
expect(primary.cleanupIncomplete).toBe(true);
expect(owners).toHaveLength(2);
expect(owners[0]).toBe(owners[1]);
expect(owners[0]?.handle.fd).toBeGreaterThanOrEqual(0);
allowClose = true;
await provider.close();
expect(primary.cleanupIncomplete).toBe(false);
expect(primary.cause).toBe(originalCause);
expect(owners[0]?.handle.fd).toBe(-1);
expect(launches).toHaveLength(0);
} finally {
allowClose = true;
await provider.close();
close.mockRestore();
await verifyJadxFixtureCleanup(launches);
}
});