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
119 changes: 92 additions & 27 deletions .fallowrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,28 +32,30 @@
"apple/runner/AgentDeviceRunner/AgentDeviceRunnerUITests.xctestplan",
"scripts/write-xcuitest-cache-metadata.mjs"
],
"ignoreDependencies": ["@theme"],
"ignoreDependencies": [
"@theme"
],
"ignoreExports": [
{
"file": "src/__tests__/test-utils/index.ts",
"exports": ["*"]
"exports": [
"*"
]
},
{
"comment": "Authoritative supported-command list; sole consumer is the conformance oracle (scripts/maestro-conformance, an ignored tooling tree).",
"file": "src/compat/maestro/program-ir-command-parser.ts",
"exports": ["SUPPORTED_MAESTRO_COMMAND_NAMES"]
"exports": [
"SUPPORTED_MAESTRO_COMMAND_NAMES"
]
},
{
"file": "src/__tests__/test-utils/device-fixtures.ts",
"exports": ["LINUX_DEVICE", "ANDROID_TV_DEVICE", "TVOS_SIMULATOR"]
},
{
"file": "src/__tests__/test-utils/mocked-binaries.ts",
"exports": ["withMockedXcrun"]
},
{
"file": "src/daemon/app-log.ts",
"exports": ["readRecentAndroidLogcatForPackage"]
"exports": [
"LINUX_DEVICE",
"ANDROID_TV_DEVICE",
"TVOS_SIMULATOR"
]
},
{
"file": "src/platforms/android/app-lifecycle.ts",
Expand All @@ -66,19 +68,28 @@
},
{
"file": "src/platforms/android/index.ts",
"exports": ["installAndroidInstallablePath"]
"exports": [
"installAndroidInstallablePath"
]
},
{
"file": "src/platforms/apple/core/apps.ts",
"exports": ["listSimulatorApps", "uninstallIosApp"]
"exports": [
"listSimulatorApps",
"uninstallIosApp"
]
},
{
"file": "src/platforms/apple/core/runner/runner-contract.ts",
"exports": ["resolveRunnerEarlyExitHint"]
"exports": [
"resolveRunnerEarlyExitHint"
]
},
{
"file": "src/platforms/apple/core/runner/runner-session.ts",
"exports": ["stopAllIosRunnerSessions"]
"exports": [
"stopAllIosRunnerSessions"
]
},
{
"file": "src/platforms/apple/core/runner/runner-xctestrun.ts",
Expand All @@ -92,48 +103,102 @@
},
{
"file": "src/cloud-webdriver.ts",
"exports": ["CLOUD_WEBDRIVER_PROVIDERS"]
"exports": [
"CLOUD_WEBDRIVER_PROVIDERS"
]
},
{
"file": "src/daemon/handlers/lease.ts",
"exports": ["handleLeaseCommands"]
"exports": [
"handleLeaseCommands"
]
},
{
"file": "src/daemon/handlers/session.ts",
"exports": ["handleSessionCommands"]
"exports": [
"handleSessionCommands"
]
},
{
"file": "src/daemon/handlers/snapshot.ts",
"exports": ["handleSnapshotCommands"]
"exports": [
"handleSnapshotCommands"
]
},
{
"file": "src/daemon/handlers/react-native.ts",
"exports": ["handleReactNativeCommands"]
"exports": [
"handleReactNativeCommands"
]
},
{
"file": "src/daemon/handlers/record-trace.ts",
"exports": ["handleRecordTraceCommands"]
"exports": [
"handleRecordTraceCommands"
]
},
{
"file": "src/daemon/handlers/find.ts",
"exports": ["handleFindCommands"]
"exports": [
"handleFindCommands"
]
},
{
"file": "src/daemon/handlers/interaction.ts",
"exports": ["handleInteractionCommands"]
"exports": [
"handleInteractionCommands"
]
},
{
"comment": "Published package surface (see package.json#exports); consumed by SDK users, not internally.",
"file": "src/sdk/*.ts",
"exports": [
"*"
]
},
{
"comment": "Tool config default exports, loaded by the tool rather than imported.",
"file": "{tsdown.config.ts,website/rspress.config.ts,test/skillgym/skillgym.config.ts,test/skillgym/suites/*.ts}",
"exports": [
"default"
]
},
{
"comment": "Type-level `AssertTrue<...>` registry-totality guards. Exported only so `noUnusedLocals` keeps them alive; a consumer would defeat the point.",
"file": "{src/core/command-descriptor/registry.ts,src/core/interactors/register-builtins.ts,src/core/platform-descriptor/registry.ts,src/daemon/request-platform-providers.ts}",
"exports": [
"CommandOwnerFileClaimsAreComplete",
"BuiltinPluginsCoverAllPlatforms",
"PlatformDescriptorsAreTotal",
"GatedKeysAreResolverKeys"
]
}
],
"usedClassMembers": ["name", "listActiveLeases", "delete", "values", "elapsedMs", "isExpired"],
"usedClassMembers": [
"name",
"listActiveLeases",
"delete",
"values",
"elapsedMs",
"isExpired"
],
"rules": {
"unused-types": "off",
"unused-types": "warn",
"duplicate-exports": "off"
},
"production": {
"dupes": true
},
"publicPackages": ["agent-device"],
"publicPackages": [
"agent-device"
],
"audit": {
"deadCodeBaseline": "fallow-baselines/dead-code.json",
"healthBaseline": "fallow-baselines/health.json"
},
"includeEntryExports": true,
"ignoreExportsUsedInFile": {
"type": true,
"interface": true
}
}
2 changes: 1 addition & 1 deletion scripts/layering/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export function topFolder(file: string): string {
return match ? match[1]! : '(root)';
}

export function targetDagZone(file: string): string {
function targetDagZone(file: string): string {
if (file.startsWith('src/daemon/client/')) return 'daemon-client';
if (file.startsWith('src/daemon/')) return 'daemon-server';
return topFolder(file);
Expand Down
4 changes: 2 additions & 2 deletions scripts/package-apple-runner-source.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const LEGACY_OUTPUT_DIRS = [
const SKIPPED_DIR_NAMES = new Set(['.build', '.swiftpm', 'xcuserdata']);
const SKIPPED_ROOT_FILES = new Set(['README.md', 'RUNNER_PROTOCOL.md']);

export function packageAppleRunnerSource(options = {}) {
function packageAppleRunnerSource(options = {}) {
const root = path.resolve(options.root ?? process.cwd());
const sourceRoot = path.join(root, SOURCE_DIR);
const outputRoot = path.join(root, OUTPUT_DIR);
Expand All @@ -40,7 +40,7 @@ export function packageAppleRunnerSource(options = {}) {
return summary;
}

export function stripRunnerUnitTestBlocks(source, filePath = '<swift source>') {
function stripRunnerUnitTestBlocks(source, filePath = '<swift source>') {
const lines = source.match(/[^\n]*\n|[^\n]+/g) ?? [];
const state = {
output: [],
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/test-utils/android-snapshot-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export function createAndroidSnapshotHelperExecutor(options: {
};
}

export function isAndroidSnapshotHelperCapture(args: readonly string[]): boolean {
function isAndroidSnapshotHelperCapture(args: readonly string[]): boolean {
return args[0] === 'shell' && args[1] === 'am' && args[2] === 'instrument';
}

Expand Down
35 changes: 19 additions & 16 deletions src/__tests__/test-utils/android-ui-hierarchy-fixtures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,22 @@ import type { RawSnapshotNode } from '../../kernel/snapshot.ts';
* tracks depth and parent via its own recursion, never `node.depth`), so they
* are not populated here.
*/
export function rawFixtureToAndroidTree(rawNodes: RawSnapshotNode[]): AndroidUiHierarchy {
function rawFixtureToTreeNode(raw: RawSnapshotNode): AndroidUiHierarchy {
return {
type: raw.type ?? null,
label: raw.label ?? null,
value: raw.value ?? null,
identifier: raw.identifier ?? null,
packageName: raw.bundleId ?? null,
rect: raw.rect,
hittable: raw.hittable,
visibleToUser: raw.visibleToUser,
depth: 0,
children: [],
};
}

function rawFixtureToAndroidTree(rawNodes: RawSnapshotNode[]): AndroidUiHierarchy {
const root: AndroidUiHierarchy = {
type: null,
label: null,
Expand All @@ -33,21 +48,9 @@ export function rawFixtureToAndroidTree(rawNodes: RawSnapshotNode[]): AndroidUiH
depth: -1,
children: [],
};
const byIndex = new Map<number, AndroidUiHierarchy>();
for (const raw of rawNodes) {
byIndex.set(raw.index, {
type: raw.type ?? null,
label: raw.label ?? null,
value: raw.value ?? null,
identifier: raw.identifier ?? null,
packageName: raw.bundleId ?? null,
rect: raw.rect,
hittable: raw.hittable,
visibleToUser: raw.visibleToUser,
depth: 0,
children: [],
});
}
const byIndex = new Map<number, AndroidUiHierarchy>(
rawNodes.map((raw) => [raw.index, rawFixtureToTreeNode(raw)]),
);
for (const raw of rawNodes) {
const node = byIndex.get(raw.index);
if (!node) continue;
Expand Down
6 changes: 0 additions & 6 deletions src/__tests__/test-utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,10 @@ export {

export { makeSnapshotState } from './snapshot-builders.ts';

export {
rawFixtureToAndroidTree,
walkNonRawAndroidFixture,
} from './android-ui-hierarchy-fixtures.ts';

export {
ANDROID_SNAPSHOT_HELPER_FIXTURE_ARTIFACT,
androidSnapshotHelperOutput,
createAndroidSnapshotHelperExecutor,
isAndroidSnapshotHelperCapture,
} from './android-snapshot-helper.ts';

export { makeSessionStore } from './store-factory.ts';
Expand Down
33 changes: 0 additions & 33 deletions src/__tests__/test-utils/mocked-binaries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,39 +37,6 @@ export async function withMockedAdb(
}
}

/**
* Creates a temporary stub `xcrun` binary that logs all args to a file,
* prepends it to PATH, and cleans up after the callback finishes.
*/
export async function withMockedXcrun(
tempPrefix: string,
run: (argsLogPath: string) => Promise<void>,
): Promise<void> {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), tempPrefix));
const xcrunPath = path.join(tmpDir, 'xcrun');
const argsLogPath = path.join(tmpDir, 'args.log');
await fs.writeFile(
xcrunPath,
'#!/bin/sh\nprintf "%s\\n" "$@" >> "$AGENT_DEVICE_TEST_ARGS_FILE"\nexit 0\n',
'utf8',
);
await fs.chmod(xcrunPath, 0o755);

const previousPath = process.env.PATH;
const previousArgsFile = process.env.AGENT_DEVICE_TEST_ARGS_FILE;
process.env.PATH = `${tmpDir}${path.delimiter}${previousPath ?? ''}`;
process.env.AGENT_DEVICE_TEST_ARGS_FILE = argsLogPath;

try {
await run(argsLogPath);
} finally {
process.env.PATH = previousPath;
if (previousArgsFile === undefined) delete process.env.AGENT_DEVICE_TEST_ARGS_FILE;
else process.env.AGENT_DEVICE_TEST_ARGS_FILE = previousArgsFile;
await fs.rm(tmpDir, { recursive: true, force: true });
}
}

/**
* Like {@link withMockedAdb}, but with a caller-provided stub `adb` script so
* tests can shape per-subcommand responses instead of only recording args.
Expand Down
10 changes: 1 addition & 9 deletions src/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,7 @@ import type { TvRemoteButton } from './contracts/tv-remote.ts';
import type { GesturePlan } from './contracts/gesture-plan-types.ts';
import type { RecordingExportQuality } from './core/recording-export-quality.ts';
import type { SnapshotDiagnosticsSummary } from './snapshot-diagnostics.ts';
import type {
SnapshotCaptureAnalysis,
SnapshotCaptureAnnotations,
SnapshotCaptureFreshness,
} from './snapshot-capture-annotations.ts';
import type { SnapshotCaptureAnnotations } from './snapshot-capture-annotations.ts';
import type { ScreenshotResultData } from './utils/screenshot-result.ts';

// The backend's public leaf platform (approach b): backends distinguish iOS from
Expand Down Expand Up @@ -67,10 +63,6 @@ export type BackendSnapshotOptions = SnapshotOptions & {
outPath?: string;
};

export type BackendSnapshotAnalysis = SnapshotCaptureAnalysis;

export type BackendSnapshotFreshness = SnapshotCaptureFreshness;

export type BackendReadTextResult = {
text: string;
};
Expand Down
Loading
Loading