(
simulatorUuid: string,
bundleId: string,
executor: CommandExecutor,
options?: {
args?: string[];
env?: Record<string, string>;
},
deps?: {
spawner?: ProcessSpawner;
},
)
| 174 | * OSLog (Logger) messages are captured separately via `simctl spawn log stream`. |
| 175 | */ |
| 176 | export async function launchSimulatorAppWithLogging( |
| 177 | simulatorUuid: string, |
| 178 | bundleId: string, |
| 179 | executor: CommandExecutor, |
| 180 | options?: { |
| 181 | args?: string[]; |
| 182 | env?: Record<string, string>; |
| 183 | }, |
| 184 | deps?: { |
| 185 | spawner?: ProcessSpawner; |
| 186 | }, |
| 187 | ): Promise<LaunchWithLoggingResult> { |
| 188 | const validationError = validateLogSubsystem(bundleId); |
| 189 | if (validationError) { |
| 190 | return { success: false, error: validationError }; |
| 191 | } |
| 192 | |
| 193 | const spawner = deps?.spawner ?? spawn; |
| 194 | |
| 195 | const logsDir = resolveSimulatorLogDir(); |
| 196 | const ts = formatLogTimestamp(); |
| 197 | const suffix = shortRandomSuffix(); |
| 198 | let logFilePath = path.join( |
| 199 | logsDir.path, |
| 200 | `${bundleId}_${ts}_ownerpid${process.pid}_${suffix}.log`, |
| 201 | ); |
| 202 | |
| 203 | let fd: number | undefined; |
| 204 | try { |
| 205 | fs.mkdirSync(logsDir.path, { recursive: true }); |
| 206 | scheduleArtifactCreatedSweep(logsDir); |
| 207 | fd = fs.openSync(logFilePath, 'wx'); |
| 208 | |
| 209 | const args = [ |
| 210 | 'simctl', |
| 211 | 'launch', |
| 212 | '--console-pty', |
| 213 | '--terminate-running-process', |
| 214 | simulatorUuid, |
| 215 | bundleId, |
| 216 | ]; |
| 217 | if (options?.args?.length) { |
| 218 | args.push(...options.args); |
| 219 | } |
| 220 | |
| 221 | const spawnOpts: SpawnOptions = { |
| 222 | stdio: ['ignore', fd, fd], |
| 223 | detached: true, |
| 224 | }; |
| 225 | if (options?.env && Object.keys(options.env).length > 0) { |
| 226 | spawnOpts.env = { ...process.env, ...normalizeSimctlChildEnv(options.env) }; |
| 227 | } |
| 228 | |
| 229 | const child = spawner('xcrun', args, spawnOpts); |
| 230 | if (child.pid && Number.isInteger(child.pid)) { |
| 231 | const helperLogFilePath = path.join( |
| 232 | logsDir.path, |
| 233 | `${bundleId}_${ts}_helperpid${child.pid}_ownerpid${process.pid}_${suffix}.log`, |
no test coverage detected