Add a ProcessManager to HarnessContext to make subprocess (adb/xcrun/…) access testable
还没有人认领这个 Issue。
评估
- 难度
- 5/5
- 预计耗时
- 一周以上
- 新手友好度
- 38/100
- Issue 类型
- 功能
- 描述清晰度
- 基本清楚
- 活跃度
- 冷清
- 技术栈
- node.js, typescript
调研方向
先阅读 issue #165 中依赖的 HarnessContext 和 AsyncLocalStorage 工作,然后检查当前的 spawn wrapper 和 packages/platform-android/src/adb.ts:79。在定义 manager 和 fake 之前,追踪 subprocess 调用点以及现有的 simctl、adb 和 shared-prefs 测试。完成标准是调用点使用 getProcessManager().spawn,移除原始 child_process escape hatch 和独立的 spawn 导出,并且迁移后的测试使用符合 Subprocess 形状的 fake。
由索引模型根据 Issue 内容生成。
描述
Problem Statement
All subprocess execution already funnels through a single spawn() in @react-native-harness/tools (a wrapper over nano-spawn) — with one exception: a raw node:child_process call at packages/platform-android/src/adb.ts:79.
The heaviest, most side-effecting host code shells out to external tools: adb (~21 call sites), xcrun simctl/devicectl (15+), xcodebuild, kepler, lipo/plutil/uname, and bash/unzip. Today it is tested with vi.spyOn(tools, 'spawn'), asserting on exact argv arrays and returning hand-cast fake result shapes. That approach:
- is module-global monkeypatching, so it is not parallel-safe;
- is brittle — it couples tests to exact CLI flag formatting;
- makes the gnarliest, most externally-dependent code the hardest to test.
Solution
Extend the HarnessContext introduced in the companion FileSystem issue (#165) with a process manager, injected via the same AsyncLocalStorage, and exposed through an explicit getProcessManager() accessor that mirrors getFs().
The process manager owns spawn as a method. Subprocess call sites move from the bare spawn(...) to getProcessManager().spawn(...):
// before
await spawn('xcrun', ['simctl', 'boot', udid]);
// after
await getProcessManager().spawn('xcrun', ['simctl', 'boot', udid]);
This is a deliberate design choice over a "magic" spawn() that silently reads the ambient context. Consistency with getFs() matters: the dependency-injection origin should be visible at the call site. A plain-looking spawn(...) that secretly resolves an ambient runner hides where the implementation comes from; getProcessManager().spawn(...) makes it obvious. We accept a mechanical spawn(...) → getProcessManager().spawn(...) sweep across the subprocess-using modules as the price of that clarity. Crucially, this still avoids threading a dependency object through every function signature — the accessor reads from AsyncLocalStorage internally — so the core low-churn benefit of the context is preserved.
Consequences:
- The standalone
spawn()export intoolsis removed in favour ofgetProcessManager().spawn; the current wrapper behavior (default options, logging) moves onto the real process-manager implementation. - The raw
node:child_processcall atadb.ts:79is routed through the manager, closing the last escape hatch. - The default-provider fallback (a real
nano-spawn-backed manager when no context is active) keeps production and external consumers working outside a wrapped context. - Tests set a fake process manager via
runWithHarnessContext, replacingvi.spyOn(tools, 'spawn')with a parallel-safe, per-run mechanism.
Fake design (independent of the DI mechanism):
- The default fake is an argv-programmable process runner: match on command + args, return canned stdout/stderr/exit code. This alone is a large improvement over the status quo and covers the vast majority of cases.
- The fake must return a value matching
nano-spawn'sSubprocessshape — a thenable that also supports streaming / async iteration and carries a handle to the child — so streaming callers such asstreamLogsand device-log tails behave correctly. This fidelity cost exists under any DI mechanism. - If a specific suite later needs rich device-state simulation (installed apps, boot state, shell properties), a stateful process-runner fake that interprets commands into an in-memory model can be layered on the same seam. That is an optional escalation, not a blocker. Dedicated domain-level ports (
Adb,Simctl) remain a possible future refinement but are out of scope here.
Expected outcome
HarnessContextcarries aProcessManager, exposed viagetProcessManager(), with a realnano-spawn-backed default.- Subprocess call sites use
getProcessManager().spawn(...); the standalonespawn()export is removed and theadb.ts:79node:child_processescape hatch is closed. - An argv-programmable,
Subprocess-shaped fake is available from the testing-only entry. - Subprocess tests (e.g.
simctl,adb,shared-prefs) are migrated offvi.spyOn(tools, 'spawn')to the fake.
High-level implementation plan
- (Depends on the FileSystem issue (#165)
HarnessContext+AsyncLocalStorageplumbing.) Define theProcessManagertype, addgetProcessManager()with a realnano-spawn-backed default, and move the currentspawn()wrapper behavior onto the real manager. - Migrate subprocess call sites from
spawn(...)togetProcessManager().spawn(...), remove the standalonespawn()export, and route the rawnode:child_processcall atadb.ts:79through the manager. - Build the argv-programmable fake with
Subprocess-shaped return values in the testing-only entry. - Migrate existing spawn-spy tests to the fake.
- Document how to build a stateful process-runner fake for suites that need device-state fidelity.
- 主要语言
- TypeScript
- 星标
- 330
- 派生
- 17
- 平均合并
- 8 天 11 小时
- 30 天内合并 PR
- 2
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
callstackincubator/react-native-harness 的其他 Issue
-
难度 2/5 1-3 小时 新手友好度 74/100
-
难度 2/5 1-3 小时 新手友好度 78/100
-
难度 4/5 3-5 天 新手友好度 68/100
-
enhancement
难度 4/5 3-5 天 新手友好度 52/100
-
enhancement
难度 4/5 3-5 天 新手友好度 52/100
查看 callstackincubator/react-native-harness 的全部 Issue
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 68/100
microsoft/vscode-livepreview#876 ·
维护者通常 1 天内回复
-
needs-triage
难度 1/5 1 小时以内 新手友好度 90/100
JustJarethB/invoicer#54 ·
-
ICP 1.2.0 shows a scheduled task's interval in milliseconds under the label "Interval (In seconds)"未关闭Needs Triage Type/Bug
难度 2/5 1-3 小时 新手友好度 68/100
wso2/product-integrator#2585 ·
维护者通常 1 天内回复
-
check:passed streams:add
难度 2/5 1-3 小时 新手友好度 68/100
维护者通常 1 天内回复
-
design
难度 2/5 1-3 小时 新手友好度 72/100
MTES-MCT/monitor-field#119 ·
维护者通常 1 天内回复