Skip to content

Commit 372a311

Browse files
authored
refactor: pave the way to add recording to measure command (#281)
* chore: fix husky install deprecation * fix(tools): fix videoFixMetadata command * refactor: start to move recorder instantiation in measurer * refactor: move more functions into measurer * refactor: fix some typings * refactor: move all recording logic to measurer
1 parent ac00255 commit 372a311

File tree

7 files changed

+77
-49
lines changed

7 files changed

+77
-49
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
"build": "rm -rf .parcel-cache && yarn clean-dist && tsc --build && yarn workspace @perf-profiler/web-reporter build && yarn workspace @perf-profiler/measure build",
2929
"release": "yarn build && lerna publish",
3030
"test:e2e": "mkdir -p report && npx @perf-profiler/aws-device-farm runTest --apkPath .github/workflows/example.apk --projectName 'Flashlight-Serverless' --reportDestinationPath report --testCommand 'npx ts-node examples/e2e/appium-ci.test.ts' --testFolder .",
31-
"prepare": "husky install"
31+
"prepare": "husky"
3232
},
3333
"homepage": "https://github.com/bamlab/flashlight#readme",
3434
"devDependencies": {

packages/commands/measure/src/server/ServerSocketConnectionApp.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,11 @@ export const ServerSocketConnectionApp = ({ socket, url }: { socket: SocketType;
4343
return;
4444
}
4545

46-
performanceMeasureRef.current = new PerformanceMeasurer(state.bundleId);
46+
performanceMeasureRef.current = new PerformanceMeasurer(state.bundleId, {
47+
recordOptions: {
48+
record: false,
49+
},
50+
});
4751

4852
addNewResult(state.bundleId);
4953
performanceMeasureRef.current?.start(() =>

packages/commands/test/src/PerformanceMeasurer.ts

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,40 @@
11
import { Logger } from "@perf-profiler/logger";
22
import { profiler, waitFor } from "@perf-profiler/profiler";
3+
import { basename, dirname } from "path";
34
import { Trace } from "./Trace";
4-
import { Measure, POLLING_INTERVAL } from "@perf-profiler/types";
5+
import { Measure, POLLING_INTERVAL, TestCaseIterationResult } from "@perf-profiler/types";
56

67
export class PerformanceMeasurer {
78
measures: Measure[] = [];
89
polling?: { stop: () => void };
9-
bundleId: string;
1010
shouldStop = false;
1111
timingTrace?: Trace;
1212

13-
constructor(bundleId: string) {
14-
this.bundleId = bundleId;
15-
}
13+
constructor(
14+
private bundleId: string,
15+
private options: {
16+
recordOptions:
17+
| { record: false }
18+
| {
19+
record: true;
20+
size?: string;
21+
bitRate?: number;
22+
videoPath: string;
23+
};
24+
}
25+
) {}
26+
27+
private recorder = this.options.recordOptions.record
28+
? profiler.getScreenRecorder(basename(this.options.recordOptions.videoPath))
29+
: null;
1630

1731
async start(
1832
onMeasure: (measure: Measure) => void = () => {
1933
// noop by default
2034
}
2135
) {
36+
await this.maybeStartRecording();
37+
2238
this.polling = profiler.pollPerformanceMeasures(this.bundleId, {
2339
onMeasure: (measure) => {
2440
if (this.shouldStop) {
@@ -40,7 +56,7 @@ export class PerformanceMeasurer {
4056
this.polling?.stop();
4157
}
4258

43-
async stop(duration?: number) {
59+
async stop(duration?: number): Promise<TestCaseIterationResult> {
4460
const time = this.timingTrace?.stop();
4561

4662
if (duration) {
@@ -61,10 +77,36 @@ export class PerformanceMeasurer {
6177
// Ensure polling has stopped
6278
this.polling?.stop();
6379

80+
await this.maybeStopRecording();
81+
82+
const startTime = this.timingTrace?.startTime ?? 0;
83+
6484
return {
6585
time: time ?? 0,
66-
startTime: this.timingTrace?.startTime ?? 0,
86+
startTime,
6787
measures: this.measures,
88+
status: "SUCCESS",
89+
videoInfos:
90+
this.options.recordOptions.record && this.recorder
91+
? {
92+
path: this.options.recordOptions.videoPath,
93+
startOffset: Math.floor(startTime - this.recorder.getRecordingStartTime()),
94+
}
95+
: undefined,
6896
};
6997
}
98+
99+
private async maybeStartRecording() {
100+
if (this.options.recordOptions.record && this.recorder) {
101+
const { bitRate, size } = this.options.recordOptions;
102+
await this.recorder.startRecording({ bitRate, size });
103+
}
104+
}
105+
106+
private async maybeStopRecording() {
107+
if (this.options.recordOptions.record && this.recorder) {
108+
await this.recorder.stopRecording();
109+
await this.recorder.pullRecording(dirname(this.options.recordOptions.videoPath));
110+
}
111+
}
70112
}
Lines changed: 8 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
import {
22
AveragedTestCaseResult,
3-
Measure,
43
TestCaseIterationResult,
54
TestCaseIterationStatus,
65
} from "@perf-profiler/types";
76
import { PerformanceMeasurer } from "./PerformanceMeasurer";
8-
import { profiler } from "@perf-profiler/profiler";
9-
import { basename, dirname } from "path";
107

118
export interface TestCase {
129
beforeTest?: () => Promise<void> | void;
@@ -39,11 +36,15 @@ export class SingleIterationTester {
3936
) {}
4037

4138
private currentTestCaseIterationResult: TestCaseIterationResult | undefined = undefined;
42-
private performanceMeasurer: PerformanceMeasurer = new PerformanceMeasurer(this.bundleId);
4339
private videoPath = `${this.options.resultsFileOptions.path.replace(".json", "")}_iteration_${
4440
this.iterationIndex
4541
}_${new Date().getTime()}.mp4`;
46-
private recorder = profiler.getScreenRecorder(basename(this.videoPath));
42+
private performanceMeasurer: PerformanceMeasurer = new PerformanceMeasurer(this.bundleId, {
43+
recordOptions: {
44+
...this.options.recordOptions,
45+
videoPath: this.videoPath,
46+
},
47+
});
4748

4849
public getCurrentTestCaseIterationResult() {
4950
return this.currentTestCaseIterationResult;
@@ -55,62 +56,34 @@ export class SingleIterationTester {
5556
try {
5657
if (beforeTest) await beforeTest();
5758

58-
await this.maybeStartRecording();
59-
this.performanceMeasurer.start();
59+
await this.performanceMeasurer.start();
6060
await run();
6161
const measures = await this.performanceMeasurer.stop(duration);
62-
await this.maybeStopRecording();
6362

6463
if (afterTest) await afterTest();
6564

6665
this.setCurrentTestCaseIterationResult(measures, "SUCCESS");
6766
} catch (error) {
6867
const measures = await this.performanceMeasurer.stop();
69-
await this.maybeStopRecording();
7068
this.setCurrentTestCaseIterationResult(measures, "FAILURE");
7169
this.performanceMeasurer.forceStop();
7270
throw error;
7371
}
7472
}
7573

76-
private async maybeStartRecording() {
77-
if (this.options.recordOptions.record && this.recorder) {
78-
const { bitRate, size } = this.options.recordOptions;
79-
await this.recorder.startRecording({ bitRate, size });
80-
}
81-
}
82-
8374
public setIsRetry(isRetry: boolean) {
8475
if (this.currentTestCaseIterationResult) {
8576
this.currentTestCaseIterationResult.isRetriedIteration = isRetry;
8677
}
8778
}
8879

89-
private async maybeStopRecording() {
90-
if (this.options.recordOptions.record && this.recorder) {
91-
await this.recorder.stopRecording();
92-
await this.recorder.pullRecording(dirname(this.options.resultsFileOptions.path));
93-
}
94-
}
95-
9680
private setCurrentTestCaseIterationResult(
97-
measures: {
98-
time: number;
99-
startTime: number;
100-
measures: Measure[];
101-
},
81+
measures: TestCaseIterationResult,
10282
status: TestCaseIterationStatus
10383
) {
10484
this.currentTestCaseIterationResult = {
10585
...measures,
10686
status,
107-
videoInfos:
108-
this.options.recordOptions.record && this.recorder
109-
? {
110-
path: this.videoPath,
111-
startOffset: Math.floor(measures.startTime - this.recorder.getRecordingStartTime()),
112-
}
113-
: undefined,
11487
};
11588
}
11689
}

packages/commands/test/src/__tests__/PerformanceMeasurer.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ const loggerDebug = jest.spyOn(Logger, "debug");
99
const loggerError = jest.spyOn(Logger, "error");
1010

1111
describe("PerformanceMeasurer", () => {
12-
it("handles c++ errors correctly", () => {
13-
const measurer = new PerformanceMeasurer("com.example");
14-
measurer.start();
12+
it("handles c++ errors correctly", async () => {
13+
const measurer = new PerformanceMeasurer("com.example", {
14+
recordOptions: {
15+
record: false,
16+
},
17+
});
18+
await measurer.start();
1519
emitMeasure(0);
1620
emitMeasure(1);
1721
perfProfilerMock.stderr?.emit("data", "CPP_ERROR_CANNOT_OPEN_FILE /proc/1234/tasks/578/stat");

packages/commands/tools/src/bin.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { program } from "commander";
44
import { processVideoFile } from "@perf-profiler/shell";
55
import { profiler } from "@perf-profiler/profiler";
6+
import fs from "fs";
67

78
const toolsCommand = program.command("tools").description("Utility tools related to Flashlight");
89

@@ -18,6 +19,8 @@ toolsCommand
1819
.description(
1920
"When coming from AWS Device Farm or certain devices, it seems the video from flashlight test is not encoded properly"
2021
)
21-
.action((options) => {
22-
processVideoFile(options.video_file_path, "destinationPath");
22+
.action((videoFilePath) => {
23+
const backupFilePath = `${videoFilePath}.bak`;
24+
fs.cpSync(videoFilePath, backupFilePath);
25+
processVideoFile(backupFilePath, videoFilePath);
2326
});

packages/core/types/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export type TestCaseIterationStatus = "SUCCESS" | "FAILURE";
1919

2020
export interface TestCaseIterationResult {
2121
time: number;
22+
// we probably don't need this but this is added by the PerformanceMeasurer
23+
startTime?: number;
2224
measures: Measure[];
2325
status: TestCaseIterationStatus;
2426
videoInfos?: {

0 commit comments

Comments
 (0)