@react-native-harness/platform-android 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adb.d.ts +2 -4
- package/dist/adb.d.ts.map +1 -1
- package/dist/adb.js +2 -3
- package/dist/app-session.d.ts +3 -12
- package/dist/app-session.d.ts.map +1 -1
- package/dist/app-session.js +23 -33
- package/dist/instance.d.ts.map +1 -1
- package/dist/instance.js +2 -4
- package/package.json +4 -4
- package/src/adb.ts +5 -6
- package/src/app-session.ts +14 -31
- package/src/instance.ts +2 -4
- package/dist/__tests__/adb-id.test.d.ts +0 -2
- package/dist/__tests__/adb-id.test.d.ts.map +0 -1
- package/dist/__tests__/adb-id.test.js +0 -41
- package/dist/__tests__/adb-wait.test.d.ts +0 -2
- package/dist/__tests__/adb-wait.test.d.ts.map +0 -1
- package/dist/__tests__/adb-wait.test.js +0 -69
- package/dist/__tests__/adb.test.d.ts +0 -2
- package/dist/__tests__/adb.test.d.ts.map +0 -1
- package/dist/__tests__/adb.test.js +0 -552
- package/dist/__tests__/app-monitor.test.d.ts +0 -2
- package/dist/__tests__/app-monitor.test.d.ts.map +0 -1
- package/dist/__tests__/app-monitor.test.js +0 -257
- package/dist/__tests__/app-session.test.d.ts +0 -2
- package/dist/__tests__/app-session.test.d.ts.map +0 -1
- package/dist/__tests__/app-session.test.js +0 -57
- package/dist/__tests__/avd-config.test.d.ts +0 -2
- package/dist/__tests__/avd-config.test.d.ts.map +0 -1
- package/dist/__tests__/avd-config.test.js +0 -232
- package/dist/__tests__/ci-action.test.d.ts +0 -2
- package/dist/__tests__/ci-action.test.d.ts.map +0 -1
- package/dist/__tests__/ci-action.test.js +0 -46
- package/dist/__tests__/crash-diagnostics.test.d.ts +0 -2
- package/dist/__tests__/crash-diagnostics.test.d.ts.map +0 -1
- package/dist/__tests__/crash-diagnostics.test.js +0 -119
- package/dist/__tests__/crash-parser.test.d.ts +0 -2
- package/dist/__tests__/crash-parser.test.d.ts.map +0 -1
- package/dist/__tests__/crash-parser.test.js +0 -45
- package/dist/__tests__/crash-reporter.test.d.ts +0 -2
- package/dist/__tests__/crash-reporter.test.d.ts.map +0 -1
- package/dist/__tests__/crash-reporter.test.js +0 -150
- package/dist/__tests__/emulator-startup.test.d.ts +0 -2
- package/dist/__tests__/emulator-startup.test.d.ts.map +0 -1
- package/dist/__tests__/emulator-startup.test.js +0 -47
- package/dist/__tests__/environment.test.d.ts +0 -2
- package/dist/__tests__/environment.test.d.ts.map +0 -1
- package/dist/__tests__/environment.test.js +0 -117
- package/dist/__tests__/instance.test.d.ts +0 -2
- package/dist/__tests__/instance.test.d.ts.map +0 -1
- package/dist/__tests__/instance.test.js +0 -599
- package/dist/__tests__/shared-prefs.test.d.ts +0 -2
- package/dist/__tests__/shared-prefs.test.d.ts.map +0 -1
- package/dist/__tests__/shared-prefs.test.js +0 -87
- package/dist/__tests__/targets.test.d.ts +0 -2
- package/dist/__tests__/targets.test.d.ts.map +0 -1
- package/dist/__tests__/targets.test.js +0 -49
- package/dist/app-monitor.d.ts +0 -13
- package/dist/app-monitor.d.ts.map +0 -1
- package/dist/app-monitor.js +0 -376
- package/dist/device-state.d.ts +0 -6
- package/dist/device-state.d.ts.map +0 -1
- package/dist/device-state.js +0 -142
- package/dist/tsconfig.lib.tsbuildinfo +0 -1
- package/src/__tests__/adb-id.test.ts +0 -51
- package/src/__tests__/adb-wait.test.ts +0 -95
- package/src/__tests__/app-session.test.ts +0 -76
- package/src/__tests__/avd-config.test.ts +0 -276
- package/src/__tests__/ci-action.test.ts +0 -81
- package/src/__tests__/crash-diagnostics.test.ts +0 -144
- package/src/__tests__/crash-parser.test.ts +0 -52
- package/src/__tests__/crash-reporter.test.ts +0 -167
- package/src/__tests__/emulator-startup.test.ts +0 -82
- package/src/__tests__/instance.test.ts +0 -844
- package/src/__tests__/shared-prefs.test.ts +0 -144
- package/src/__tests__/targets.test.ts +0 -53
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,257 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { NativeCrashError } from '@react-native-harness/platforms';
|
|
3
|
-
import { createAndroidAppMonitor, createAndroidLogEvent } from '../app-monitor.js';
|
|
4
|
-
import * as adb from '../adb.js';
|
|
5
|
-
const createStreamingSubprocess = (chunks) => ({
|
|
6
|
-
nodeChildProcess: Promise.resolve({
|
|
7
|
-
kill: vi.fn(),
|
|
8
|
-
}),
|
|
9
|
-
[Symbol.asyncIterator]: async function* () {
|
|
10
|
-
for (const { line, delayMs = 0 } of chunks) {
|
|
11
|
-
if (delayMs > 0) {
|
|
12
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
13
|
-
}
|
|
14
|
-
yield line;
|
|
15
|
-
}
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
describe('createAndroidLogEvent', () => {
|
|
19
|
-
it('detects confirmation-grade am_crash events', () => {
|
|
20
|
-
const event = createAndroidLogEvent('05-20 12:00:00.200 1000 1000 I am_crash: [0,1234,com.harnessplayground,123,java.lang.IllegalStateException,boom]', 'com.harnessplayground');
|
|
21
|
-
expect(event).toMatchObject({
|
|
22
|
-
type: 'crash_confirmed',
|
|
23
|
-
crashDetails: {
|
|
24
|
-
platform: 'android',
|
|
25
|
-
kind: 'java-exception',
|
|
26
|
-
confidence: 'high',
|
|
27
|
-
source: 'logs',
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
});
|
|
32
|
-
describe('createAndroidAppMonitor', () => {
|
|
33
|
-
beforeEach(() => {
|
|
34
|
-
vi.restoreAllMocks();
|
|
35
|
-
vi.useFakeTimers();
|
|
36
|
-
vi.spyOn(adb, 'getLogcatTimestamp').mockResolvedValue('05-20 12:00:00.000');
|
|
37
|
-
});
|
|
38
|
-
afterEach(() => {
|
|
39
|
-
vi.useRealTimers();
|
|
40
|
-
});
|
|
41
|
-
it('starts logcat with crash, main, system, and events buffers', async () => {
|
|
42
|
-
const startLogcat = vi
|
|
43
|
-
.spyOn(adb, 'startLogcat')
|
|
44
|
-
.mockReturnValue(createStreamingSubprocess([]));
|
|
45
|
-
const monitor = createAndroidAppMonitor({
|
|
46
|
-
adbId: 'emulator-5554',
|
|
47
|
-
bundleId: 'com.harnessplayground',
|
|
48
|
-
appUid: 10234,
|
|
49
|
-
isAppRunning: async () => true,
|
|
50
|
-
});
|
|
51
|
-
await monitor.start();
|
|
52
|
-
await monitor.stop();
|
|
53
|
-
expect(startLogcat).toHaveBeenCalledWith('emulator-5554', [
|
|
54
|
-
'logcat',
|
|
55
|
-
'-b',
|
|
56
|
-
'crash',
|
|
57
|
-
'-b',
|
|
58
|
-
'main',
|
|
59
|
-
'-b',
|
|
60
|
-
'system',
|
|
61
|
-
'-b',
|
|
62
|
-
'events',
|
|
63
|
-
'-v',
|
|
64
|
-
'threadtime',
|
|
65
|
-
'-T',
|
|
66
|
-
'05-20 12:00:00.000',
|
|
67
|
-
]);
|
|
68
|
-
});
|
|
69
|
-
it('reports app lifecycle and crash events through the reporter', async () => {
|
|
70
|
-
vi.spyOn(adb, 'startLogcat').mockReturnValue(createStreamingSubprocess([
|
|
71
|
-
{
|
|
72
|
-
line: '05-20 12:00:00.000 1234 1234 I ActivityManager: Start proc 1234:com.harnessplayground/u0a123 for activity',
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
line: '05-20 12:00:00.100 1234 1234 E AndroidRuntime: Process: com.harnessplayground, PID: 1234',
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
line: '05-20 12:00:00.200 1000 1000 I am_crash: [0,1234,com.harnessplayground,123,java.lang.IllegalStateException,boom]',
|
|
79
|
-
},
|
|
80
|
-
]));
|
|
81
|
-
const eventReporter = vi.fn();
|
|
82
|
-
const monitor = createAndroidAppMonitor({
|
|
83
|
-
adbId: 'emulator-5554',
|
|
84
|
-
bundleId: 'com.harnessplayground',
|
|
85
|
-
appUid: 10234,
|
|
86
|
-
isAppRunning: async () => true,
|
|
87
|
-
eventReporter,
|
|
88
|
-
});
|
|
89
|
-
const crashWatch = monitor.watch('example.test.ts', 'startup');
|
|
90
|
-
crashWatch.promise.catch(() => undefined);
|
|
91
|
-
monitor.launchRequested({
|
|
92
|
-
type: 'launch_requested',
|
|
93
|
-
launchId: 'launch-1',
|
|
94
|
-
at: Date.now(),
|
|
95
|
-
reason: 'start',
|
|
96
|
-
});
|
|
97
|
-
await monitor.start();
|
|
98
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
99
|
-
expect(eventReporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
100
|
-
type: 'app:started',
|
|
101
|
-
appPlatform: 'android',
|
|
102
|
-
targetIdentifier: 'emulator-5554',
|
|
103
|
-
launchId: 'launch-1',
|
|
104
|
-
}));
|
|
105
|
-
expect(eventReporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
106
|
-
type: 'app:crash-suspected',
|
|
107
|
-
appPlatform: 'android',
|
|
108
|
-
processName: 'com.harnessplayground',
|
|
109
|
-
}));
|
|
110
|
-
expect(eventReporter).toHaveBeenCalledWith(expect.objectContaining({
|
|
111
|
-
type: 'app:crash-confirmed',
|
|
112
|
-
appPlatform: 'android',
|
|
113
|
-
processName: 'com.harnessplayground',
|
|
114
|
-
}));
|
|
115
|
-
await monitor.stop();
|
|
116
|
-
});
|
|
117
|
-
it('rejects the watch for Java crashes when am_crash arrives even if the process still looks alive', async () => {
|
|
118
|
-
vi.spyOn(adb, 'startLogcat').mockReturnValue(createStreamingSubprocess([
|
|
119
|
-
{
|
|
120
|
-
line: '05-20 12:00:00.000 1234 1234 I ActivityManager: Start proc 1234:com.harnessplayground/u0a123 for activity',
|
|
121
|
-
},
|
|
122
|
-
{
|
|
123
|
-
line: '05-20 12:00:00.100 1234 1234 E AndroidRuntime: FATAL EXCEPTION: main',
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
line: '05-20 12:00:00.101 1234 1234 E AndroidRuntime: Process: com.harnessplayground, PID: 1234',
|
|
127
|
-
},
|
|
128
|
-
{
|
|
129
|
-
line: '05-20 12:00:00.102 1234 1234 E AndroidRuntime: java.lang.IllegalStateException: Intentional pre-RN startup crash',
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
line: '05-20 12:00:00.103 1234 1234 E AndroidRuntime: at com.harnessplayground.MainActivity.onCreate(MainActivity.kt:42)',
|
|
133
|
-
},
|
|
134
|
-
{
|
|
135
|
-
line: '05-20 12:00:00.200 1000 1000 I am_crash: [0,1234,com.harnessplayground,123,java.lang.IllegalStateException,Intentional pre-RN startup crash]',
|
|
136
|
-
},
|
|
137
|
-
]));
|
|
138
|
-
const monitor = createAndroidAppMonitor({
|
|
139
|
-
adbId: 'emulator-5554',
|
|
140
|
-
bundleId: 'com.harnessplayground',
|
|
141
|
-
appUid: 10234,
|
|
142
|
-
isAppRunning: async () => true,
|
|
143
|
-
});
|
|
144
|
-
const crashWatch = monitor.watch('example.test.ts', 'startup');
|
|
145
|
-
crashWatch.promise.catch(() => undefined);
|
|
146
|
-
await monitor.start();
|
|
147
|
-
await vi.advanceTimersByTimeAsync(100);
|
|
148
|
-
await expect(crashWatch.promise).rejects.toMatchObject({
|
|
149
|
-
testFilePath: 'example.test.ts',
|
|
150
|
-
details: {
|
|
151
|
-
phase: 'startup',
|
|
152
|
-
platform: 'android',
|
|
153
|
-
kind: 'java-exception',
|
|
154
|
-
confidence: 'high',
|
|
155
|
-
processName: 'com.harnessplayground',
|
|
156
|
-
pid: 1234,
|
|
157
|
-
exceptionType: 'java.lang.IllegalStateException: Intentional pre-RN startup crash',
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
await expect(crashWatch.promise).rejects.toBeInstanceOf(NativeCrashError);
|
|
161
|
-
await monitor.stop();
|
|
162
|
-
});
|
|
163
|
-
it('rejects the watch when a suspected crash is followed by process exit', async () => {
|
|
164
|
-
vi.spyOn(adb, 'startLogcat').mockReturnValue(createStreamingSubprocess([
|
|
165
|
-
{
|
|
166
|
-
line: '05-20 12:00:00.101 1234 1234 E AndroidRuntime: Process: com.harnessplayground, PID: 1234',
|
|
167
|
-
},
|
|
168
|
-
{
|
|
169
|
-
line: '05-20 12:00:00.102 1234 1234 E AndroidRuntime: java.lang.IllegalStateException: Intentional delayed startup crash',
|
|
170
|
-
},
|
|
171
|
-
]));
|
|
172
|
-
const isAppRunning = vi
|
|
173
|
-
.fn()
|
|
174
|
-
.mockResolvedValueOnce(true)
|
|
175
|
-
.mockResolvedValueOnce(false)
|
|
176
|
-
.mockResolvedValue(false);
|
|
177
|
-
const monitor = createAndroidAppMonitor({
|
|
178
|
-
adbId: 'emulator-5554',
|
|
179
|
-
bundleId: 'com.harnessplayground',
|
|
180
|
-
appUid: 10234,
|
|
181
|
-
isAppRunning,
|
|
182
|
-
});
|
|
183
|
-
const crashWatch = monitor.watch('example.test.ts', 'execution');
|
|
184
|
-
crashWatch.promise.catch(() => undefined);
|
|
185
|
-
await monitor.start();
|
|
186
|
-
await vi.advanceTimersByTimeAsync(350);
|
|
187
|
-
await expect(crashWatch.promise).rejects.toMatchObject({
|
|
188
|
-
details: {
|
|
189
|
-
phase: 'execution',
|
|
190
|
-
platform: 'android',
|
|
191
|
-
kind: 'java-exception',
|
|
192
|
-
processName: 'com.harnessplayground',
|
|
193
|
-
pid: 1234,
|
|
194
|
-
},
|
|
195
|
-
});
|
|
196
|
-
await monitor.stop();
|
|
197
|
-
});
|
|
198
|
-
it('does not reject the watch when the process exits without a recent crash signal', async () => {
|
|
199
|
-
vi.spyOn(adb, 'startLogcat').mockReturnValue(createStreamingSubprocess([]));
|
|
200
|
-
const isAppRunning = vi
|
|
201
|
-
.fn()
|
|
202
|
-
.mockResolvedValueOnce(true)
|
|
203
|
-
.mockResolvedValueOnce(false)
|
|
204
|
-
.mockResolvedValue(false);
|
|
205
|
-
const monitor = createAndroidAppMonitor({
|
|
206
|
-
adbId: 'emulator-5554',
|
|
207
|
-
bundleId: 'com.harnessplayground',
|
|
208
|
-
appUid: 10234,
|
|
209
|
-
isAppRunning,
|
|
210
|
-
});
|
|
211
|
-
const crashWatch = monitor.watch('example.test.ts', 'execution');
|
|
212
|
-
let settled = false;
|
|
213
|
-
const watchHandled = crashWatch.promise.catch(() => {
|
|
214
|
-
settled = true;
|
|
215
|
-
});
|
|
216
|
-
await monitor.start();
|
|
217
|
-
await vi.advanceTimersByTimeAsync(600);
|
|
218
|
-
expect(settled).toBe(false);
|
|
219
|
-
crashWatch.cancel();
|
|
220
|
-
await monitor.stop();
|
|
221
|
-
await watchHandled;
|
|
222
|
-
});
|
|
223
|
-
it('suppresses process-exit confirmation during a controlled stop window', async () => {
|
|
224
|
-
vi.spyOn(adb, 'startLogcat').mockReturnValue(createStreamingSubprocess([
|
|
225
|
-
{
|
|
226
|
-
line: '05-20 12:00:00.101 1234 1234 E AndroidRuntime: Process: com.harnessplayground, PID: 1234',
|
|
227
|
-
},
|
|
228
|
-
]));
|
|
229
|
-
const isAppRunning = vi
|
|
230
|
-
.fn()
|
|
231
|
-
.mockResolvedValueOnce(true)
|
|
232
|
-
.mockResolvedValueOnce(false)
|
|
233
|
-
.mockResolvedValue(false);
|
|
234
|
-
const monitor = createAndroidAppMonitor({
|
|
235
|
-
adbId: 'emulator-5554',
|
|
236
|
-
bundleId: 'com.harnessplayground',
|
|
237
|
-
appUid: 10234,
|
|
238
|
-
isAppRunning,
|
|
239
|
-
});
|
|
240
|
-
const crashWatch = monitor.watch('example.test.ts', 'execution');
|
|
241
|
-
let settled = false;
|
|
242
|
-
const watchHandled = crashWatch.promise.catch(() => {
|
|
243
|
-
settled = true;
|
|
244
|
-
});
|
|
245
|
-
await monitor.start();
|
|
246
|
-
monitor.stopRequested({
|
|
247
|
-
type: 'stop_requested',
|
|
248
|
-
at: Date.now(),
|
|
249
|
-
reason: 'manual',
|
|
250
|
-
});
|
|
251
|
-
await vi.advanceTimersByTimeAsync(400);
|
|
252
|
-
expect(settled).toBe(false);
|
|
253
|
-
crashWatch.cancel();
|
|
254
|
-
await monitor.stop();
|
|
255
|
-
await watchHandled;
|
|
256
|
-
});
|
|
257
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"app-session.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/app-session.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createAndroidAppSession } from '../app-session.js';
|
|
3
|
-
// Mimics nano-spawn: the async iterator only ends once its abort signal
|
|
4
|
-
// fires, matching how startLogcat's real subprocess reacts to abort.
|
|
5
|
-
const createAbortableLogcatProcess = (signal) => ({
|
|
6
|
-
[Symbol.asyncIterator]: () => ({
|
|
7
|
-
next: () => new Promise((resolve) => {
|
|
8
|
-
if (signal.aborted) {
|
|
9
|
-
resolve({ done: true, value: undefined });
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
signal.addEventListener('abort', () => resolve({ done: true, value: undefined }), { once: true });
|
|
13
|
-
}),
|
|
14
|
-
}),
|
|
15
|
-
catch: () => undefined,
|
|
16
|
-
});
|
|
17
|
-
describe('createAndroidAppSession', () => {
|
|
18
|
-
it('combines the session-lifetime signal with its own logcat abort controller', async () => {
|
|
19
|
-
let capturedSignal;
|
|
20
|
-
const controller = new AbortController();
|
|
21
|
-
const session = await createAndroidAppSession({
|
|
22
|
-
appUid: 1,
|
|
23
|
-
bundleId: 'com.example',
|
|
24
|
-
startApp: async () => undefined,
|
|
25
|
-
stopApp: async () => undefined,
|
|
26
|
-
getAppPid: async () => null,
|
|
27
|
-
getLogcatTimestamp: async () => '00:00:00.000',
|
|
28
|
-
startLogcat: (_args, options) => {
|
|
29
|
-
capturedSignal = options.signal;
|
|
30
|
-
return createAbortableLogcatProcess(options.signal);
|
|
31
|
-
},
|
|
32
|
-
signal: controller.signal,
|
|
33
|
-
});
|
|
34
|
-
expect(capturedSignal?.aborted).toBe(false);
|
|
35
|
-
controller.abort();
|
|
36
|
-
expect(capturedSignal?.aborted).toBe(true);
|
|
37
|
-
await session.dispose();
|
|
38
|
-
});
|
|
39
|
-
it('still aborts the logcat signal via dispose() with no external signal provided', async () => {
|
|
40
|
-
let capturedSignal;
|
|
41
|
-
const session = await createAndroidAppSession({
|
|
42
|
-
appUid: 1,
|
|
43
|
-
bundleId: 'com.example',
|
|
44
|
-
startApp: async () => undefined,
|
|
45
|
-
stopApp: async () => undefined,
|
|
46
|
-
getAppPid: async () => null,
|
|
47
|
-
getLogcatTimestamp: async () => '00:00:00.000',
|
|
48
|
-
startLogcat: (_args, options) => {
|
|
49
|
-
capturedSignal = options.signal;
|
|
50
|
-
return createAbortableLogcatProcess(options.signal);
|
|
51
|
-
},
|
|
52
|
-
});
|
|
53
|
-
expect(capturedSignal?.aborted).toBe(false);
|
|
54
|
-
await session.dispose();
|
|
55
|
-
expect(capturedSignal?.aborted).toBe(true);
|
|
56
|
-
});
|
|
57
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"avd-config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/avd-config.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { getNormalizedAvdCacheConfig, isAvdCompatible, parseAvdConfig, resolveAvdCachingEnabled, } from '../avd-config.js';
|
|
3
|
-
import { AndroidPlatformConfigSchema } from '../config.js';
|
|
4
|
-
import { getEmulatorCpuCores } from '../emulator-startup.js';
|
|
5
|
-
const emulatorCpuCores = getEmulatorCpuCores();
|
|
6
|
-
describe('AVD config helpers', () => {
|
|
7
|
-
it('parses snapshot config from Android schema', () => {
|
|
8
|
-
const config = AndroidPlatformConfigSchema.parse({
|
|
9
|
-
name: 'android',
|
|
10
|
-
bundleId: 'com.example.app',
|
|
11
|
-
device: {
|
|
12
|
-
type: 'emulator',
|
|
13
|
-
name: 'Pixel_8_API_35',
|
|
14
|
-
avd: {
|
|
15
|
-
apiLevel: 35,
|
|
16
|
-
profile: 'pixel_8',
|
|
17
|
-
diskSize: '1G',
|
|
18
|
-
heapSize: '512M',
|
|
19
|
-
snapshot: {
|
|
20
|
-
enabled: true,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
},
|
|
24
|
-
});
|
|
25
|
-
expect(config.device.type).toBe('emulator');
|
|
26
|
-
if (config.device.type === 'emulator') {
|
|
27
|
-
expect(config.device.avd?.snapshot?.enabled).toBe(true);
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
it('lets HARNESS_AVD_CACHING override config before interactive gating', () => {
|
|
31
|
-
expect(resolveAvdCachingEnabled({
|
|
32
|
-
avd: {
|
|
33
|
-
apiLevel: 35,
|
|
34
|
-
profile: 'pixel_8',
|
|
35
|
-
diskSize: '1G',
|
|
36
|
-
heapSize: '1G',
|
|
37
|
-
snapshot: { enabled: false },
|
|
38
|
-
},
|
|
39
|
-
isInteractive: false,
|
|
40
|
-
env: {
|
|
41
|
-
HARNESS_AVD_CACHING: 'true',
|
|
42
|
-
},
|
|
43
|
-
})).toBe(true);
|
|
44
|
-
});
|
|
45
|
-
it('disables caching for interactive sessions even when requested', () => {
|
|
46
|
-
expect(resolveAvdCachingEnabled({
|
|
47
|
-
avd: {
|
|
48
|
-
apiLevel: 35,
|
|
49
|
-
profile: 'pixel_8',
|
|
50
|
-
diskSize: '1G',
|
|
51
|
-
heapSize: '1G',
|
|
52
|
-
snapshot: { enabled: true },
|
|
53
|
-
},
|
|
54
|
-
isInteractive: true,
|
|
55
|
-
})).toBe(false);
|
|
56
|
-
});
|
|
57
|
-
it('parses config.ini and matches compatible AVD metadata', () => {
|
|
58
|
-
const avdConfig = parseAvdConfig(`
|
|
59
|
-
image.sysdir.1=system-images/android-35/default/x86_64/
|
|
60
|
-
abi.type=x86_64
|
|
61
|
-
hw.device.name=pixel_8
|
|
62
|
-
disk.dataPartition.size=1G
|
|
63
|
-
vm.heapSize=512M
|
|
64
|
-
hw.cpu.ncore=${emulatorCpuCores}
|
|
65
|
-
`);
|
|
66
|
-
expect(avdConfig.hwCpuNcore).toBe(String(emulatorCpuCores));
|
|
67
|
-
expect(isAvdCompatible({
|
|
68
|
-
emulator: {
|
|
69
|
-
type: 'emulator',
|
|
70
|
-
name: 'Pixel_8_API_35',
|
|
71
|
-
avd: {
|
|
72
|
-
apiLevel: 35,
|
|
73
|
-
profile: 'pixel_8',
|
|
74
|
-
diskSize: '1G',
|
|
75
|
-
heapSize: '512M',
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
avdConfig,
|
|
79
|
-
hostArch: 'x86_64',
|
|
80
|
-
})).toEqual({ compatible: true });
|
|
81
|
-
});
|
|
82
|
-
it('rejects AVDs missing hw.cpu.ncore', () => {
|
|
83
|
-
const avdConfig = parseAvdConfig(`
|
|
84
|
-
image.sysdir.1=system-images/android-35/default/x86_64/
|
|
85
|
-
abi.type=x86_64
|
|
86
|
-
hw.device.name=pixel_8
|
|
87
|
-
disk.dataPartition.size=1G
|
|
88
|
-
vm.heapSize=512M
|
|
89
|
-
`);
|
|
90
|
-
expect(isAvdCompatible({
|
|
91
|
-
emulator: {
|
|
92
|
-
type: 'emulator',
|
|
93
|
-
name: 'Pixel_8_API_35',
|
|
94
|
-
avd: {
|
|
95
|
-
apiLevel: 35,
|
|
96
|
-
profile: 'pixel_8',
|
|
97
|
-
diskSize: '1G',
|
|
98
|
-
heapSize: '512M',
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
avdConfig,
|
|
102
|
-
hostArch: 'x86_64',
|
|
103
|
-
})).toMatchObject({
|
|
104
|
-
compatible: false,
|
|
105
|
-
reason: `CPU core count mismatch: expected ${emulatorCpuCores}, got missing.`,
|
|
106
|
-
});
|
|
107
|
-
});
|
|
108
|
-
it('rejects AVDs with a different hw.cpu.ncore', () => {
|
|
109
|
-
const avdConfig = parseAvdConfig(`
|
|
110
|
-
image.sysdir.1=system-images/android-35/default/x86_64/
|
|
111
|
-
abi.type=x86_64
|
|
112
|
-
hw.device.name=pixel_8
|
|
113
|
-
disk.dataPartition.size=1G
|
|
114
|
-
vm.heapSize=512M
|
|
115
|
-
hw.cpu.ncore=${emulatorCpuCores === 4 ? 3 : 4}
|
|
116
|
-
`);
|
|
117
|
-
expect(isAvdCompatible({
|
|
118
|
-
emulator: {
|
|
119
|
-
type: 'emulator',
|
|
120
|
-
name: 'Pixel_8_API_35',
|
|
121
|
-
avd: {
|
|
122
|
-
apiLevel: 35,
|
|
123
|
-
profile: 'pixel_8',
|
|
124
|
-
diskSize: '1G',
|
|
125
|
-
heapSize: '512M',
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
avdConfig,
|
|
129
|
-
hostArch: 'x86_64',
|
|
130
|
-
})).toMatchObject({
|
|
131
|
-
compatible: false,
|
|
132
|
-
reason: `CPU core count mismatch: expected ${emulatorCpuCores}, got ${emulatorCpuCores === 4 ? 3 : 4}.`,
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
it('accepts disk partition sizes rewritten to bytes', () => {
|
|
136
|
-
const avdConfig = parseAvdConfig(`
|
|
137
|
-
image.sysdir.1=system-images/android-35/default/x86_64/
|
|
138
|
-
abi.type=x86_64
|
|
139
|
-
hw.device.name=pixel_8
|
|
140
|
-
disk.dataPartition.size=6442450944
|
|
141
|
-
vm.heapSize=512M
|
|
142
|
-
hw.cpu.ncore=${emulatorCpuCores}
|
|
143
|
-
`);
|
|
144
|
-
expect(isAvdCompatible({
|
|
145
|
-
emulator: {
|
|
146
|
-
type: 'emulator',
|
|
147
|
-
name: 'Pixel_8_API_35',
|
|
148
|
-
avd: {
|
|
149
|
-
apiLevel: 35,
|
|
150
|
-
profile: 'pixel_8',
|
|
151
|
-
diskSize: '1G',
|
|
152
|
-
heapSize: '512M',
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
avdConfig,
|
|
156
|
-
hostArch: 'x86_64',
|
|
157
|
-
})).toEqual({ compatible: true });
|
|
158
|
-
});
|
|
159
|
-
it('rejects smaller disk partitions even when sizes are normalized', () => {
|
|
160
|
-
const avdConfig = parseAvdConfig(`
|
|
161
|
-
image.sysdir.1=system-images/android-35/default/x86_64/
|
|
162
|
-
abi.type=x86_64
|
|
163
|
-
hw.device.name=pixel_8
|
|
164
|
-
disk.dataPartition.size=536870912
|
|
165
|
-
vm.heapSize=512M
|
|
166
|
-
`);
|
|
167
|
-
expect(isAvdCompatible({
|
|
168
|
-
emulator: {
|
|
169
|
-
type: 'emulator',
|
|
170
|
-
name: 'Pixel_8_API_35',
|
|
171
|
-
avd: {
|
|
172
|
-
apiLevel: 35,
|
|
173
|
-
profile: 'pixel_8',
|
|
174
|
-
diskSize: '1G',
|
|
175
|
-
heapSize: '512M',
|
|
176
|
-
},
|
|
177
|
-
},
|
|
178
|
-
avdConfig,
|
|
179
|
-
hostArch: 'x86_64',
|
|
180
|
-
})).toMatchObject({
|
|
181
|
-
compatible: false,
|
|
182
|
-
reason: 'Disk size mismatch: expected 1G, got 536870912.',
|
|
183
|
-
});
|
|
184
|
-
});
|
|
185
|
-
it('reports incompatibility when AVD metadata differs', () => {
|
|
186
|
-
const avdConfig = parseAvdConfig(`
|
|
187
|
-
image.sysdir.1=system-images/android-34/default/x86_64/
|
|
188
|
-
abi.type=x86_64
|
|
189
|
-
hw.device.name=pixel_7
|
|
190
|
-
disk.dataPartition.size=2G
|
|
191
|
-
vm.heapSize=1G
|
|
192
|
-
`);
|
|
193
|
-
expect(isAvdCompatible({
|
|
194
|
-
emulator: {
|
|
195
|
-
type: 'emulator',
|
|
196
|
-
name: 'Pixel_8_API_35',
|
|
197
|
-
avd: {
|
|
198
|
-
apiLevel: 35,
|
|
199
|
-
profile: 'pixel_8',
|
|
200
|
-
diskSize: '1G',
|
|
201
|
-
heapSize: '512M',
|
|
202
|
-
},
|
|
203
|
-
},
|
|
204
|
-
avdConfig,
|
|
205
|
-
hostArch: 'x86_64',
|
|
206
|
-
})).toMatchObject({
|
|
207
|
-
compatible: false,
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
it('normalizes AVD cache key input with name and host arch', () => {
|
|
211
|
-
expect(getNormalizedAvdCacheConfig({
|
|
212
|
-
emulator: {
|
|
213
|
-
type: 'emulator',
|
|
214
|
-
name: 'Pixel_8_API_35',
|
|
215
|
-
avd: {
|
|
216
|
-
apiLevel: 35,
|
|
217
|
-
profile: ' Pixel_8 ',
|
|
218
|
-
diskSize: '1G',
|
|
219
|
-
heapSize: '512M',
|
|
220
|
-
},
|
|
221
|
-
},
|
|
222
|
-
hostArch: 'arm64-v8a',
|
|
223
|
-
})).toEqual({
|
|
224
|
-
name: 'Pixel_8_API_35',
|
|
225
|
-
apiLevel: 35,
|
|
226
|
-
arch: 'arm64-v8a',
|
|
227
|
-
profile: 'pixel_8',
|
|
228
|
-
diskSize: '1g',
|
|
229
|
-
heapSize: '512m',
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ci-action.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/ci-action.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
const workspaceRoot = path.resolve(import.meta.dirname, '../../../..');
|
|
5
|
-
describe('Android GitHub action config', () => {
|
|
6
|
-
it('does not duplicate Android SDK verification in the action YAML', async () => {
|
|
7
|
-
const [rootAction, packageAction] = await Promise.all([
|
|
8
|
-
readFile(path.join(workspaceRoot, 'action.yml'), 'utf8'),
|
|
9
|
-
readFile(path.join(workspaceRoot, 'packages/github-action/src/action.yml'), 'utf8'),
|
|
10
|
-
]);
|
|
11
|
-
for (const actionYaml of [rootAction, packageAction]) {
|
|
12
|
-
expect(actionYaml).not.toContain('Verify Android SDK packages');
|
|
13
|
-
expect(actionYaml).toContain("steps.avd-cache.outputs.cache-hit != 'true'");
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
it('removes the third-party emulator runner and maps cacheAvd to HARNESS_AVD_CACHING', async () => {
|
|
17
|
-
const [rootAction, packageAction] = await Promise.all([
|
|
18
|
-
readFile(path.join(workspaceRoot, 'action.yml'), 'utf8'),
|
|
19
|
-
readFile(path.join(workspaceRoot, 'packages/github-action/src/action.yml'), 'utf8'),
|
|
20
|
-
]);
|
|
21
|
-
for (const actionYaml of [rootAction, packageAction]) {
|
|
22
|
-
expect(actionYaml).not.toContain('reactivecircus/android-emulator-runner');
|
|
23
|
-
expect(actionYaml).toContain('HARNESS_AVD_CACHING: ${{ inputs.cacheAvd }}');
|
|
24
|
-
expect(actionYaml).toContain('fromJson(steps.load-config.outputs.config).action.avdCachingEnabled');
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
it('saves the AVD cache after the Harness run step', async () => {
|
|
28
|
-
const [rootAction, packageAction] = await Promise.all([
|
|
29
|
-
readFile(path.join(workspaceRoot, 'action.yml'), 'utf8'),
|
|
30
|
-
readFile(path.join(workspaceRoot, 'packages/github-action/src/action.yml'), 'utf8'),
|
|
31
|
-
]);
|
|
32
|
-
for (const actionYaml of [rootAction, packageAction]) {
|
|
33
|
-
expect(actionYaml.indexOf('- name: Run E2E tests')).toBeLessThan(actionYaml.indexOf('- name: Save AVD cache'));
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
it('uses a cache key that includes the emulator name', async () => {
|
|
37
|
-
const [rootAction, packageAction] = await Promise.all([
|
|
38
|
-
readFile(path.join(workspaceRoot, 'action.yml'), 'utf8'),
|
|
39
|
-
readFile(path.join(workspaceRoot, 'packages/github-action/src/action.yml'), 'utf8'),
|
|
40
|
-
]);
|
|
41
|
-
for (const actionYaml of [rootAction, packageAction]) {
|
|
42
|
-
expect(actionYaml).toContain("AVD_NAME='${{ fromJson(steps.load-config.outputs.config).config.device.name }}'");
|
|
43
|
-
expect(actionYaml).toContain('CACHE_KEY="avd-$AVD_NAME-$ARCH-$AVD_CONFIG_HASH"');
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"crash-diagnostics.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/crash-diagnostics.test.ts"],"names":[],"mappings":""}
|