@onekeyfe/hd-core 1.2.0-alpha.106 → 1.2.0-alpha.107
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/__tests__/DeviceCommands.test.ts +28 -100
- package/__tests__/device-lifecycle-events.test.ts +2 -113
- package/__tests__/device-pool-state.test.ts +0 -25
- package/__tests__/deviceSettings.test.ts +1 -0
- package/__tests__/deviceUploadNft.test.ts +4 -33
- package/__tests__/get-device-state.test.ts +13 -52
- package/__tests__/logBlockEvent.test.ts +1 -13
- package/__tests__/protocol-v2.test.ts +121 -758
- package/__tests__/refresh-device-state.test.ts +1 -3
- package/__tests__/search-devices.test.ts +38 -32
- package/dist/api/FirmwareUpdateV4.d.ts +3 -10
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/UploadPortfolio.d.ts +1 -1
- package/dist/api/UploadPortfolio.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceUploadNft.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts +3 -2
- package/dist/api/protocol-v2/DeviceUploadWallpaper.d.ts.map +1 -1
- package/dist/api/ton/TonSignMessage.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +2 -7
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts.map +1 -1
- package/dist/events/logBlockEvent.d.ts.map +1 -1
- package/dist/index.d.ts +16 -12
- package/dist/index.js +201 -446
- package/dist/protocols/protocol-v2/features.d.ts +1 -9
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/index.d.ts +2 -2
- package/dist/protocols/protocol-v2/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +1 -1
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts.map +1 -1
- package/dist/utils/pro2Nft.d.ts +0 -7
- package/dist/utils/pro2Nft.d.ts.map +1 -1
- package/package.json +4 -6
- package/src/api/FirmwareUpdateV4.ts +174 -312
- package/src/api/SearchDevices.ts +2 -2
- package/src/api/UploadPortfolio.ts +2 -9
- package/src/api/protocol-v2/DeviceUploadNft.ts +8 -56
- package/src/api/protocol-v2/DeviceUploadWallpaper.ts +18 -37
- package/src/api/ton/TonSignMessage.ts +3 -5
- package/src/core/index.ts +2 -6
- package/src/device/Device.ts +31 -53
- package/src/device/DeviceCommands.ts +14 -4
- package/src/device/DevicePool.ts +2 -6
- package/src/events/logBlockEvent.ts +1 -14
- package/src/protocols/protocol-v2/features.ts +2 -19
- package/src/protocols/protocol-v2/index.ts +0 -2
- package/src/types/api/protocolV2.ts +1 -1
- package/src/types/device.ts +2 -1
- package/src/utils/deviceSettings.ts +1 -1
- package/src/utils/pro2Nft.ts +8 -31
- package/__tests__/base64Data.test.ts +0 -70
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +0 -145
- package/__tests__/resourceBase64Boundary.test.ts +0 -48
- package/__tests__/ton-sign-message.test.ts +0 -84
- package/dist/api/helpers/base64Data.d.ts +0 -16
- package/dist/api/helpers/base64Data.d.ts.map +0 -1
- package/src/api/helpers/base64Data.ts +0 -85
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
1
|
+
import { EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
3
2
|
import { sha256 } from '@noble/hashes/sha256';
|
|
4
3
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
5
4
|
import JSZip from 'jszip';
|
|
6
|
-
import { encode as encodeJpeg } from 'jpeg-js';
|
|
7
5
|
import {
|
|
8
6
|
DeviceRebootType,
|
|
9
7
|
DeviceSessionPinType,
|
|
@@ -125,18 +123,6 @@ jest.mock('../src/data/config', () => ({
|
|
|
125
123
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
126
124
|
}));
|
|
127
125
|
|
|
128
|
-
const jpegBase64Cache = new Map<string, string>();
|
|
129
|
-
|
|
130
|
-
const createJpegBase64 = (width: number, height: number) => {
|
|
131
|
-
const key = `${width}x${height}`;
|
|
132
|
-
const cached = jpegBase64Cache.get(key);
|
|
133
|
-
if (cached) return cached;
|
|
134
|
-
const rgba = new Uint8Array(width * height * 4).fill(0xff);
|
|
135
|
-
const value = encodeJpeg({ width, height, data: rgba }, 80).data.toString('base64');
|
|
136
|
-
jpegBase64Cache.set(key, value);
|
|
137
|
-
return value;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
126
|
const createProtocolV2OkppBinary = ({
|
|
141
127
|
version = [1, 2, 3],
|
|
142
128
|
payloadHashByte = 0x11,
|
|
@@ -174,18 +160,8 @@ const createWalletSessionTypedCall = (
|
|
|
174
160
|
});
|
|
175
161
|
|
|
176
162
|
describe('DeviceUploadWallpaper', () => {
|
|
177
|
-
const wallpaperProtocolInfo = {
|
|
178
|
-
version: 2,
|
|
179
|
-
supported_messages: [60412, 60805, 60809],
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const stubWallpaperDevice = <T extends Record<string, any>>(device: T) =>
|
|
183
|
-
stubDevice({
|
|
184
|
-
...device,
|
|
185
|
-
ensureProtocolV2RuntimeContext: jest.fn().mockResolvedValue(wallpaperProtocolInfo),
|
|
186
|
-
});
|
|
187
|
-
|
|
188
163
|
test('encodes, uploads and applies a Pro2 wallpaper', async () => {
|
|
164
|
+
const rgba = new Uint8Array(604 * 1024 * 4).fill(255);
|
|
189
165
|
const typedCall = jest.fn().mockImplementation((request, _response, params) => {
|
|
190
166
|
if (request === 'FilesystemDirMake') return { message: { message: 'directory ready' } };
|
|
191
167
|
if (request === 'FilesystemFileWrite') {
|
|
@@ -199,12 +175,9 @@ describe('DeviceUploadWallpaper', () => {
|
|
|
199
175
|
});
|
|
200
176
|
const method = new DeviceUploadWallpaper({
|
|
201
177
|
id: 1,
|
|
202
|
-
payload: {
|
|
203
|
-
method: 'deviceUploadWallpaper',
|
|
204
|
-
jpegBase64: createJpegBase64(604, 1024),
|
|
205
|
-
},
|
|
178
|
+
payload: { method: 'deviceUploadWallpaper', width: 604, height: 1024, rgba },
|
|
206
179
|
});
|
|
207
|
-
(method as any).device =
|
|
180
|
+
(method as any).device = stubDevice({ commands: { typedCall } });
|
|
208
181
|
method.postMessage = jest.fn();
|
|
209
182
|
|
|
210
183
|
method.init();
|
|
@@ -252,10 +225,12 @@ describe('DeviceUploadWallpaper', () => {
|
|
|
252
225
|
id: 1,
|
|
253
226
|
payload: {
|
|
254
227
|
method: 'deviceUploadWallpaper',
|
|
255
|
-
|
|
228
|
+
width: 604,
|
|
229
|
+
height: 1024,
|
|
230
|
+
rgba: new Uint8Array(604 * 1024 * 4),
|
|
256
231
|
},
|
|
257
232
|
});
|
|
258
|
-
(method as any).device =
|
|
233
|
+
(method as any).device = stubDevice({ commands: { typedCall } });
|
|
259
234
|
method.init();
|
|
260
235
|
|
|
261
236
|
await expect(method.run()).rejects.toThrow('write failed');
|
|
@@ -267,37 +242,15 @@ describe('DeviceUploadWallpaper', () => {
|
|
|
267
242
|
id: 1,
|
|
268
243
|
payload: {
|
|
269
244
|
method: 'deviceUploadWallpaper',
|
|
270
|
-
|
|
245
|
+
width: 604,
|
|
246
|
+
height: 1024,
|
|
247
|
+
rgba: new Uint8Array(604 * 1024 * 4),
|
|
271
248
|
fileName: '../wallpaper.bin',
|
|
272
249
|
},
|
|
273
250
|
});
|
|
274
251
|
|
|
275
252
|
expect(() => method.init()).toThrow('fileName');
|
|
276
253
|
});
|
|
277
|
-
|
|
278
|
-
test('rejects unsupported firmware before creating or writing files', async () => {
|
|
279
|
-
const typedCall = jest.fn();
|
|
280
|
-
const method = new DeviceUploadWallpaper({
|
|
281
|
-
id: 1,
|
|
282
|
-
payload: {
|
|
283
|
-
method: 'deviceUploadWallpaper',
|
|
284
|
-
jpegBase64: createJpegBase64(604, 1024),
|
|
285
|
-
},
|
|
286
|
-
});
|
|
287
|
-
(method as any).device = stubDevice({
|
|
288
|
-
commands: { typedCall },
|
|
289
|
-
ensureProtocolV2RuntimeContext: jest.fn().mockResolvedValue({
|
|
290
|
-
version: 2,
|
|
291
|
-
supported_messages: [60805, 60809],
|
|
292
|
-
}),
|
|
293
|
-
});
|
|
294
|
-
method.init();
|
|
295
|
-
|
|
296
|
-
await expect(method.run()).rejects.toMatchObject({
|
|
297
|
-
errorCode: HardwareErrorCode.DeviceNotSupportMethod,
|
|
298
|
-
});
|
|
299
|
-
expect(typedCall).not.toHaveBeenCalled();
|
|
300
|
-
});
|
|
301
254
|
});
|
|
302
255
|
|
|
303
256
|
describe('UploadPortfolio', () => {
|
|
@@ -313,6 +266,7 @@ describe('UploadPortfolio', () => {
|
|
|
313
266
|
});
|
|
314
267
|
|
|
315
268
|
test('writes and applies the portfolio while the device is locked without unlocking', async () => {
|
|
269
|
+
const packageBytes = new Uint8Array([1, 2, 3]);
|
|
316
270
|
const typedCall = jest
|
|
317
271
|
.fn()
|
|
318
272
|
.mockResolvedValueOnce({ message: { processed_byte: 3 } })
|
|
@@ -328,7 +282,7 @@ describe('UploadPortfolio', () => {
|
|
|
328
282
|
id: 1,
|
|
329
283
|
payload: {
|
|
330
284
|
method: 'uploadPortfolio',
|
|
331
|
-
|
|
285
|
+
packageBytes,
|
|
332
286
|
},
|
|
333
287
|
});
|
|
334
288
|
(method as any).device = device;
|
|
@@ -360,7 +314,7 @@ describe('UploadPortfolio', () => {
|
|
|
360
314
|
id: 1,
|
|
361
315
|
payload: {
|
|
362
316
|
method: 'uploadPortfolio',
|
|
363
|
-
|
|
317
|
+
packageBytes,
|
|
364
318
|
},
|
|
365
319
|
});
|
|
366
320
|
(method as any).device = stubPortfolioDevice({ commands: { typedCall } });
|
|
@@ -408,7 +362,7 @@ describe('UploadPortfolio', () => {
|
|
|
408
362
|
id: 1,
|
|
409
363
|
payload: {
|
|
410
364
|
method: 'uploadPortfolio',
|
|
411
|
-
|
|
365
|
+
packageBytes: new Uint8Array([1]),
|
|
412
366
|
},
|
|
413
367
|
});
|
|
414
368
|
(method as any).device = stubPortfolioDevice({ commands: { typedCall } });
|
|
@@ -430,7 +384,7 @@ describe('UploadPortfolio', () => {
|
|
|
430
384
|
id: 1,
|
|
431
385
|
payload: {
|
|
432
386
|
method: 'uploadPortfolio',
|
|
433
|
-
|
|
387
|
+
packageBytes,
|
|
434
388
|
},
|
|
435
389
|
});
|
|
436
390
|
method.abortSignal = abortController.signal;
|
|
@@ -451,7 +405,7 @@ describe('UploadPortfolio', () => {
|
|
|
451
405
|
id: 1,
|
|
452
406
|
payload: {
|
|
453
407
|
method: 'uploadPortfolio',
|
|
454
|
-
|
|
408
|
+
packageBytes: new Uint8Array([1]),
|
|
455
409
|
},
|
|
456
410
|
});
|
|
457
411
|
(method as any).device = stubDevice({
|
|
@@ -469,18 +423,6 @@ describe('UploadPortfolio', () => {
|
|
|
469
423
|
});
|
|
470
424
|
expect(typedCall).not.toHaveBeenCalled();
|
|
471
425
|
});
|
|
472
|
-
|
|
473
|
-
test('rejects non-canonical Base64 before device communication', () => {
|
|
474
|
-
const method = new UploadPortfolio({
|
|
475
|
-
id: 1,
|
|
476
|
-
payload: {
|
|
477
|
-
method: 'uploadPortfolio',
|
|
478
|
-
packageBase64: 'data:application/octet-stream;base64,AQID',
|
|
479
|
-
},
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
expect(() => method.init()).toThrow('canonical Base64');
|
|
483
|
-
});
|
|
484
426
|
});
|
|
485
427
|
|
|
486
428
|
const descriptor = {
|
|
@@ -907,34 +849,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
907
849
|
).toBeUndefined();
|
|
908
850
|
});
|
|
909
851
|
|
|
910
|
-
test('infers legacy Protocol V2 loader mode from firmware image metadata', () => {
|
|
911
|
-
const legacyProtocolInfo = {
|
|
912
|
-
version: 1,
|
|
913
|
-
build_fingerprint: '',
|
|
914
|
-
supported_messages: [],
|
|
915
|
-
protobuf_definition: null,
|
|
916
|
-
};
|
|
917
|
-
|
|
918
|
-
expect(
|
|
919
|
-
getProtocolV2RuntimeMode(legacyProtocolInfo, {
|
|
920
|
-
fw: { bootloader: { version: '0.2.0' } },
|
|
921
|
-
})
|
|
922
|
-
).toBe('bootloader');
|
|
923
|
-
expect(
|
|
924
|
-
getProtocolV2RuntimeMode(legacyProtocolInfo, {
|
|
925
|
-
fw: { romloader: { version: '1.0.0' } },
|
|
926
|
-
})
|
|
927
|
-
).toBe('romloader');
|
|
928
|
-
expect(
|
|
929
|
-
getProtocolV2RuntimeMode(legacyProtocolInfo, {
|
|
930
|
-
fw: {
|
|
931
|
-
application: { version: '1.2.3' },
|
|
932
|
-
bootloader: { version: '0.2.0' },
|
|
933
|
-
},
|
|
934
|
-
})
|
|
935
|
-
).toBeUndefined();
|
|
936
|
-
});
|
|
937
|
-
|
|
938
852
|
test('keeps the Protocol V2 main wallet on the default empty-passphrase context', async () => {
|
|
939
853
|
const features = normalizeProtocolV2Features(descriptor as any);
|
|
940
854
|
features.firmwareVersion = '1.2.3';
|
|
@@ -2783,35 +2697,19 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2783
2697
|
});
|
|
2784
2698
|
});
|
|
2785
2699
|
|
|
2786
|
-
test('syncs Protocol V2 cached features
|
|
2787
|
-
const typedCall = jest.fn().mockResolvedValue({ message: protocolV2ApplicationInfo });
|
|
2700
|
+
test('syncs Protocol V2 cached features without cached profile', () => {
|
|
2788
2701
|
const cached = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
2789
2702
|
(cached as any).features = {
|
|
2790
2703
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
2791
2704
|
deviceId: null,
|
|
2792
2705
|
serialNo: 'CACHED-SERIAL',
|
|
2793
2706
|
};
|
|
2794
|
-
(cached as any).commands = { typedCall };
|
|
2795
2707
|
|
|
2796
2708
|
const current = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
2797
|
-
current.updateState(
|
|
2798
|
-
{
|
|
2799
|
-
raw: { protocolV2ProtocolInfo: protocolV2BootloaderInfo },
|
|
2800
|
-
status: { mode: 'bootloader' },
|
|
2801
|
-
},
|
|
2802
|
-
'initialize'
|
|
2803
|
-
);
|
|
2804
2709
|
current.updateFromCache(cached);
|
|
2805
2710
|
|
|
2806
|
-
await expect(current.ensureProtocolV2RuntimeContext()).resolves.toEqual(
|
|
2807
|
-
protocolV2ApplicationInfo
|
|
2808
|
-
);
|
|
2809
|
-
|
|
2810
2711
|
expect(current.getCurrentDeviceId()).toBeUndefined();
|
|
2811
2712
|
expect(current.getCurrentSerialNo()).toBe('CACHED-SERIAL');
|
|
2812
|
-
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
2813
|
-
eventless_wallet_session: true,
|
|
2814
|
-
});
|
|
2815
2713
|
expect(current.toMessageObject()).toMatchObject({
|
|
2816
2714
|
serialNo: 'CACHED-SERIAL',
|
|
2817
2715
|
uuid: 'CACHED-SERIAL',
|
|
@@ -3296,159 +3194,6 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
3296
3194
|
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
3297
3195
|
});
|
|
3298
3196
|
|
|
3299
|
-
test('initializes a legacy Protocol V2 bootloader without compatibility options', async () => {
|
|
3300
|
-
const device = Device.fromDescriptor({
|
|
3301
|
-
path: 'usb-path',
|
|
3302
|
-
protocolType: 'V2',
|
|
3303
|
-
} as any);
|
|
3304
|
-
const typedCall = jest
|
|
3305
|
-
.fn()
|
|
3306
|
-
.mockResolvedValueOnce({
|
|
3307
|
-
type: 'DeviceInfo',
|
|
3308
|
-
message: {
|
|
3309
|
-
hw: { Device_type: DeviceType.PRO2, serial_no: 'PR2SERIAL' },
|
|
3310
|
-
fw: { bootloader: { version: '0.2.0' } },
|
|
3311
|
-
},
|
|
3312
|
-
})
|
|
3313
|
-
.mockResolvedValueOnce({
|
|
3314
|
-
type: 'ProtocolInfo',
|
|
3315
|
-
message: {
|
|
3316
|
-
version: 1,
|
|
3317
|
-
build_fingerprint: '',
|
|
3318
|
-
supported_messages: [],
|
|
3319
|
-
protobuf_definition: null,
|
|
3320
|
-
},
|
|
3321
|
-
});
|
|
3322
|
-
(device as any).commands = { typedCall };
|
|
3323
|
-
|
|
3324
|
-
await device.initialize();
|
|
3325
|
-
|
|
3326
|
-
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
3327
|
-
expect(typedCall).not.toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
3328
|
-
expect(device.features).toMatchObject({
|
|
3329
|
-
mode: 'bootloader',
|
|
3330
|
-
bootloaderMode: true,
|
|
3331
|
-
initialized: null,
|
|
3332
|
-
});
|
|
3333
|
-
});
|
|
3334
|
-
|
|
3335
|
-
test('uses DeviceStatusGet to identify App mode for legacy ProtocolInfo', async () => {
|
|
3336
|
-
const device = Device.fromDescriptor({
|
|
3337
|
-
path: 'usb-path',
|
|
3338
|
-
protocolType: 'V2',
|
|
3339
|
-
} as any);
|
|
3340
|
-
const typedCall = jest
|
|
3341
|
-
.fn()
|
|
3342
|
-
.mockResolvedValueOnce({
|
|
3343
|
-
type: 'ProtocolInfo',
|
|
3344
|
-
message: {
|
|
3345
|
-
version: 1,
|
|
3346
|
-
build_fingerprint: '',
|
|
3347
|
-
supported_messages: [],
|
|
3348
|
-
protobuf_definition: null,
|
|
3349
|
-
},
|
|
3350
|
-
})
|
|
3351
|
-
.mockResolvedValueOnce({
|
|
3352
|
-
type: 'DeviceStatus',
|
|
3353
|
-
message: { init_states: true, unlocked: true },
|
|
3354
|
-
});
|
|
3355
|
-
(device as any).commands = { typedCall };
|
|
3356
|
-
|
|
3357
|
-
await device.probeProtocolV2RuntimeState(
|
|
3358
|
-
{
|
|
3359
|
-
hw: { Device_type: DeviceType.PRO2, serial_no: 'PR2SERIAL' },
|
|
3360
|
-
fw: { application: { version: '1.2.3' } },
|
|
3361
|
-
},
|
|
3362
|
-
5000
|
|
3363
|
-
);
|
|
3364
|
-
|
|
3365
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
3366
|
-
1,
|
|
3367
|
-
'ProtocolInfoRequest',
|
|
3368
|
-
'ProtocolInfo',
|
|
3369
|
-
{ eventless_wallet_session: true },
|
|
3370
|
-
{ timeoutMs: 5000 }
|
|
3371
|
-
);
|
|
3372
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
3373
|
-
2,
|
|
3374
|
-
'DeviceStatusGet',
|
|
3375
|
-
'DeviceStatus',
|
|
3376
|
-
{},
|
|
3377
|
-
{ timeoutMs: 5000 }
|
|
3378
|
-
);
|
|
3379
|
-
expect(device.features).toMatchObject({
|
|
3380
|
-
mode: 'normal',
|
|
3381
|
-
bootloaderMode: false,
|
|
3382
|
-
initialized: true,
|
|
3383
|
-
unlocked: true,
|
|
3384
|
-
});
|
|
3385
|
-
});
|
|
3386
|
-
|
|
3387
|
-
test('maps legacy bootloader metadata without calling DeviceStatusGet', async () => {
|
|
3388
|
-
const device = Device.fromDescriptor({
|
|
3389
|
-
path: 'usb-path',
|
|
3390
|
-
protocolType: 'V2',
|
|
3391
|
-
} as any);
|
|
3392
|
-
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
3393
|
-
type: 'ProtocolInfo',
|
|
3394
|
-
message: {
|
|
3395
|
-
version: 1,
|
|
3396
|
-
build_fingerprint: '',
|
|
3397
|
-
supported_messages: [],
|
|
3398
|
-
protobuf_definition: null,
|
|
3399
|
-
},
|
|
3400
|
-
});
|
|
3401
|
-
(device as any).commands = { typedCall };
|
|
3402
|
-
|
|
3403
|
-
await device.probeProtocolV2RuntimeState({
|
|
3404
|
-
hw: { Device_type: DeviceType.NEO, serial_no: 'NEOSERIAL' },
|
|
3405
|
-
fw: { bootloader: { version: '0.2.0' } },
|
|
3406
|
-
});
|
|
3407
|
-
|
|
3408
|
-
expect(typedCall).toHaveBeenNthCalledWith(1, 'ProtocolInfoRequest', 'ProtocolInfo', {
|
|
3409
|
-
eventless_wallet_session: true,
|
|
3410
|
-
});
|
|
3411
|
-
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
3412
|
-
expect(typedCall).not.toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
3413
|
-
expect(device.features).toMatchObject({
|
|
3414
|
-
deviceType: 'neo',
|
|
3415
|
-
mode: 'bootloader',
|
|
3416
|
-
bootloaderMode: true,
|
|
3417
|
-
initialized: null,
|
|
3418
|
-
});
|
|
3419
|
-
});
|
|
3420
|
-
|
|
3421
|
-
test('maps legacy romloader metadata without calling DeviceStatusGet', async () => {
|
|
3422
|
-
const device = Device.fromDescriptor({
|
|
3423
|
-
path: 'usb-path',
|
|
3424
|
-
protocolType: 'V2',
|
|
3425
|
-
} as any);
|
|
3426
|
-
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
3427
|
-
type: 'ProtocolInfo',
|
|
3428
|
-
message: {
|
|
3429
|
-
version: 1,
|
|
3430
|
-
build_fingerprint: '',
|
|
3431
|
-
supported_messages: [],
|
|
3432
|
-
protobuf_definition: null,
|
|
3433
|
-
},
|
|
3434
|
-
});
|
|
3435
|
-
(device as any).commands = { typedCall };
|
|
3436
|
-
|
|
3437
|
-
await device.probeProtocolV2RuntimeState({
|
|
3438
|
-
hw: { Device_type: DeviceType.PRO2, serial_no: 'PR2SERIAL' },
|
|
3439
|
-
fw: { romloader: { version: '1.0.0' } },
|
|
3440
|
-
});
|
|
3441
|
-
|
|
3442
|
-
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
3443
|
-
expect(typedCall).not.toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
3444
|
-
expect(device.features).toMatchObject({
|
|
3445
|
-
deviceType: 'pro2',
|
|
3446
|
-
mode: 'romloader',
|
|
3447
|
-
bootloaderMode: true,
|
|
3448
|
-
initialized: null,
|
|
3449
|
-
});
|
|
3450
|
-
});
|
|
3451
|
-
|
|
3452
3197
|
test('does not reinterpret a state update error as runtime detection failure', async () => {
|
|
3453
3198
|
const device = Device.fromDescriptor({
|
|
3454
3199
|
path: 'usb-path',
|
|
@@ -4353,7 +4098,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4353
4098
|
).toEqual([3, 4]);
|
|
4354
4099
|
});
|
|
4355
4100
|
|
|
4356
|
-
test('
|
|
4101
|
+
test('requires the external host to prepare manifest resources before bootloader entry', async () => {
|
|
4357
4102
|
const method = new FirmwareUpdateV4({
|
|
4358
4103
|
id: 1,
|
|
4359
4104
|
payload: {
|
|
@@ -4363,7 +4108,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4363
4108
|
},
|
|
4364
4109
|
});
|
|
4365
4110
|
method.init();
|
|
4366
|
-
jest.spyOn(DataManager, 'getSettings').mockReturnValue('external-only' as never);
|
|
4367
4111
|
|
|
4368
4112
|
(method as any).device = stubDevice({
|
|
4369
4113
|
originalDescriptor: { id: 'usb-id', path: 'app-path', protocolType: 'V2' },
|
|
@@ -4391,170 +4135,44 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4391
4135
|
expect((method as any).enterProtocolV2BootloaderMode).not.toHaveBeenCalled();
|
|
4392
4136
|
});
|
|
4393
4137
|
|
|
4394
|
-
test('
|
|
4395
|
-
const resourceArchiveBinary = new Uint8Array([1, 2, 3, 4]).buffer;
|
|
4396
|
-
const applicationP1Binary = new Uint8Array([5, 6, 7, 8]).buffer;
|
|
4397
|
-
const applicationP1InstallItem = {
|
|
4398
|
-
fileName: 'application_p1.bin',
|
|
4399
|
-
binary: applicationP1Binary,
|
|
4400
|
-
targetId: 4,
|
|
4401
|
-
kind: 'firmware',
|
|
4402
|
-
};
|
|
4403
|
-
const resourceArchiveSha256 = bytesToHex(sha256(new Uint8Array(resourceArchiveBinary)));
|
|
4138
|
+
test('reboots Protocol V2 normal-mode device to bootloader before transfer', async () => {
|
|
4404
4139
|
const method = new FirmwareUpdateV4({
|
|
4405
4140
|
id: 1,
|
|
4406
4141
|
payload: {
|
|
4407
4142
|
method: 'firmwareUpdateV4',
|
|
4408
|
-
platform: 'ext',
|
|
4409
|
-
targetsToUpdate: ['app_v1', 'resource'],
|
|
4410
4143
|
},
|
|
4411
4144
|
});
|
|
4412
|
-
method.init();
|
|
4413
|
-
|
|
4414
4145
|
(method as any).device = stubDevice({
|
|
4415
|
-
originalDescriptor: { id: 'usb-id', path: '
|
|
4416
|
-
features: {
|
|
4417
|
-
deviceType: 'pro2',
|
|
4418
|
-
firmwareVersion: '1.0.0',
|
|
4419
|
-
mode: 'normal',
|
|
4420
|
-
bootloaderMode: false,
|
|
4421
|
-
capabilities: [],
|
|
4422
|
-
},
|
|
4423
|
-
getCurrentDeviceType: () => 'pro2',
|
|
4146
|
+
originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
4147
|
+
features: { bootloader_mode: false, capabilities: [] },
|
|
4424
4148
|
isBootloader: () => false,
|
|
4425
|
-
|
|
4426
|
-
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
bootloaderBinary: null,
|
|
4430
|
-
fwBinaryMap: [
|
|
4431
|
-
{
|
|
4432
|
-
fileName: applicationP1InstallItem.fileName,
|
|
4433
|
-
binary: applicationP1Binary,
|
|
4434
|
-
targetId: applicationP1InstallItem.targetId,
|
|
4435
|
-
},
|
|
4436
|
-
],
|
|
4437
|
-
installItems: [applicationP1InstallItem],
|
|
4149
|
+
probeProtocolV2RuntimeState: jest.fn().mockResolvedValue({
|
|
4150
|
+
mode: 'bootloader',
|
|
4151
|
+
bootloaderMode: true,
|
|
4152
|
+
}),
|
|
4438
4153
|
});
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
(method as any).runProtocolV2PreparedArtifacts = jest
|
|
4442
|
-
.fn()
|
|
4443
|
-
.mockResolvedValue('sdk-managed-resource-result');
|
|
4444
|
-
method.postTipMessage = jest.fn();
|
|
4445
|
-
jest.spyOn(DataManager, 'getSettings').mockReturnValue('sdk-managed' as never);
|
|
4446
|
-
jest.spyOn(DataManager, 'getProtocolV2ResourceSource').mockReturnValue({
|
|
4447
|
-
archiveUrl: 'https://example.com/pro2-resource.zip',
|
|
4448
|
-
archiveSize: resourceArchiveBinary.byteLength,
|
|
4449
|
-
archiveSha256: resourceArchiveSha256,
|
|
4154
|
+
(method as any).protocolV2Reboot = jest.fn().mockResolvedValue({
|
|
4155
|
+
message: 'Device rebooted successfully',
|
|
4450
4156
|
});
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4157
|
+
(method as any).checkDeviceToBootloader = jest.fn();
|
|
4158
|
+
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
4159
|
+
if (requestType === 'DeviceInfoGet') {
|
|
4160
|
+
return Promise.resolve({
|
|
4161
|
+
type: 'DeviceInfo',
|
|
4162
|
+
message: protocolV2BootloaderDeviceInfo,
|
|
4163
|
+
});
|
|
4164
|
+
}
|
|
4165
|
+
if (requestType === 'ProtocolInfoRequest') {
|
|
4166
|
+
return Promise.resolve({
|
|
4167
|
+
type: 'ProtocolInfo',
|
|
4168
|
+
message: protocolV2BootloaderInfo,
|
|
4169
|
+
});
|
|
4170
|
+
}
|
|
4171
|
+
return Promise.reject(new Error(`unexpected call ${requestType}`));
|
|
4460
4172
|
});
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
features: expect.objectContaining({ deviceType: 'pro2' }),
|
|
4465
|
-
firmwareType: EFirmwareType.Universal,
|
|
4466
|
-
availableInstallItems: [applicationP1InstallItem],
|
|
4467
|
-
});
|
|
4468
|
-
expect((method as any).runProtocolV2PreparedArtifacts).toHaveBeenCalledTimes(1);
|
|
4469
|
-
expect(release).toHaveBeenCalledTimes(1);
|
|
4470
|
-
});
|
|
4471
|
-
|
|
4472
|
-
test('preserves SDK-managed resource installation errors after preparation', async () => {
|
|
4473
|
-
const resourceArchiveBinary = new Uint8Array([1, 2, 3, 4]).buffer;
|
|
4474
|
-
const installError = ERRORS.TypedError(
|
|
4475
|
-
HardwareErrorCode.FirmwareError,
|
|
4476
|
-
'device rejected the prepared resource installation'
|
|
4477
|
-
);
|
|
4478
|
-
const method = new FirmwareUpdateV4({
|
|
4479
|
-
id: 1,
|
|
4480
|
-
payload: {
|
|
4481
|
-
method: 'firmwareUpdateV4',
|
|
4482
|
-
platform: 'ext',
|
|
4483
|
-
targetsToUpdate: ['resource'],
|
|
4484
|
-
},
|
|
4485
|
-
});
|
|
4486
|
-
method.init();
|
|
4487
|
-
|
|
4488
|
-
(method as any).device = stubDevice({
|
|
4489
|
-
originalDescriptor: { id: 'usb-id', path: 'app-path', protocolType: 'V2' },
|
|
4490
|
-
features: {
|
|
4491
|
-
deviceType: 'pro2',
|
|
4492
|
-
firmwareVersion: '1.0.0',
|
|
4493
|
-
mode: 'normal',
|
|
4494
|
-
bootloaderMode: false,
|
|
4495
|
-
capabilities: [],
|
|
4496
|
-
},
|
|
4497
|
-
getCurrentDeviceType: () => 'pro2',
|
|
4498
|
-
isBootloader: () => false,
|
|
4499
|
-
isRomloader: () => false,
|
|
4500
|
-
});
|
|
4501
|
-
(method as any).captureProtocolV2PhysicalIdentity = jest.fn().mockResolvedValue(undefined);
|
|
4502
|
-
(method as any).downloadRemoteProtocolV2ResourceArchive = jest
|
|
4503
|
-
.fn()
|
|
4504
|
-
.mockResolvedValue(resourceArchiveBinary);
|
|
4505
|
-
const release = jest.fn();
|
|
4506
|
-
(method as any).prepareProtocolV2LocalMemoryHost = jest.fn().mockResolvedValue({ release });
|
|
4507
|
-
(method as any).runProtocolV2PreparedArtifacts = jest.fn().mockRejectedValue(installError);
|
|
4508
|
-
method.postTipMessage = jest.fn();
|
|
4509
|
-
jest.spyOn(DataManager, 'getSettings').mockReturnValue('sdk-managed' as never);
|
|
4510
|
-
|
|
4511
|
-
await expect(method.run()).rejects.toBe(installError);
|
|
4512
|
-
|
|
4513
|
-
expect(forceReloadDataSpy).toHaveBeenCalledWith({
|
|
4514
|
-
requireResources: true,
|
|
4515
|
-
resourceDeviceType: EDeviceType.Pro2,
|
|
4516
|
-
});
|
|
4517
|
-
expect(release).toHaveBeenCalledTimes(1);
|
|
4518
|
-
});
|
|
4519
|
-
|
|
4520
|
-
test('reboots Protocol V2 normal-mode device to bootloader before transfer', async () => {
|
|
4521
|
-
const method = new FirmwareUpdateV4({
|
|
4522
|
-
id: 1,
|
|
4523
|
-
payload: {
|
|
4524
|
-
method: 'firmwareUpdateV4',
|
|
4525
|
-
},
|
|
4526
|
-
});
|
|
4527
|
-
(method as any).device = stubDevice({
|
|
4528
|
-
originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
4529
|
-
features: { bootloader_mode: false, capabilities: [] },
|
|
4530
|
-
isBootloader: () => false,
|
|
4531
|
-
probeProtocolV2RuntimeState: jest.fn().mockResolvedValue({
|
|
4532
|
-
mode: 'bootloader',
|
|
4533
|
-
bootloaderMode: true,
|
|
4534
|
-
}),
|
|
4535
|
-
});
|
|
4536
|
-
(method as any).protocolV2Reboot = jest.fn().mockResolvedValue({
|
|
4537
|
-
message: 'Device rebooted successfully',
|
|
4538
|
-
});
|
|
4539
|
-
(method as any).checkDeviceToBootloader = jest.fn();
|
|
4540
|
-
const typedCall = jest.fn().mockImplementation((requestType: string) => {
|
|
4541
|
-
if (requestType === 'DeviceInfoGet') {
|
|
4542
|
-
return Promise.resolve({
|
|
4543
|
-
type: 'DeviceInfo',
|
|
4544
|
-
message: protocolV2BootloaderDeviceInfo,
|
|
4545
|
-
});
|
|
4546
|
-
}
|
|
4547
|
-
if (requestType === 'ProtocolInfoRequest') {
|
|
4548
|
-
return Promise.resolve({
|
|
4549
|
-
type: 'ProtocolInfo',
|
|
4550
|
-
message: protocolV2BootloaderInfo,
|
|
4551
|
-
});
|
|
4552
|
-
}
|
|
4553
|
-
return Promise.reject(new Error(`unexpected call ${requestType}`));
|
|
4554
|
-
});
|
|
4555
|
-
const reconnectProtocolV2Device = jest.fn().mockImplementation(() => {
|
|
4556
|
-
(method as any).device.isBootloader = () => true;
|
|
4557
|
-
return Promise.resolve();
|
|
4173
|
+
const reconnectProtocolV2Device = jest.fn().mockImplementation(() => {
|
|
4174
|
+
(method as any).device.isBootloader = () => true;
|
|
4175
|
+
return Promise.resolve();
|
|
4558
4176
|
});
|
|
4559
4177
|
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
4560
4178
|
(method as any).device.getCommands = () => ({ typedCall });
|
|
@@ -4631,43 +4249,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4631
4249
|
expect(method.postTipMessage).toHaveBeenCalledWith('GoToBootloaderSuccess');
|
|
4632
4250
|
});
|
|
4633
4251
|
|
|
4634
|
-
test('keeps an ambiguous legacy runtime on the direct loader update path', async () => {
|
|
4635
|
-
const method = new FirmwareUpdateV4({
|
|
4636
|
-
id: 1,
|
|
4637
|
-
payload: {
|
|
4638
|
-
method: 'firmwareUpdateV4',
|
|
4639
|
-
},
|
|
4640
|
-
});
|
|
4641
|
-
(method as any).device = stubDevice({
|
|
4642
|
-
originalDescriptor: { id: 'usb-id', path: 'legacy-path', protocolType: 'V2' },
|
|
4643
|
-
state: {
|
|
4644
|
-
raw: {
|
|
4645
|
-
protocolV2ProtocolInfo: {
|
|
4646
|
-
version: 1,
|
|
4647
|
-
build_fingerprint: '',
|
|
4648
|
-
supported_messages: [],
|
|
4649
|
-
protobuf_definition: null,
|
|
4650
|
-
},
|
|
4651
|
-
},
|
|
4652
|
-
},
|
|
4653
|
-
features: {
|
|
4654
|
-
deviceType: 'pro2',
|
|
4655
|
-
mode: 'bootloader',
|
|
4656
|
-
bootloaderMode: true,
|
|
4657
|
-
capabilities: [],
|
|
4658
|
-
},
|
|
4659
|
-
isBootloader: () => true,
|
|
4660
|
-
isRomloader: () => false,
|
|
4661
|
-
});
|
|
4662
|
-
(method as any).protocolV2Reboot = jest.fn();
|
|
4663
|
-
method.postTipMessage = jest.fn();
|
|
4664
|
-
|
|
4665
|
-
await expect((method as any).enterProtocolV2BootloaderMode()).resolves.toBe(false);
|
|
4666
|
-
|
|
4667
|
-
expect((method as any).protocolV2LegacyDirectUpdate).toBe(true);
|
|
4668
|
-
expect((method as any).protocolV2Reboot).not.toHaveBeenCalled();
|
|
4669
|
-
});
|
|
4670
|
-
|
|
4671
4252
|
test('keeps Protocol V2 romloader active before firmware transfer', async () => {
|
|
4672
4253
|
const method = new FirmwareUpdateV4({
|
|
4673
4254
|
id: 1,
|
|
@@ -5300,86 +4881,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5300
4881
|
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
5301
4882
|
});
|
|
5302
4883
|
|
|
5303
|
-
test('reboots a legacy App only after the direct update endpoint is explicitly unavailable', async () => {
|
|
5304
|
-
const method = new FirmwareUpdateV4({
|
|
5305
|
-
id: 1,
|
|
5306
|
-
payload: {
|
|
5307
|
-
method: 'firmwareUpdateV4',
|
|
5308
|
-
},
|
|
5309
|
-
});
|
|
5310
|
-
const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
|
|
5311
|
-
const appTypedCall = jest
|
|
5312
|
-
.fn()
|
|
5313
|
-
.mockRejectedValueOnce(new Error('Failure_InvalidMessage,Handler not registered'));
|
|
5314
|
-
const bootloaderTypedCall = jest
|
|
5315
|
-
.fn()
|
|
5316
|
-
.mockResolvedValueOnce({ type: 'Success', message: { message: '' } });
|
|
5317
|
-
let activeCommands = { typedCall: appTypedCall };
|
|
5318
|
-
|
|
5319
|
-
(method as any).device = stubDevice({
|
|
5320
|
-
getCommands: () => activeCommands,
|
|
5321
|
-
});
|
|
5322
|
-
(method as any).protocolV2LegacyDirectUpdate = true;
|
|
5323
|
-
(method as any).rebootProtocolV2ToBootloader = jest.fn().mockImplementation(() => {
|
|
5324
|
-
activeCommands = { typedCall: bootloaderTypedCall };
|
|
5325
|
-
return Promise.resolve(true);
|
|
5326
|
-
});
|
|
5327
|
-
method.postTipMessage = jest.fn();
|
|
5328
|
-
method.postProgressMessage = jest.fn();
|
|
5329
|
-
|
|
5330
|
-
await expect((method as any).protocolV2StartFirmwareUpdate({ targets })).resolves.toEqual({
|
|
5331
|
-
type: 'Success',
|
|
5332
|
-
message: { message: '' },
|
|
5333
|
-
});
|
|
5334
|
-
|
|
5335
|
-
expect(appTypedCall).toHaveBeenCalledTimes(1);
|
|
5336
|
-
expect(appTypedCall).toHaveBeenCalledWith(
|
|
5337
|
-
'DeviceFirmwareUpdateRequest',
|
|
5338
|
-
'Success',
|
|
5339
|
-
{ targets },
|
|
5340
|
-
{ timeoutMs: 180000 }
|
|
5341
|
-
);
|
|
5342
|
-
expect(bootloaderTypedCall).toHaveBeenCalledTimes(1);
|
|
5343
|
-
expect(bootloaderTypedCall).toHaveBeenCalledWith(
|
|
5344
|
-
'DeviceFirmwareUpdateRequest',
|
|
5345
|
-
'Success',
|
|
5346
|
-
{ targets },
|
|
5347
|
-
{ timeoutMs: 180000 }
|
|
5348
|
-
);
|
|
5349
|
-
expect((method as any).rebootProtocolV2ToBootloader).toHaveBeenCalledTimes(1);
|
|
5350
|
-
expect(method.postTipMessage).toHaveBeenCalledWith('FirmwareUpdating');
|
|
5351
|
-
expect(method.postProgressMessage).toHaveBeenCalledWith(0, 'installingFirmware');
|
|
5352
|
-
});
|
|
5353
|
-
|
|
5354
|
-
test('does not replay a legacy direct update after a link failure', async () => {
|
|
5355
|
-
const method = new FirmwareUpdateV4({
|
|
5356
|
-
id: 1,
|
|
5357
|
-
payload: {
|
|
5358
|
-
method: 'firmwareUpdateV4',
|
|
5359
|
-
},
|
|
5360
|
-
});
|
|
5361
|
-
const typedCall = jest.fn().mockRejectedValue(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
|
|
5362
|
-
|
|
5363
|
-
(method as any).device = stubDevice({
|
|
5364
|
-
getCommands: () => ({ typedCall }),
|
|
5365
|
-
});
|
|
5366
|
-
(method as any).protocolV2LegacyDirectUpdate = true;
|
|
5367
|
-
(method as any).rebootProtocolV2ToBootloader = jest.fn();
|
|
5368
|
-
method.postTipMessage = jest.fn();
|
|
5369
|
-
method.postProgressMessage = jest.fn();
|
|
5370
|
-
|
|
5371
|
-
await expect(
|
|
5372
|
-
(method as any).protocolV2StartFirmwareUpdate({
|
|
5373
|
-
targets: [{ target_id: 4, path: 'vol0:/application_p1.bin' }],
|
|
5374
|
-
})
|
|
5375
|
-
).rejects.toThrow('LIBUSB_TRANSFER_TIMED_OUT');
|
|
5376
|
-
|
|
5377
|
-
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
5378
|
-
expect((method as any).rebootProtocolV2ToBootloader).not.toHaveBeenCalled();
|
|
5379
|
-
expect(method.postTipMessage).not.toHaveBeenCalled();
|
|
5380
|
-
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
5381
|
-
});
|
|
5382
|
-
|
|
5383
4884
|
test('polls only firmware status while Protocol V2 bootloader is installing', async () => {
|
|
5384
4885
|
const method = new FirmwareUpdateV4({
|
|
5385
4886
|
id: 1,
|
|
@@ -5442,7 +4943,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5442
4943
|
const deviceInfo = { hw: { serial_no: 'PRO2-PHYSICAL-1' } };
|
|
5443
4944
|
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
|
|
5444
4945
|
(method as any).probeProtocolV2NormalMode = jest.fn().mockResolvedValue(true);
|
|
5445
|
-
(method as any).protocolV2InstallAckReceived = true;
|
|
5446
4946
|
method.postProgressMessage = jest.fn();
|
|
5447
4947
|
|
|
5448
4948
|
await (method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
@@ -5481,7 +4981,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5481
4981
|
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
5482
4982
|
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
|
|
5483
4983
|
(method as any).probeProtocolV2NormalMode = jest.fn().mockResolvedValue(true);
|
|
5484
|
-
(method as any).protocolV2InstallAckReceived = true;
|
|
5485
4984
|
method.postProgressMessage = jest.fn();
|
|
5486
4985
|
|
|
5487
4986
|
await expect(
|
|
@@ -5495,78 +4994,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5495
4994
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
5496
4995
|
});
|
|
5497
4996
|
|
|
5498
|
-
test('rejects normal mode without install ACK, target status, or a version change', async () => {
|
|
5499
|
-
const method = new FirmwareUpdateV4({
|
|
5500
|
-
id: 1,
|
|
5501
|
-
payload: {
|
|
5502
|
-
method: 'firmwareUpdateV4',
|
|
5503
|
-
},
|
|
5504
|
-
});
|
|
5505
|
-
const typedCall = jest
|
|
5506
|
-
.fn()
|
|
5507
|
-
.mockRejectedValue(new Error('Failure: Handler not registered for this message'));
|
|
5508
|
-
const deviceInfo = { hw: { serial_no: 'PRO2-PHYSICAL-1' } };
|
|
5509
|
-
let now = 0;
|
|
5510
|
-
jest.spyOn(Date, 'now').mockImplementation(() => now);
|
|
5511
|
-
jest.spyOn(global, 'setTimeout').mockImplementation(((callback: () => void) => {
|
|
5512
|
-
now += 31 * 1000;
|
|
5513
|
-
callback();
|
|
5514
|
-
return 0 as any;
|
|
5515
|
-
}) as typeof setTimeout);
|
|
5516
|
-
|
|
5517
|
-
(method as any).device = stubDevice({
|
|
5518
|
-
getCommands: () => ({ typedCall }),
|
|
5519
|
-
});
|
|
5520
|
-
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
5521
|
-
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
|
|
5522
|
-
(method as any).probeProtocolV2NormalMode = jest.fn().mockResolvedValue(true);
|
|
5523
|
-
method.postProgressMessage = jest.fn();
|
|
5524
|
-
|
|
5525
|
-
await expect(
|
|
5526
|
-
(method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
5527
|
-
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
5528
|
-
])
|
|
5529
|
-
).rejects.toMatchObject({ errorCode: HardwareErrorCode.FirmwareError });
|
|
5530
|
-
|
|
5531
|
-
expect(method.postProgressMessage).not.toHaveBeenCalledWith(100, 'installingFirmware');
|
|
5532
|
-
});
|
|
5533
|
-
|
|
5534
|
-
test('accepts ACK-less normal mode after the requested target version changes', async () => {
|
|
5535
|
-
const method = new FirmwareUpdateV4({
|
|
5536
|
-
id: 1,
|
|
5537
|
-
payload: {
|
|
5538
|
-
method: 'firmwareUpdateV4',
|
|
5539
|
-
},
|
|
5540
|
-
});
|
|
5541
|
-
const typedCall = jest
|
|
5542
|
-
.fn()
|
|
5543
|
-
.mockRejectedValue(new Error('Failure: Handler not registered for this message'));
|
|
5544
|
-
const deviceInfo = { hw: { serial_no: 'PRO2-PHYSICAL-1' } };
|
|
5545
|
-
const probeProtocolV2RuntimeState = jest.fn().mockResolvedValue({
|
|
5546
|
-
mode: 'normal',
|
|
5547
|
-
bootloaderMode: false,
|
|
5548
|
-
firmwareVersion: '2.0.0',
|
|
5549
|
-
});
|
|
5550
|
-
|
|
5551
|
-
(method as any).device = stubDevice({
|
|
5552
|
-
getCommands: () => ({ typedCall }),
|
|
5553
|
-
probeProtocolV2RuntimeState,
|
|
5554
|
-
});
|
|
5555
|
-
(method as any).protocolV2InstallBaselineVersions = new Map([[4, '1.0.0']]);
|
|
5556
|
-
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
5557
|
-
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
|
|
5558
|
-
method.postProgressMessage = jest.fn();
|
|
5559
|
-
|
|
5560
|
-
await expect(
|
|
5561
|
-
(method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
5562
|
-
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
5563
|
-
])
|
|
5564
|
-
).resolves.toBeUndefined();
|
|
5565
|
-
|
|
5566
|
-
expect(probeProtocolV2RuntimeState).toHaveBeenCalledWith(deviceInfo, 5000);
|
|
5567
|
-
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
5568
|
-
});
|
|
5569
|
-
|
|
5570
4997
|
test.each([
|
|
5571
4998
|
[{ mode: 'normal', bootloaderMode: false }, true],
|
|
5572
4999
|
[{ mode: 'bootloader', bootloaderMode: true }, false],
|
|
@@ -6128,7 +5555,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6128
5555
|
);
|
|
6129
5556
|
});
|
|
6130
5557
|
|
|
6131
|
-
test('
|
|
5558
|
+
test('passes bootloader, coprocessor, SE and app files to DeviceFirmwareUpdate targets', async () => {
|
|
6132
5559
|
const method = new FirmwareUpdateV4({
|
|
6133
5560
|
id: 1,
|
|
6134
5561
|
payload: {
|
|
@@ -6157,72 +5584,54 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6157
5584
|
.mockResolvedValue(undefined);
|
|
6158
5585
|
|
|
6159
5586
|
await (method as any).executeProtocolV2Update({
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
// deliberately after components to prove Core does not synthesize boot-first.
|
|
6163
|
-
installItems: [
|
|
5587
|
+
bootloaderBinary: new Uint8Array([4, 5]).buffer,
|
|
5588
|
+
fwBinaryMap: [
|
|
6164
5589
|
{
|
|
6165
|
-
fileName: '
|
|
6166
|
-
binary: new Uint8Array([
|
|
6167
|
-
targetId:
|
|
6168
|
-
kind: 'firmware',
|
|
5590
|
+
fileName: 'coprocessor.bin',
|
|
5591
|
+
binary: new Uint8Array([6]).buffer,
|
|
5592
|
+
targetId: 6,
|
|
6169
5593
|
},
|
|
6170
5594
|
{
|
|
6171
5595
|
fileName: 'se01.bin',
|
|
6172
5596
|
binary: new Uint8Array([7]).buffer,
|
|
6173
5597
|
targetId: 7,
|
|
6174
|
-
kind: 'firmware',
|
|
6175
|
-
},
|
|
6176
|
-
{
|
|
6177
|
-
fileName: 'bootloader.bin',
|
|
6178
|
-
binary: new Uint8Array([4, 5]).buffer,
|
|
6179
|
-
targetId: 3,
|
|
6180
|
-
kind: 'bootloader',
|
|
6181
5598
|
},
|
|
6182
5599
|
{
|
|
6183
|
-
fileName: '
|
|
6184
|
-
binary: new Uint8Array([
|
|
6185
|
-
targetId:
|
|
6186
|
-
kind: 'firmware',
|
|
5600
|
+
fileName: 'application_p1.bin',
|
|
5601
|
+
binary: new Uint8Array([8]).buffer,
|
|
5602
|
+
targetId: 4,
|
|
6187
5603
|
},
|
|
6188
5604
|
],
|
|
6189
5605
|
});
|
|
6190
5606
|
|
|
6191
5607
|
expect(writtenPaths).toEqual([
|
|
6192
|
-
'vol0:/application_p1.bin',
|
|
6193
|
-
'vol0:/se01.bin',
|
|
6194
5608
|
'vol0:/bootloader.bin',
|
|
6195
5609
|
'vol0:/coprocessor.bin',
|
|
5610
|
+
'vol0:/se01.bin',
|
|
5611
|
+
'vol0:/application_p1.bin',
|
|
6196
5612
|
]);
|
|
6197
5613
|
expect((method as any).verifyProtocolV2StagedFile).toHaveBeenCalledTimes(4);
|
|
6198
5614
|
expect((method as any).verifyProtocolV2StagedFile).toHaveBeenNthCalledWith(
|
|
6199
5615
|
2,
|
|
6200
|
-
'vol0:/
|
|
5616
|
+
'vol0:/coprocessor.bin',
|
|
6201
5617
|
1
|
|
6202
5618
|
);
|
|
6203
|
-
expect((method as any).
|
|
6204
|
-
expect((method as any).protocolV2StartFirmwareUpdate).
|
|
6205
|
-
|
|
5619
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(2);
|
|
5620
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
|
|
5621
|
+
targets: [{ target_id: 3, path: 'vol0:/bootloader.bin' }],
|
|
5622
|
+
});
|
|
5623
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
6206
5624
|
targets: [
|
|
6207
|
-
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
6208
|
-
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
6209
|
-
{ target_id: 3, path: 'vol0:/bootloader.bin' },
|
|
6210
5625
|
{ target_id: 6, path: 'vol0:/coprocessor.bin' },
|
|
5626
|
+
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
5627
|
+
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
6211
5628
|
],
|
|
6212
5629
|
});
|
|
6213
|
-
expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalledTimes(1);
|
|
6214
|
-
expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalledWith([
|
|
6215
|
-
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
6216
|
-
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
6217
|
-
{ target_id: 3, path: 'vol0:/bootloader.bin' },
|
|
6218
|
-
{ target_id: 6, path: 'vol0:/coprocessor.bin' },
|
|
6219
|
-
]);
|
|
6220
|
-
expect((method as any).exitProtocolV2BootloaderToNormal).not.toHaveBeenCalled();
|
|
6221
5630
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'transferData');
|
|
6222
|
-
expect((method as any).
|
|
5631
|
+
expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalled();
|
|
6223
5632
|
});
|
|
6224
5633
|
|
|
6225
|
-
test('
|
|
5634
|
+
test('announces separate resource and component transfer phases', async () => {
|
|
6226
5635
|
const method = new FirmwareUpdateV4({
|
|
6227
5636
|
id: 1,
|
|
6228
5637
|
payload: {
|
|
@@ -6279,24 +5688,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6279
5688
|
],
|
|
6280
5689
|
});
|
|
6281
5690
|
|
|
6282
|
-
expect(
|
|
6283
|
-
expect(method.postTipMessage).toHaveBeenCalledTimes(2);
|
|
5691
|
+
expect(method.postTipMessage).toHaveBeenCalledTimes(3);
|
|
6284
5692
|
expect(method.postTipMessage).toHaveBeenNthCalledWith(1, 'StartTransferData');
|
|
6285
|
-
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, '
|
|
5693
|
+
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'StartTransferData');
|
|
5694
|
+
expect(method.postTipMessage).toHaveBeenNthCalledWith(3, 'ConfirmOnDevice');
|
|
6286
5695
|
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
|
|
6287
5696
|
1,
|
|
6288
|
-
expect.objectContaining({ processedSize: 0, totalSize:
|
|
5697
|
+
expect.objectContaining({ processedSize: 0, totalSize: 2 })
|
|
6289
5698
|
);
|
|
6290
5699
|
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
|
|
6291
5700
|
2,
|
|
6292
|
-
expect.objectContaining({ processedSize:
|
|
5701
|
+
expect.objectContaining({ processedSize: 0, totalSize: 1 })
|
|
6293
5702
|
);
|
|
6294
|
-
expect(method.postProgressMessage).toHaveBeenCalledTimes(1);
|
|
6295
|
-
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'transferData');
|
|
6296
|
-
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(1);
|
|
6297
|
-
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
|
|
6298
|
-
targets: [{ target_id: 4, path: 'vol0:/application_p1.bin' }],
|
|
6299
|
-
});
|
|
6300
5703
|
expect((method as any).isProtocolV2ResourceBundleUpToDate).toHaveBeenCalledWith(
|
|
6301
5704
|
expect.objectContaining({
|
|
6302
5705
|
version: [1, 2, 3],
|
|
@@ -6306,7 +5709,50 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6306
5709
|
);
|
|
6307
5710
|
});
|
|
6308
5711
|
|
|
6309
|
-
test('
|
|
5712
|
+
test('installs and verifies bootloader before syncing manifest resources', () => {
|
|
5713
|
+
const method = new FirmwareUpdateV4({
|
|
5714
|
+
id: 1,
|
|
5715
|
+
payload: { method: 'firmwareUpdateV4' },
|
|
5716
|
+
});
|
|
5717
|
+
const phases = (method as any).buildProtocolV2ExecutionPhases({
|
|
5718
|
+
installSources: [{ kind: 'bootloader' }, { kind: 'component' }],
|
|
5719
|
+
resourceSources: [{ kind: 'resource' }],
|
|
5720
|
+
});
|
|
5721
|
+
|
|
5722
|
+
expect(phases.map((phase: { kind: string }) => phase.kind)).toEqual([
|
|
5723
|
+
'bootloader-install',
|
|
5724
|
+
'bootloader-verify',
|
|
5725
|
+
'resource-sync',
|
|
5726
|
+
'component-install',
|
|
5727
|
+
'final-verify',
|
|
5728
|
+
]);
|
|
5729
|
+
});
|
|
5730
|
+
|
|
5731
|
+
test('stages the mounted boot resource before any firmware install phase', () => {
|
|
5732
|
+
const method = new FirmwareUpdateV4({
|
|
5733
|
+
id: 1,
|
|
5734
|
+
payload: { method: 'firmwareUpdateV4' },
|
|
5735
|
+
});
|
|
5736
|
+
const bootResource = {
|
|
5737
|
+
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
5738
|
+
};
|
|
5739
|
+
const phases = (method as any).buildProtocolV2ExecutionPhases({
|
|
5740
|
+
installSources: [{ kind: 'bootloader' }, { kind: 'component' }],
|
|
5741
|
+
resourceSources: [bootResource, { devicePath: 'vol0:/resource/images/images.okpkg' }],
|
|
5742
|
+
});
|
|
5743
|
+
|
|
5744
|
+
expect(phases.map((phase: { kind: string }) => phase.kind)).toEqual([
|
|
5745
|
+
'resource-sync',
|
|
5746
|
+
'bootloader-install',
|
|
5747
|
+
'bootloader-verify',
|
|
5748
|
+
'resource-sync',
|
|
5749
|
+
'component-install',
|
|
5750
|
+
'final-verify',
|
|
5751
|
+
]);
|
|
5752
|
+
expect(phases[0].resourceSources).toEqual([bootResource]);
|
|
5753
|
+
});
|
|
5754
|
+
|
|
5755
|
+
test('removes stale boot resource staging before a component-only reboot', async () => {
|
|
6310
5756
|
const method = new FirmwareUpdateV4({
|
|
6311
5757
|
id: 1,
|
|
6312
5758
|
payload: { method: 'firmwareUpdateV4' },
|
|
@@ -6333,18 +5779,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6333
5779
|
events.push('transfer-component');
|
|
6334
5780
|
return Promise.resolve();
|
|
6335
5781
|
});
|
|
6336
|
-
(method as any).
|
|
6337
|
-
events.push('
|
|
6338
|
-
return Promise.resolve(
|
|
5782
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockImplementation(() => {
|
|
5783
|
+
events.push('reboot-normal');
|
|
5784
|
+
return Promise.resolve();
|
|
6339
5785
|
});
|
|
5786
|
+
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
6340
5787
|
|
|
6341
5788
|
await (method as any).executeProtocolV2SourceUpdate({
|
|
6342
5789
|
installSources: [{ kind: 'firmware' }],
|
|
6343
5790
|
resourceSources: [],
|
|
6344
5791
|
});
|
|
6345
5792
|
|
|
6346
|
-
expect((
|
|
6347
|
-
expect(events).toEqual(['delete-stale-staging', 'transfer-component', 'final-verify']);
|
|
5793
|
+
expect(events).toEqual(['delete-stale-staging', 'transfer-component', 'reboot-normal']);
|
|
6348
5794
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'FilesystemFileDelete', 'Success', {
|
|
6349
5795
|
path: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
6350
5796
|
});
|
|
@@ -7691,9 +7137,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7691
7137
|
method.postProgressMessage = jest.fn();
|
|
7692
7138
|
(method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
|
|
7693
7139
|
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
7694
|
-
(method as any).ensureProtocolV2BootResourceStagingIsEmpty = jest
|
|
7695
|
-
.fn()
|
|
7696
|
-
.mockResolvedValue(undefined);
|
|
7697
7140
|
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
7698
7141
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
7699
7142
|
|
|
@@ -7820,86 +7263,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7820
7263
|
});
|
|
7821
7264
|
});
|
|
7822
7265
|
|
|
7823
|
-
test('sends one monotonic device transfer progress range across multiple files', async () => {
|
|
7824
|
-
const method = new FirmwareUpdateV4({
|
|
7825
|
-
id: 1,
|
|
7826
|
-
payload: {
|
|
7827
|
-
method: 'firmwareUpdateV4',
|
|
7828
|
-
},
|
|
7829
|
-
});
|
|
7830
|
-
const typedCall = jest.fn(
|
|
7831
|
-
(
|
|
7832
|
-
_name: string,
|
|
7833
|
-
_resType: string,
|
|
7834
|
-
params: { file: { offset: number; data: { byteLength: number } } }
|
|
7835
|
-
) =>
|
|
7836
|
-
Promise.resolve({
|
|
7837
|
-
type: 'FilesystemFile',
|
|
7838
|
-
message: {
|
|
7839
|
-
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
7840
|
-
},
|
|
7841
|
-
})
|
|
7842
|
-
);
|
|
7843
|
-
|
|
7844
|
-
(method as any).device = stubDevice({
|
|
7845
|
-
getCommands: () => ({ typedCall }),
|
|
7846
|
-
getCurrentDeviceType: () => 'pro2',
|
|
7847
|
-
toMessageObject: () => ({ connectId: 'firmware-device' }),
|
|
7848
|
-
});
|
|
7849
|
-
method.postMessage = jest.fn();
|
|
7850
|
-
method.postTipMessage = jest.fn();
|
|
7851
|
-
method.postProgressMessage = jest.fn();
|
|
7852
|
-
(method as any).isProtocolV2ResourceBundleUpToDate = jest.fn().mockResolvedValue(false);
|
|
7853
|
-
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
7854
|
-
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
7855
|
-
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
7856
|
-
.fn()
|
|
7857
|
-
.mockResolvedValue(undefined);
|
|
7858
|
-
|
|
7859
|
-
const resourceSource = await openFirmwareByteSource({
|
|
7860
|
-
binary: new Uint8Array(4097).buffer,
|
|
7861
|
-
});
|
|
7862
|
-
const firmwareSource = await openFirmwareByteSource({
|
|
7863
|
-
binary: new Uint8Array(4097).buffer,
|
|
7864
|
-
});
|
|
7865
|
-
try {
|
|
7866
|
-
await (method as any).executeProtocolV2TransferPhase({
|
|
7867
|
-
resourceSources: [
|
|
7868
|
-
{
|
|
7869
|
-
name: 'images.okpkg',
|
|
7870
|
-
source: resourceSource,
|
|
7871
|
-
devicePath: 'vol0:/resource/images/images.okpkg',
|
|
7872
|
-
},
|
|
7873
|
-
],
|
|
7874
|
-
installSources: [
|
|
7875
|
-
{
|
|
7876
|
-
fileName: 'application_p1.bin',
|
|
7877
|
-
source: firmwareSource,
|
|
7878
|
-
targetId: 4,
|
|
7879
|
-
kind: 'firmware',
|
|
7880
|
-
},
|
|
7881
|
-
],
|
|
7882
|
-
});
|
|
7883
|
-
} finally {
|
|
7884
|
-
await resourceSource?.close();
|
|
7885
|
-
await firmwareSource?.close();
|
|
7886
|
-
}
|
|
7887
|
-
|
|
7888
|
-
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
7889
|
-
const deviceProgress = writePayloads.map(payload => payload.ui_percentage);
|
|
7890
|
-
expect(deviceProgress).toEqual([0, 50, 99, 100]);
|
|
7891
|
-
expect(deviceProgress.filter(progress => progress === 0)).toHaveLength(1);
|
|
7892
|
-
expect(deviceProgress.filter(progress => progress === 100)).toHaveLength(1);
|
|
7893
|
-
expect(method.postTipMessage).toHaveBeenCalledTimes(2);
|
|
7894
|
-
expect(method.postTipMessage).toHaveBeenNthCalledWith(1, 'StartTransferData');
|
|
7895
|
-
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'ConfirmOnDevice');
|
|
7896
|
-
expect(
|
|
7897
|
-
(method.postProgressMessage as jest.Mock).mock.calls.filter(
|
|
7898
|
-
([progress, progressType]) => progress === 100 && progressType === 'transferData'
|
|
7899
|
-
)
|
|
7900
|
-
).toHaveLength(1);
|
|
7901
|
-
});
|
|
7902
|
-
|
|
7903
7266
|
// TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
|
|
7904
7267
|
// writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
|
|
7905
7268
|
// streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
|