diff --git a/patches/@perf-profiler+android+0.13.0+001+pid-changed.patch b/patches/@perf-profiler+android+0.13.0+001+pid-changed.patch new file mode 100644 index 000000000000..d607b1460d79 --- /dev/null +++ b/patches/@perf-profiler+android+0.13.0+001+pid-changed.patch @@ -0,0 +1,45 @@ +diff --git a/node_modules/@perf-profiler/android/dist/src/commands/platforms/UnixProfiler.js b/node_modules/@perf-profiler/android/dist/src/commands/platforms/UnixProfiler.js +index 657c3b0..c97e363 100644 +--- a/node_modules/@perf-profiler/android/dist/src/commands/platforms/UnixProfiler.js ++++ b/node_modules/@perf-profiler/android/dist/src/commands/platforms/UnixProfiler.js +@@ -113,7 +113,7 @@ class UnixProfiler { + } + pollPerformanceMeasures(bundleId, { onMeasure, onStartMeasuring = () => { + // noop by default +- }, }) { ++ }, onPidChanged = () => {}}) { + let initialTime = null; + let previousTime = null; + let cpuMeasuresAggregator = new CpuMeasureAggregator_1.CpuMeasureAggregator(this.getCpuClockTick()); +@@ -170,6 +170,7 @@ class UnixProfiler { + previousTime = timestamp; + }, () => { + logger_1.Logger.warn("Process id has changed, ignoring measures until now"); ++ onPidChanged(); + reset(); + }); + } +diff --git a/node_modules/@perf-profiler/android/src/commands/platforms/UnixProfiler.ts b/node_modules/@perf-profiler/android/src/commands/platforms/UnixProfiler.ts +index be26fe6..0473f78 100644 +--- a/node_modules/@perf-profiler/android/src/commands/platforms/UnixProfiler.ts ++++ b/node_modules/@perf-profiler/android/src/commands/platforms/UnixProfiler.ts +@@ -105,9 +105,11 @@ export abstract class UnixProfiler implements Profiler { + onStartMeasuring = () => { + // noop by default + }, ++ onPidChanged = () => {}, + }: { + onMeasure: (measure: Measure) => void; + onStartMeasuring?: () => void; ++ onPidChanged?: () => void; + } + ) { + let initialTime: number | null = null; +@@ -187,6 +189,7 @@ export abstract class UnixProfiler implements Profiler { + }, + () => { + Logger.warn("Process id has changed, ignoring measures until now"); ++ onPidChanged(); + reset(); + } + ); diff --git a/patches/@perf-profiler+types+0.8.0+001+pid-changed.patch b/patches/@perf-profiler+types+0.8.0+001+pid-changed.patch new file mode 100644 index 000000000000..bb24b9e880cf --- /dev/null +++ b/patches/@perf-profiler+types+0.8.0+001+pid-changed.patch @@ -0,0 +1,12 @@ +diff --git a/node_modules/@perf-profiler/types/dist/index.d.ts b/node_modules/@perf-profiler/types/dist/index.d.ts +index 0d0f55f..ef7f864 100644 +--- a/node_modules/@perf-profiler/types/dist/index.d.ts ++++ b/node_modules/@perf-profiler/types/dist/index.d.ts +@@ -80,6 +80,7 @@ export interface ScreenRecorder { + export interface ProfilerPollingOptions { + onMeasure: (measure: Measure) => void; + onStartMeasuring?: () => void; ++ onPidChanged?: () => void; + } + export interface Profiler { + pollPerformanceMeasures: (bundleId: string, options: ProfilerPollingOptions) => { diff --git a/tests/e2e/server/index.ts b/tests/e2e/server/index.ts index cd1bbeca2ba9..494b8b4644e1 100644 --- a/tests/e2e/server/index.ts +++ b/tests/e2e/server/index.ts @@ -22,12 +22,15 @@ type TestResultListener = (testResult: TestResult) => void; type AddListener = (listener: TListener) => () => void; +type ClearAllListeners = () => void; + type ServerInstance = { setTestConfig: (testConfig: TestConfig) => void; getTestConfig: () => TestConfig; addTestStartedListener: AddListener; addTestResultListener: AddListener; addTestDoneListener: AddListener; + clearAllTestDoneListeners: ClearAllListeners; forceTestCompletion: () => void; setReadyToAcceptTestResults: (isReady: boolean) => void; isReadyToAcceptTestResults: boolean; @@ -70,7 +73,7 @@ const getPostJSONRequestData = (req: IncomingM }); }; -const createListenerState = (): [TListener[], AddListener] => { +const createListenerState = (): [TListener[], AddListener, ClearAllListeners] => { const listeners: TListener[] = []; const addListener = (listener: TListener) => { listeners.push(listener); @@ -81,8 +84,11 @@ const createListenerState = (): [TListener[], AddListener] } }; }; + const clearAllListeners = () => { + listeners.splice(0, listeners.length); + }; - return [listeners, addListener]; + return [listeners, addListener, clearAllListeners]; }; /** @@ -98,7 +104,7 @@ const createListenerState = (): [TListener[], AddListener] const createServerInstance = (): ServerInstance => { const [testStartedListeners, addTestStartedListener] = createListenerState(); const [testResultListeners, addTestResultListener] = createListenerState(); - const [testDoneListeners, addTestDoneListener] = createListenerState(); + const [testDoneListeners, addTestDoneListener, clearAllTestDoneListeners] = createListenerState(); let isReadyToAcceptTestResults = true; const setReadyToAcceptTestResults = (isReady: boolean) => { @@ -229,6 +235,7 @@ const createServerInstance = (): ServerInstance => { addTestStartedListener, addTestResultListener, addTestDoneListener, + clearAllTestDoneListeners, forceTestCompletion, start: () => new Promise((resolve) => { diff --git a/tests/e2e/testRunner.ts b/tests/e2e/testRunner.ts index 3556f311a393..2d78cf154ff1 100644 --- a/tests/e2e/testRunner.ts +++ b/tests/e2e/testRunner.ts @@ -52,7 +52,7 @@ const setConfigPath = (configPathParam: string | undefined) => { if (!configPath?.startsWith('.')) { configPath = `./${configPath}`; } - const customConfig = require(configPath).default; + const customConfig = (require(configPath) as CustomConfig).default; config = Object.assign(defaultConfig, customConfig); }; @@ -150,8 +150,7 @@ const runTests = async (): Promise => { Logger.log('Launching', appPackage); await launchApp('android', appPackage, config.ACTIVITY_PATH, launchArgs); - MeasureUtils.start(appPackage); - await withFailTimeout( + const {promise, resetTimeout} = withFailTimeout( new Promise((resolve) => { const removeListener = server.addTestDoneListener(() => { Logger.success(iterationText); @@ -194,9 +193,22 @@ const runTests = async (): Promise => { removeListener(); resolve(); }); + MeasureUtils.start(appPackage, { + onAttachFailed: async () => { + MeasureUtils.stop(); + resetTimeout(); + removeListener(); + // something went wrong, let's wait a little bit and try again + await sleep(5000); + // simply restart the test + await runTestIteration(appPackage, iterationText, branch, launchArgs); + resolve(); + }, + }); }), iterationText, ); + await promise; Logger.log('Killing', appPackage); await killApp('android', appPackage); @@ -256,6 +268,8 @@ const runTests = async (): Promise => { // We run each test multiple time to average out the results for (let testIteration = 0; testIteration < config.RUNS; testIteration++) { const onError = (e: Error) => { + MeasureUtils.stop(); + server.clearAllTestDoneListeners(); errorCountRef.errorCount += 1; if (testIteration === 0 || errorCountRef.errorCount === errorCountRef.allowedExceptions) { Logger.error("There was an error running the test and we've reached the maximum number of allowed exceptions. Stopping the test run."); diff --git a/tests/e2e/utils/measure.ts b/tests/e2e/utils/measure.ts index 96ac1bb4541e..a8ea98aff3e8 100644 --- a/tests/e2e/utils/measure.ts +++ b/tests/e2e/utils/measure.ts @@ -11,7 +11,7 @@ const POLLING_STOPPED = { }; let polling = POLLING_STOPPED; -const start = (bundleId: string) => { +const start = (bundleId: string, {onAttachFailed}: {onAttachFailed: () => Promise}) => { // clear our measurements results measures = []; @@ -19,6 +19,9 @@ const start = (bundleId: string) => { onMeasure: (measure: Measure) => { measures.push(measure); }, + onPidChanged: () => { + onAttachFailed(); + }, }); }; diff --git a/tests/e2e/utils/withFailTimeout.ts b/tests/e2e/utils/withFailTimeout.ts index bac21c58a169..34d041a72198 100644 --- a/tests/e2e/utils/withFailTimeout.ts +++ b/tests/e2e/utils/withFailTimeout.ts @@ -3,9 +3,13 @@ import CONFIG from '../config'; // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- nullish coalescing doesn't achieve the same result in this case const TIMEOUT = Number(process.env.INTERACTION_TIMEOUT || CONFIG.INTERACTION_TIMEOUT); -const withFailTimeout = (promise: Promise, name: string): Promise => - new Promise((resolve, reject) => { - const timeoutId = setTimeout(() => { +const withFailTimeout = (promise: Promise, name: string): {promise: Promise; resetTimeout: () => void} => { + let timeoutId: NodeJS.Timeout; + const resetTimeout = () => { + clearTimeout(timeoutId); + }; + const race = new Promise((resolve, reject) => { + timeoutId = setTimeout(() => { reject(new Error(`"${name}": Interaction timed out after ${(TIMEOUT / 1000).toFixed(0)}s`)); }, TIMEOUT); @@ -17,8 +21,11 @@ const withFailTimeout = (promise: Promise, name: string): Promise => reject(e); }) .finally(() => { - clearTimeout(timeoutId); + resetTimeout(); }); }); + return {promise: race, resetTimeout}; +}; + export default withFailTimeout;