microsoft/vscode-react-native

Public

mirrored fromhttps://github.com/microsoft/vscode-react-nativeAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
0.6.9

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

test/resources/processExecution/recorder.ts

127lines · modecode

1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for details.
3
4import * as events from "events";
5import * as fs from "fs";
6import * as os from "os";
7import * as path from "path";
8import child_process = require("child_process");
9
10import {ITimedEvent,
11 IEventArguments,
12 Recording,
13 IAndroidDevice,
14 IIOSDevice,
15 ISpawnArguments,
16 ISpawnOptions,
17} from "./recording";
18
19/* We use this class to capture the behavior of a ChildProces running inside of node, so we can store all the
20 visible events and side-effects of that process, and then we can perfectly reproduce them in a test by using
21 the Simulator class.
22
23 Call Recorder.installGlobalRecorder() when your program starts to record the events of all the spawned processes.
24 The recordings will be stored at the OS temporary directory e.g.:
25 Windows: %TEMP%\processExecutionRecording.txt
26 OS X: $TMPDIR/processExecutionRecording.txt
27*/
28export class Recorder {
29 private static originalSpawn: (command: string, args: string[], options: ISpawnOptions) => child_process.ChildProcess;
30
31 private recording: Recording;
32 private previousEventTimestamp: number;
33
34 public static installGlobalRecorder(): void {
35 if (!this.originalSpawn) {
36 this.originalSpawn = child_process.spawn;
37 child_process.spawn = this.recordAndSpawn.bind(this);
38 }
39 }
40
41 public static recordAndSpawn(command: string, args: string[] = [], options: ISpawnOptions = {}): child_process.ChildProcess {
42 const spawnedProcess = this.originalSpawn(command, args, options);
43 new Recorder(spawnedProcess, { command, args, options }).record();
44 return spawnedProcess;
45 }
46
47 constructor(private execution: child_process.ChildProcess, spawnArguments: ISpawnArguments,
48 private filePath = Recorder.defaultFilePath()) {
49 this.initializeRecording(spawnArguments);
50 }
51
52 public record(): void {
53 this.addListenerForRecordingEvent(this.execution.stdout, "stdout", "data", "data", data =>
54 data.toString());
55 this.addListenerForRecordingEvent(this.execution.stderr, "stderr", "data", "data", data =>
56 data.toString());
57 this.addListenerForRecordingEvent(this.execution, "error", "error", "error");
58 this.addListenerForRecordingEvent(this.execution, "exit", "exit", "code");
59 this.execution.on("error", () =>
60 this.store());
61 this.execution.on("exit", () =>
62 this.store());
63 this.previousEventTimestamp = this.now();
64 }
65
66 private initializeRecording(spawnArguments: ISpawnArguments): void {
67 /* The TBD values needs to be filled manually by the recorder, so we know the full context
68 where this recording was made */
69 this.recording = {
70 title: "TBD",
71 arguments: spawnArguments,
72 date: new Date(),
73 configuration: {
74 os: { platform: os.platform(), release: os.release() },
75 android: {
76 sdk: { tools: "TBD", platformTools: "TBD", buildTools: "TBD", repositoryForSupportLibraries: "TBD" },
77 intelHAXMEmulator: "TBD",
78 visualStudioEmulator: "TBD",
79 },
80 reactNative: "TBD",
81 node: "TBD",
82 npm: "TBD",
83 },
84 state: {
85 reactNative: { packager: "TBD" },
86 devices: { android: <IAndroidDevice[]>[], ios: <IIOSDevice[]>[] },
87 },
88 events: <IEventArguments[]>[],
89 };
90 }
91
92 private static defaultFilePath(): string {
93 return path.join(os.tmpdir(), "processExecutionRecording.txt");
94 }
95
96 private now(): number {
97 return new Date().getTime();
98 }
99
100 private addListenerForRecordingEvent(emitter: events.EventEmitter, storedEventName: string, eventToListenName: string,
101 argumentName: string, argumentsConverter: (value: any) => any = value => value): void {
102 emitter.on(eventToListenName, (argument: any) => {
103 const now = this.now();
104 const relativeTimestamp = now - this.previousEventTimestamp;
105 this.previousEventTimestamp = now;
106 this.recording.events.push(this.generateEvent(relativeTimestamp, storedEventName, argumentName, argumentsConverter(argument)));
107 });
108 }
109
110 /* Generate an event based on the parameters e.g.: { "after": 0, "stdout": { "data": ":app:assembleDebug" } } */
111 private generateEvent(relativeTimestamp: number, eventName: string, argumentName: string, argument: any): IEventArguments {
112 const event: ITimedEvent = { after: relativeTimestamp };
113 (<any>event)[eventName] = this.generateEventArguments(argumentName, argument);
114 return <IEventArguments>event;
115 }
116
117 /* Generate the event arguments based on the parameters e.g.: { "data": ":app:assembleDebug" } */
118 private generateEventArguments(argumentName: string, argument: any): IEventArguments {
119 const eventArguments: IEventArguments = <IEventArguments>{};
120 (<any>eventArguments)[argumentName] = argument;
121 return eventArguments;
122 }
123
124 private store(): void {
125 fs.appendFileSync(this.filePath, JSON.stringify(this.recording) + "\n\n\n", "utf8");
126 }
127}