import { describe, expect, it, onTestFinished, vi } from "vitest"; import { createDeferred } from "../../tests/fixtures/binarySession.js"; import type { Device, DeviceManager, FridaMessage, Process, Script, Session, Signal, } from "frida"; import { jsonObjectSchema } from "../domain/jsonValue.js"; import { FridaScriptLoadCancelledError } from "../application/frida/FridaInstrumentationPort.js"; import { AnalysisCapabilityUnavailableError, AnalysisInputError, AnalysisTimeoutError, } from "../domain/analysisErrorCore.js"; import { FridaInstrumentationManager } from "./FridaInstrumentationManager.js"; import { fridaCaptureLimitations } from "./fridaCaptureLimits.js"; import { AnalysisError } from "../domain/analysisErrorBase.js"; import { projectAnalysisError } from "../domain/analysisErrorProjection.js"; import { FridaInstrumentationService } from "../application/frida/FridaInstrumentationService.js"; const signal = void>() => { let connected: Handler | undefined; const value: Signal & { emit: (...args: Parameters) => void; } = { connect: (handler) => { connected = handler; }, disconnect: (handler) => { if (connected === handler) connected = undefined; }, emit: (...args) => connected?.(...args), }; return value; }; class FakeScript implements Script { readonly message = signal<(message: FridaMessage, data: Buffer | null) => void>(); isDestroyed = false; loadFailure: Error | undefined; unloadFailureCount = 0; unloadCalls = 0; constructor( readonly payload: unknown = { event: "loaded" }, readonly beforeLoad: () => Promise = async () => {}, readonly description = "script loaded", readonly stack = "", readonly messageValue?: FridaMessage, ) {} async load(): Promise { await this.beforeLoad(); if (this.loadFailure !== undefined) throw this.loadFailure; this.message.emit( this.messageValue ?? { type: "send", payload: this.payload, description: this.description, stack: this.stack, }, null, ); } async unload(): Promise { this.unloadCalls += 1; if (this.unloadFailureCount > 0) { this.unloadFailureCount -= 1; throw new Error("script unload failed"); } if (this.isDestroyed) throw new Error("Script is destroyed"); this.isDestroyed = true; } } class FakeSession implements Session { readonly detached = signal<() => void>(); readonly scripts: FakeScript[] = []; nextPayload: unknown = { event: "loaded" }; nextDescription = "script loaded"; nextStack = ""; nextMessage: FridaMessage | undefined; nextLoadFailure: Error | undefined; nextUnloadFailureCount = 0; detachFailureCount = 0; scriptLoadGate: Promise | undefined; onScriptLoadStarted: (() => void) | undefined; private detachedState = false; readonly pid = 512; isDetached(): boolean { return this.detachedState; } async createScript(): Promise