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
33 changes: 0 additions & 33 deletions src/core/driver-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,26 @@ import type { DeviceInput, UnavailableDeviceInput } from "./input.js";
import { type ScreenFeed, ScreenNotSupportedError, type ScreenSettings } from "./screen.js";
import { stableError } from "./stable-error.js";

/** Thrown when no installed driver can serve the requested platform. */
export class NoDriverError extends Error {
constructor(readonly platform: Platform) {
super(`No driver registered for platform: ${platform}`);
this.name = "NoDriverError";
}
}

/** Thrown when no registered driver answers to the requested `simlock <tool>` wrapper. */
export class UnknownPassthroughToolError extends Error {
constructor(readonly tool: string) {
super(`No driver provides a ${tool} passthrough`);
this.name = "UnknownPassthroughToolError";
}
}

/**
* The model names to try for each class, per platform, most preferred first: the operator's own
* list followed by the driver's built-in one (ADR 0015 §4). Built once, by the composition root.
*/
export type ModelPreferences = Readonly<
Partial<Record<Platform, Readonly<Partial<Record<DeviceClass, readonly string[]>>>>>
>;

/** One registered driver's catalog entry, tagged with its platform. */
export interface PlatformCatalog extends DriverCatalogEntry {
readonly platform: Platform;
/** ADR 0015 §4: the model Simlock would create for each class on this host. */
readonly classDefaults: Readonly<Partial<Record<DeviceClass, string>>>;
}

Expand All @@ -50,7 +42,6 @@ export interface ScreenAvailability {
readonly reason?: string;
}

/** Immutable platform-to-driver lookup used by the lease path. */
export class DriverCatalog {
readonly #drivers: ReadonlyMap<Platform, Driver>;
readonly #logger: Logger;
Expand All @@ -74,12 +65,6 @@ export class DriverCatalog {
return driver;
}

/**
* Routes `simlock <tool> <args>` to whichever driver claims that tool name. Routing is
* all this does: which flag scopes the tool, which verbs it refuses, and what its
* environment must carry are decided inside the driver, so a third driver can add a
* wrapper without a line changing here (architecture rules 2 and 3).
*/
async passthrough(
tool: string,
device: VirtualDriverDevice | PhysicalDriverDevice | undefined,
Expand Down Expand Up @@ -139,14 +124,6 @@ export class DriverCatalog {
return this.get(request.platform).resolveSpec(request);
}

/**
* Aggregates catalogs across every registered driver, or just the given
* platform. A platform with no registered driver (its SDK is missing) is
* omitted rather than raising `NoDriverError` — mirrors `discoverDrivers`.
* Across platforms, a driver whose catalog rejects is left out and logged and
* the others are still listed; with one platform named, its rejection is the
* answer.
*/
async listCatalog(platform?: Platform): Promise<readonly PlatformCatalog[]> {
const listed = await Promise.all(
this.select(platform).map(async (driver): Promise<PlatformCatalog | undefined> => {
Expand All @@ -170,12 +147,6 @@ export class DriverCatalog {
return listed.filter((entry) => entry !== undefined);
}

/**
* For each class, the model Simlock would create on this host (ADR 0015 §4): of the names on
* the class's preference list that the entry lists and that are of that class, the first that
* pairs with an installed runtime, else the first of them. A class in which no name counts has
* no entry.
*/
#classDefaults(
platform: Platform,
entry: DriverCatalogEntry,
Expand All @@ -193,10 +164,6 @@ export class DriverCatalog {
return defaults;
}

/**
* Every registered driver, or only the given platform's. A platform with no registered driver
* gives none rather than raising `NoDriverError`: a read across platforms leaves it out.
*/
select(platform?: Platform): readonly Driver[] {
if (platform === undefined) return [...this.#drivers.values()];
const driver = this.#drivers.get(platform);
Expand Down
72 changes: 2 additions & 70 deletions src/core/fake-driver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,45 +87,17 @@ export interface FakeDriverOptions {
readonly removalResidue?: string;
readonly deviceRoot?: string;
readonly estimateMs?: Partial<Record<DriverEstimateOperation, number>>;
/**
* Reclaim estimate for a `full` clean, when a test needs the two clean levels priced apart
* the way a real driver prices them (an Android `snapshot` against a `wipe`). Falls back to
* `estimateMs.reclaim`, so a test that does not care about the split says nothing.
*/
readonly fullCleanReclaimEstimateMs?: number;
/**
* The `DriverDevice.mode` `makeReady` reports for every boot, overriding what it would
* otherwise report -- settable by a test that needs a slim pass that did not happen, or the
* core's `mode` persistence without a real iOS driver.
*/
readonly mode?: DeviceMode | undefined;
/**
* The OS versions this fake can slim. A slim request on one of them resolves to a slim spec,
* and a prepare boot of a slim-spec device reports `"slim"`. Absent or empty: this fake knows
* nothing of modes, like every real driver but iOS, and every request resolves to a full spec.
*/
readonly slimmableOsVersions?: readonly string[];
readonly knownModels?: readonly string[];
/**
* What `listCatalog` reports a model pairs with. A model left out pairs with every available
* version, which is also the default for every model.
*/
readonly modelRuntimes?: Readonly<Record<string, readonly string[]>>;
/** What `listCatalog` reports as other names per model; none unless a test says otherwise. */
readonly modelAliases?: Readonly<Record<string, readonly string[]>>;
/** The built-in preference lists the driver carries, per class; none unless a test says otherwise. */
readonly defaultModels?: Readonly<Partial<Record<DeviceClass, readonly string[]>>>;
/** What `listCatalog` reports as each model's class; empty unless a test says otherwise. */
readonly modelClasses?: Readonly<Record<string, DeviceClass>>;
/**
* What `listCatalog` reports as installed images; the field is absent unless set. A request
* naming an image tag resolves only to a runtime listed here with that tag.
*/
readonly images?: readonly DriverCatalogImage[];
/** What `listCatalog` reports as custom models; the field is absent unless set. */
readonly customModels?: readonly string[];
readonly latencyMs?: Partial<Record<FakeDriverOperation, number>>;
/** What a grant for this driver's devices should carry; empty unless a test says otherwise. */
readonly leaseEnvironment?: Readonly<Record<string, string>>;
/** why: absent by default, so two fakes in one catalog do not both answer to the same tool. */
readonly passthroughTools?: readonly string[];
Expand All @@ -138,11 +110,6 @@ export interface FakeDriverOptions {
context?: PassthroughContext,
) => PassthroughCommand;
readonly platform: Platform;
/**
* What this driver claims to find outside its root for a given device id, keyed by
* `driverDeviceId`. Empty by default: a driver that has never had a pre-root life finds
* nothing, which is what keeps every other test's missing device simply missing.
*/
readonly legacyDevices?: Readonly<Record<string, LegacyDevice>>;
readonly reclaimResult?: "ready" | "shutdown";
readonly reclaimStrategy?: "erase" | "snapshot" | "wipe";
Expand Down Expand Up @@ -207,7 +174,6 @@ export class FakeDriver implements Driver {
readonly #reclaimStrategy: "erase" | "snapshot" | "wipe";
readonly #devices = new Map<string, "provisioned" | "ready" | "shutdown">();
readonly #legacyDevices: Map<string, LegacyDevice>;
/** Bumped on every `makeReady` boot -- mirrors a real driver reassigning a port per boot. */
readonly #bootCounts = new Map<string, number>();
readonly #addressIsDeviceId: boolean;
#managedReality: DriverReality | undefined;
Expand Down Expand Up @@ -263,11 +229,6 @@ export class FakeDriver implements Driver {
return this.#defaultModels ?? {};
}

/**
* Succeeds unless a test fails it through `failOn`. A real driver re-runs the filesystem
* checks here; the fake only has to be refusable, since what `Doctor` does with a refusal
* is the behaviour under test.
*/
async revalidateRoot(): Promise<void> {
await this.#beforeCall("revalidateRoot");
}
Expand All @@ -282,7 +243,6 @@ export class FakeDriver implements Driver {
this.#legacyDevices.delete(device.deviceId);
}

/** Never installs: a version that is not available throws, naming it as the component. */
async resolveSpec(request: ExactDeviceRequest): Promise<VirtualSpec> {
await this.#beforeCall("resolveSpec", request);
this.#assertMatchingPlatform(request.platform);
Expand All @@ -308,7 +268,6 @@ export class FakeDriver implements Driver {
};
}

/** Defaults to the newest available version; a missing one fails, naming it as the component. */
#resolveOsVersion(requested: string | undefined): string {
const osVersion = requested ?? newestVersion(this.#availableOsVersions);
if (osVersion === undefined) {
Expand All @@ -320,10 +279,6 @@ export class FakeDriver implements Driver {
return osVersion;
}

/**
* A version with a scripted image (`images`) of `imageTag`: the requested one, or else the
* newest. None fails naming no component, so a tagged request is never offered a download.
*/
#resolveTaggedOsVersion(requested: string | undefined, imageTag: string): string {
const tagged = new Set(
(this.#images ?? []).filter((image) => image.tag === imageTag).map((image) => image.runtime),
Expand All @@ -335,18 +290,11 @@ export class FakeDriver implements Driver {
return osVersion;
}

/** `"latest"` is not a version, so it is never found here -- only an install run can tell. */
async findComponent(component: string): Promise<InstalledComponent | undefined> {
await this.#beforeCall("findComponent", component);
return this.#installed(component);
}

/**
* Makes `component` available and logs the call. `"latest"` is the newest available version,
* or a version called `latest` when there is none. Scripted through `failOn("installComponent",
* n, error)`, `holdInstalls()` (it then waits for `releaseInstalls()` or its signal) and the
* `installProgress` option.
*/
async installComponent(
component: string,
options: {
Expand All @@ -370,21 +318,11 @@ export class FakeDriver implements Driver {
return { outcome: "installed", receipt, version };
}

/**
* One entry per available version, with the receipt `findComponent` and `installComponent`
* report for it, and the scripted size and foreign devices.
*/
async listComponents(): Promise<readonly DriverComponent[]> {
await this.#beforeCall("listComponents");
return this.#listedComponents();
}

/**
* Removes the available version whose receipt this is, through the same steps every driver
* takes (`removeListedComponent`), reading the same listing `listComponents` answers. Scripted
* through `failOn("removeComponent", n, error)`, `holdRemovals()` (it then waits for
* `releaseRemovals()` or its signal) and the `removalResidue` option.
*/
async removeComponent(
receipt: ComponentReceipt,
options: { readonly signal: AbortSignal },
Expand Down Expand Up @@ -474,12 +412,6 @@ export class FakeDriver implements Driver {
};
}

/**
* Each boot re-reads a fresh address, same as the real drivers -- never the caller's.
* `options` is recorded on the call log (`calls`) so a test can assert what a caller passed.
* A slimming fake (`slimmableOsVersions`) reports `"slim"` for a prepare boot of a slim-spec
* device and nothing on a recover boot, as the iOS driver does; the `mode` option overrides it.
*/
async makeReady(
device: DriverDevice,
options: { readonly purpose: "prepare" | "recover"; readonly mode: DeviceMode },
Expand Down Expand Up @@ -695,7 +627,7 @@ export class FakeDriver implements Driver {
}

/**
* Stages what `listManaged` reports. Everything passed in is reported back, name and
* why: Stages what `listManaged` reports. Everything passed in is reported back, name and
* all: a real driver answers from what sits inside the root it owns, so a double that
* screened entries by prefix would be modelling ownership Simlock stopped inferring
* (safety rule 8).
Expand Down Expand Up @@ -747,7 +679,7 @@ export class FakeDriver implements Driver {
}
}

/** One 30 s read limit plus one 60 s uninstall limit: what the real physical drivers will answer. */
/** why: One 30 s read limit plus one 60 s uninstall limit: what the real physical drivers will answer. */
const PHYSICAL_RECLAIM_ESTIMATE_MS = 90_000;

function preinstalledReceipts(versions: ReadonlySet<string>): Map<string, ComponentReceipt> {
Expand Down
9 changes: 1 addition & 8 deletions src/core/testing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { warmPoolOwner } from "./warm-pool/index.js";
import { SerializedDecision } from "./serialized-decision.js";

/**
* Test-only: fakes and test wiring other modules' tests use. Only a `*.test.ts` file or a
* why: Test-only: fakes and test wiring other modules' tests use. Only a `*.test.ts` file or a
* `test-*.ts` helper may import this file, and `pnpm lint` enforces it. Anything production code
* also uses belongs on `index.ts`.
*/
Expand All @@ -37,20 +37,13 @@ export { FakeDriver, fakeDeviceDataTable } from "./fake-driver.js";
export type { FakeDriverOptions } from "./fake-driver.js";
export { QuarantineCoordinator } from "./quarantine-coordinator.js";

/**
* Test-only: the decision gate and the component installer `createCore` and `createLeasing` are
* built with, wired the way the daemon wires them -- one gate shared by both and the installer, so the
* installer's registry writes are serialized with every other one. Unlimited free disk. A test
* that passes its own `components` gets the gate alone.
*/
export function testComponentWiring<
Components extends Pick<ComponentInstaller, "claimProvision" | "install"> = ComponentInstaller,
>(options: {
readonly clock: Clock;
readonly drivers: readonly Driver[];
readonly eventBus: Pick<EventBus, "emit">;
readonly registry: Pick<Registry, "deleteComponent" | "recordComponent" | "snapshot">;
/** Stands in for the installer, when a test needs to see whether it was reached at all. */
readonly components?: Components | undefined;
}): {
readonly components: Components | ComponentInstaller;
Expand Down
2 changes: 1 addition & 1 deletion src/drivers/android/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,7 @@ function allowedGlobalArity(argument: string): "none" | "value" | undefined {
if (argument === "-d" || argument === "-e") return "none";
if (argument === "-s") return "value";
if (argument === "-t") return "value";
if (argument.startsWith("-t") && argument.length > 2) return "none"; // fused, e.g. `-t123`
if (argument.startsWith("-t") && argument.length > 2) return "none";
return undefined;
}

Expand Down
2 changes: 1 addition & 1 deletion src/gateway/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* `src/gateway` -- the second implementation of the daemon contract (ADR 0005 §32), for a
* why: `src/gateway` -- the second implementation of the daemon contract (ADR 0005 §32), for a
* daemon that owns no devices and fronts the workers connected to it.
*
* Boundaries (§33, enforced by `pnpm lint`): nothing here imports `src/drivers`, and
Expand Down
Loading
Loading