Add a ProcessManager to HarnessContext to make subprocess (adb/xcrun/…) access testable
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 5/5
- Tiempo estimado
- Más de una semana
- Aptitud para principiantes
- 38/100
- Tipo de issue
- Nueva funcionalidad
- Claridad
- Bastante claro
- Estado de actividad
- Tranquilo
- Stack tecnológico
- node.js, typescript
Línea de trabajo
Empieza leyendo el trabajo dependiente de HarnessContext y AsyncLocalStorage en el issue #165 y, después, inspecciona el spawn wrapper actual y packages/platform-android/src/adb.ts:79. Rastrea los puntos de llamada a subprocesos y los tests existentes de simctl, adb y shared-prefs antes de definir el manager y el fake. Se considera terminado cuando los puntos de llamada usan getProcessManager().spawn, se eliminan el escape hatch de child_process sin abstraer y la exportación independiente de spawn, y los tests migrados usan el fake con forma de Subprocess.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- TypeScript
- Estrellas
- 330
- Forks
- 17
- Merge medio
- 8 d 11 h
- PR fusionados (30 d)
- 2
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de callstackincubator/react-native-harness
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
-
Dificultad 4/5 3-5 días Aptitud para principiantes 68/100
-
enhancement
Dificultad 4/5 3-5 días Aptitud para principiantes 52/100
-
enhancement
Dificultad 4/5 3-5 días Aptitud para principiantes 52/100
Todos los issues de callstackincubator/react-native-harness
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
melgarafael/DeskcommCRM#1812 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 84/100
prisma/prisma-cli#309 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
gregwebs/pi-quota-dispatcher#26 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 74/100
openwatersio/slackwater.xyz#124 ·
Los mantenedores suelen responder en 1 día
-
agent-reported area/browser area/docs documentation good first issue hacktoberfest help wanted P2
Dificultad 1/5 Menos de una hora Aptitud para principiantes 90/100
Los mantenedores suelen responder en 2 días