@onekeyfe/hd-core 1.2.0-alpha.104 → 1.2.0-alpha.106
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 +100 -28
- package/__tests__/check-all-firmware-release-protocol-v2.test.ts +0 -2
- package/__tests__/device-lifecycle-events.test.ts +113 -2
- package/__tests__/device-pool-state.test.ts +1 -5
- package/__tests__/deviceSettings.test.ts +0 -1
- package/__tests__/firmware-update/firmware-update-v4-install-poll.test.ts +145 -0
- package/__tests__/get-device-state.test.ts +0 -50
- package/__tests__/logBlockEvent.test.ts +13 -1
- package/__tests__/protocol-v2.test.ts +466 -126
- package/__tests__/refresh-device-state.test.ts +0 -1
- package/__tests__/search-devices.test.ts +0 -2
- package/dist/api/FirmwareUpdateV4.d.ts +7 -3
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/GetDeviceState.d.ts.map +1 -1
- package/dist/api/SearchDevices.d.ts.map +1 -1
- package/dist/api/firmware/protocolV2Release.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -3
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/device/DeviceCommands.d.ts.map +1 -1
- package/dist/device/DevicePool.d.ts +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 +1 -6
- package/dist/index.js +260 -196
- package/dist/protocols/protocol-v2/features.d.ts +1 -1
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/types/api/getDeviceState.d.ts +0 -1
- package/dist/types/api/getDeviceState.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +262 -172
- package/src/api/GetDeviceState.ts +0 -1
- package/src/api/SearchDevices.ts +0 -2
- package/src/api/firmware/protocolV2Release.ts +0 -1
- package/src/core/index.ts +6 -6
- package/src/device/Device.ts +27 -63
- package/src/device/DeviceCommands.ts +4 -14
- package/src/device/DevicePool.ts +3 -11
- package/src/events/logBlockEvent.ts +14 -1
- package/src/protocols/protocol-v2/features.ts +9 -2
- package/src/types/api/getDeviceState.ts +0 -2
- package/src/utils/deviceSettings.ts +1 -1
- package/__tests__/protocol-v2-legacy-recovery.test.ts +0 -29
- package/dist/core/protocolV2LegacyRecovery.d.ts +0 -5
- package/dist/core/protocolV2LegacyRecovery.d.ts.map +0 -1
- package/src/core/protocolV2LegacyRecovery.ts +0 -12
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
|
-
|
|
3
|
-
import { EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
+
import { EDeviceType, EFirmwareType, ERRORS, HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
4
3
|
import { sha256 } from '@noble/hashes/sha256';
|
|
5
4
|
import { bytesToHex } from '@noble/hashes/utils';
|
|
6
5
|
import JSZip from 'jszip';
|
|
@@ -908,6 +907,34 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
908
907
|
).toBeUndefined();
|
|
909
908
|
});
|
|
910
909
|
|
|
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
|
+
|
|
911
938
|
test('keeps the Protocol V2 main wallet on the default empty-passphrase context', async () => {
|
|
912
939
|
const features = normalizeProtocolV2Features(descriptor as any);
|
|
913
940
|
features.firmwareVersion = '1.2.3';
|
|
@@ -2756,19 +2783,35 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
2756
2783
|
});
|
|
2757
2784
|
});
|
|
2758
2785
|
|
|
2759
|
-
test('syncs Protocol V2 cached features
|
|
2786
|
+
test('syncs Protocol V2 cached features and renegotiates the adopted command channel', async () => {
|
|
2787
|
+
const typedCall = jest.fn().mockResolvedValue({ message: protocolV2ApplicationInfo });
|
|
2760
2788
|
const cached = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
2761
2789
|
(cached as any).features = {
|
|
2762
2790
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
2763
2791
|
deviceId: null,
|
|
2764
2792
|
serialNo: 'CACHED-SERIAL',
|
|
2765
2793
|
};
|
|
2794
|
+
(cached as any).commands = { typedCall };
|
|
2766
2795
|
|
|
2767
2796
|
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
|
+
);
|
|
2768
2804
|
current.updateFromCache(cached);
|
|
2769
2805
|
|
|
2806
|
+
await expect(current.ensureProtocolV2RuntimeContext()).resolves.toEqual(
|
|
2807
|
+
protocolV2ApplicationInfo
|
|
2808
|
+
);
|
|
2809
|
+
|
|
2770
2810
|
expect(current.getCurrentDeviceId()).toBeUndefined();
|
|
2771
2811
|
expect(current.getCurrentSerialNo()).toBe('CACHED-SERIAL');
|
|
2812
|
+
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {
|
|
2813
|
+
eventless_wallet_session: true,
|
|
2814
|
+
});
|
|
2772
2815
|
expect(current.toMessageObject()).toMatchObject({
|
|
2773
2816
|
serialNo: 'CACHED-SERIAL',
|
|
2774
2817
|
uuid: 'CACHED-SERIAL',
|
|
@@ -3253,33 +3296,43 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
3253
3296
|
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
3254
3297
|
});
|
|
3255
3298
|
|
|
3256
|
-
test('
|
|
3299
|
+
test('initializes a legacy Protocol V2 bootloader without compatibility options', async () => {
|
|
3257
3300
|
const device = Device.fromDescriptor({
|
|
3258
3301
|
path: 'usb-path',
|
|
3259
3302
|
protocolType: 'V2',
|
|
3260
3303
|
} as any);
|
|
3261
|
-
const typedCall = jest
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
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
|
+
});
|
|
3270
3322
|
(device as any).commands = { typedCall };
|
|
3271
3323
|
|
|
3272
|
-
await
|
|
3273
|
-
device.probeProtocolV2RuntimeState({
|
|
3274
|
-
hw: { Device_type: DeviceType.PRO2, serial_no: 'PR2SERIAL' },
|
|
3275
|
-
fw: { application: { version: '1.2.3' } },
|
|
3276
|
-
})
|
|
3277
|
-
).rejects.toMatchObject({ errorCode: HardwareErrorCode.DeviceInitializeFailed });
|
|
3324
|
+
await device.initialize();
|
|
3278
3325
|
|
|
3279
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
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
|
+
});
|
|
3280
3333
|
});
|
|
3281
3334
|
|
|
3282
|
-
test('uses DeviceStatusGet to identify App mode for
|
|
3335
|
+
test('uses DeviceStatusGet to identify App mode for legacy ProtocolInfo', async () => {
|
|
3283
3336
|
const device = Device.fromDescriptor({
|
|
3284
3337
|
path: 'usb-path',
|
|
3285
3338
|
protocolType: 'V2',
|
|
@@ -3306,8 +3359,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
3306
3359
|
hw: { Device_type: DeviceType.PRO2, serial_no: 'PR2SERIAL' },
|
|
3307
3360
|
fw: { application: { version: '1.2.3' } },
|
|
3308
3361
|
},
|
|
3309
|
-
5000
|
|
3310
|
-
{ allowLegacyProtocolV2ProtocolInfo: true }
|
|
3362
|
+
5000
|
|
3311
3363
|
);
|
|
3312
3364
|
|
|
3313
3365
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
@@ -3332,38 +3384,32 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
3332
3384
|
});
|
|
3333
3385
|
});
|
|
3334
3386
|
|
|
3335
|
-
test('maps
|
|
3387
|
+
test('maps legacy bootloader metadata without calling DeviceStatusGet', async () => {
|
|
3336
3388
|
const device = Device.fromDescriptor({
|
|
3337
3389
|
path: 'usb-path',
|
|
3338
3390
|
protocolType: 'V2',
|
|
3339
3391
|
} as any);
|
|
3340
|
-
const typedCall = jest
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
},
|
|
3350
|
-
})
|
|
3351
|
-
.mockRejectedValueOnce(new Error('Failure_UnexpectedMessage,Unknown message'));
|
|
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
|
+
});
|
|
3352
3401
|
(device as any).commands = { typedCall };
|
|
3353
3402
|
|
|
3354
|
-
await device.probeProtocolV2RuntimeState(
|
|
3355
|
-
{
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
},
|
|
3359
|
-
undefined,
|
|
3360
|
-
{ allowLegacyProtocolV2ProtocolInfo: true }
|
|
3361
|
-
);
|
|
3403
|
+
await device.probeProtocolV2RuntimeState({
|
|
3404
|
+
hw: { Device_type: DeviceType.NEO, serial_no: 'NEOSERIAL' },
|
|
3405
|
+
fw: { bootloader: { version: '0.2.0' } },
|
|
3406
|
+
});
|
|
3362
3407
|
|
|
3363
3408
|
expect(typedCall).toHaveBeenNthCalledWith(1, 'ProtocolInfoRequest', 'ProtocolInfo', {
|
|
3364
3409
|
eventless_wallet_session: true,
|
|
3365
3410
|
});
|
|
3366
|
-
expect(typedCall).
|
|
3411
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
3412
|
+
expect(typedCall).not.toHaveBeenCalledWith('DeviceStatusGet', 'DeviceStatus', {});
|
|
3367
3413
|
expect(device.features).toMatchObject({
|
|
3368
3414
|
deviceType: 'neo',
|
|
3369
3415
|
mode: 'bootloader',
|
|
@@ -3372,6 +3418,37 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
3372
3418
|
});
|
|
3373
3419
|
});
|
|
3374
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
|
+
|
|
3375
3452
|
test('does not reinterpret a state update error as runtime detection failure', async () => {
|
|
3376
3453
|
const device = Device.fromDescriptor({
|
|
3377
3454
|
path: 'usb-path',
|
|
@@ -4276,7 +4353,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4276
4353
|
).toEqual([3, 4]);
|
|
4277
4354
|
});
|
|
4278
4355
|
|
|
4279
|
-
test('
|
|
4356
|
+
test('keeps external-only resource updates on the prepared host path', async () => {
|
|
4280
4357
|
const method = new FirmwareUpdateV4({
|
|
4281
4358
|
id: 1,
|
|
4282
4359
|
payload: {
|
|
@@ -4286,6 +4363,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4286
4363
|
},
|
|
4287
4364
|
});
|
|
4288
4365
|
method.init();
|
|
4366
|
+
jest.spyOn(DataManager, 'getSettings').mockReturnValue('external-only' as never);
|
|
4289
4367
|
|
|
4290
4368
|
(method as any).device = stubDevice({
|
|
4291
4369
|
originalDescriptor: { id: 'usb-id', path: 'app-path', protocolType: 'V2' },
|
|
@@ -4313,6 +4391,132 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4313
4391
|
expect((method as any).enterProtocolV2BootloaderMode).not.toHaveBeenCalled();
|
|
4314
4392
|
});
|
|
4315
4393
|
|
|
4394
|
+
test('downloads Protocol V2 resource archives in SDK-managed mode', async () => {
|
|
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)));
|
|
4404
|
+
const method = new FirmwareUpdateV4({
|
|
4405
|
+
id: 1,
|
|
4406
|
+
payload: {
|
|
4407
|
+
method: 'firmwareUpdateV4',
|
|
4408
|
+
platform: 'ext',
|
|
4409
|
+
targetsToUpdate: ['app_v1', 'resource'],
|
|
4410
|
+
},
|
|
4411
|
+
});
|
|
4412
|
+
method.init();
|
|
4413
|
+
|
|
4414
|
+
(method as any).device = stubDevice({
|
|
4415
|
+
originalDescriptor: { id: 'usb-id', path: 'app-path', protocolType: 'V2' },
|
|
4416
|
+
features: {
|
|
4417
|
+
deviceType: 'pro2',
|
|
4418
|
+
firmwareVersion: '1.0.0',
|
|
4419
|
+
mode: 'normal',
|
|
4420
|
+
bootloaderMode: false,
|
|
4421
|
+
capabilities: [],
|
|
4422
|
+
},
|
|
4423
|
+
getCurrentDeviceType: () => 'pro2',
|
|
4424
|
+
isBootloader: () => false,
|
|
4425
|
+
isRomloader: () => false,
|
|
4426
|
+
});
|
|
4427
|
+
(method as any).captureProtocolV2PhysicalIdentity = jest.fn().mockResolvedValue(undefined);
|
|
4428
|
+
(method as any).prepareRemoteProtocolV2Binaries = jest.fn().mockResolvedValue({
|
|
4429
|
+
bootloaderBinary: null,
|
|
4430
|
+
fwBinaryMap: [
|
|
4431
|
+
{
|
|
4432
|
+
fileName: applicationP1InstallItem.fileName,
|
|
4433
|
+
binary: applicationP1Binary,
|
|
4434
|
+
targetId: applicationP1InstallItem.targetId,
|
|
4435
|
+
},
|
|
4436
|
+
],
|
|
4437
|
+
installItems: [applicationP1InstallItem],
|
|
4438
|
+
});
|
|
4439
|
+
const release = jest.fn();
|
|
4440
|
+
(method as any).prepareProtocolV2LocalMemoryHost = jest.fn().mockResolvedValue({ release });
|
|
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,
|
|
4450
|
+
});
|
|
4451
|
+
const getSysResourceBinarySpy = jest
|
|
4452
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
4453
|
+
.mockResolvedValue({ binary: resourceArchiveBinary });
|
|
4454
|
+
|
|
4455
|
+
await expect(method.run()).resolves.toBe('sdk-managed-resource-result');
|
|
4456
|
+
|
|
4457
|
+
expect(forceReloadDataSpy).toHaveBeenCalledWith({
|
|
4458
|
+
requireResources: true,
|
|
4459
|
+
resourceDeviceType: EDeviceType.Pro2,
|
|
4460
|
+
});
|
|
4461
|
+
expect(getSysResourceBinarySpy).toHaveBeenCalledWith('https://example.com/pro2-resource.zip');
|
|
4462
|
+
expect((method as any).params.resourceArchiveBinary).toBe(resourceArchiveBinary);
|
|
4463
|
+
expect((method as any).prepareProtocolV2LocalMemoryHost).toHaveBeenCalledWith({
|
|
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
|
+
|
|
4316
4520
|
test('reboots Protocol V2 normal-mode device to bootloader before transfer', async () => {
|
|
4317
4521
|
const method = new FirmwareUpdateV4({
|
|
4318
4522
|
id: 1,
|
|
@@ -5116,9 +5320,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5116
5320
|
getCommands: () => activeCommands,
|
|
5117
5321
|
});
|
|
5118
5322
|
(method as any).protocolV2LegacyDirectUpdate = true;
|
|
5119
|
-
(method as any).rebootProtocolV2ToBootloader = jest.fn().mockImplementation(
|
|
5323
|
+
(method as any).rebootProtocolV2ToBootloader = jest.fn().mockImplementation(() => {
|
|
5120
5324
|
activeCommands = { typedCall: bootloaderTypedCall };
|
|
5121
|
-
return true;
|
|
5325
|
+
return Promise.resolve(true);
|
|
5122
5326
|
});
|
|
5123
5327
|
method.postTipMessage = jest.fn();
|
|
5124
5328
|
method.postProgressMessage = jest.fn();
|
|
@@ -5238,6 +5442,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5238
5442
|
const deviceInfo = { hw: { serial_no: 'PRO2-PHYSICAL-1' } };
|
|
5239
5443
|
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
|
|
5240
5444
|
(method as any).probeProtocolV2NormalMode = jest.fn().mockResolvedValue(true);
|
|
5445
|
+
(method as any).protocolV2InstallAckReceived = true;
|
|
5241
5446
|
method.postProgressMessage = jest.fn();
|
|
5242
5447
|
|
|
5243
5448
|
await (method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
@@ -5276,6 +5481,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5276
5481
|
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
5277
5482
|
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
|
|
5278
5483
|
(method as any).probeProtocolV2NormalMode = jest.fn().mockResolvedValue(true);
|
|
5484
|
+
(method as any).protocolV2InstallAckReceived = true;
|
|
5279
5485
|
method.postProgressMessage = jest.fn();
|
|
5280
5486
|
|
|
5281
5487
|
await expect(
|
|
@@ -5289,6 +5495,78 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5289
5495
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
5290
5496
|
});
|
|
5291
5497
|
|
|
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
|
+
|
|
5292
5570
|
test.each([
|
|
5293
5571
|
[{ mode: 'normal', bootloaderMode: false }, true],
|
|
5294
5572
|
[{ mode: 'bootloader', bootloaderMode: true }, false],
|
|
@@ -5304,9 +5582,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5304
5582
|
(method as any).device = stubDevice({ probeProtocolV2RuntimeState });
|
|
5305
5583
|
|
|
5306
5584
|
await expect((method as any).probeProtocolV2NormalMode(deviceInfo)).resolves.toBe(expected);
|
|
5307
|
-
expect(probeProtocolV2RuntimeState).toHaveBeenCalledWith(deviceInfo, 5000
|
|
5308
|
-
allowLegacyProtocolV2ProtocolInfo: true,
|
|
5309
|
-
});
|
|
5585
|
+
expect(probeProtocolV2RuntimeState).toHaveBeenCalledWith(deviceInfo, 5000);
|
|
5310
5586
|
});
|
|
5311
5587
|
|
|
5312
5588
|
test('keeps polling when the firmware status handler is missing in loader mode', async () => {
|
|
@@ -5852,7 +6128,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5852
6128
|
);
|
|
5853
6129
|
});
|
|
5854
6130
|
|
|
5855
|
-
test('
|
|
6131
|
+
test('preserves component-first target order for the firmware-owned loader handoff', async () => {
|
|
5856
6132
|
const method = new FirmwareUpdateV4({
|
|
5857
6133
|
id: 1,
|
|
5858
6134
|
payload: {
|
|
@@ -5881,54 +6157,72 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5881
6157
|
.mockResolvedValue(undefined);
|
|
5882
6158
|
|
|
5883
6159
|
await (method as any).executeProtocolV2Update({
|
|
5884
|
-
|
|
5885
|
-
|
|
6160
|
+
// The active bootloader installs its component runners in records order and
|
|
6161
|
+
// leaves the bootloader record pending for romloader after reboot. Keep boot
|
|
6162
|
+
// deliberately after components to prove Core does not synthesize boot-first.
|
|
6163
|
+
installItems: [
|
|
5886
6164
|
{
|
|
5887
|
-
fileName: '
|
|
5888
|
-
binary: new Uint8Array([
|
|
5889
|
-
targetId:
|
|
6165
|
+
fileName: 'application_p1.bin',
|
|
6166
|
+
binary: new Uint8Array([8]).buffer,
|
|
6167
|
+
targetId: 4,
|
|
6168
|
+
kind: 'firmware',
|
|
5890
6169
|
},
|
|
5891
6170
|
{
|
|
5892
6171
|
fileName: 'se01.bin',
|
|
5893
6172
|
binary: new Uint8Array([7]).buffer,
|
|
5894
6173
|
targetId: 7,
|
|
6174
|
+
kind: 'firmware',
|
|
5895
6175
|
},
|
|
5896
6176
|
{
|
|
5897
|
-
fileName: '
|
|
5898
|
-
binary: new Uint8Array([
|
|
5899
|
-
targetId:
|
|
6177
|
+
fileName: 'bootloader.bin',
|
|
6178
|
+
binary: new Uint8Array([4, 5]).buffer,
|
|
6179
|
+
targetId: 3,
|
|
6180
|
+
kind: 'bootloader',
|
|
6181
|
+
},
|
|
6182
|
+
{
|
|
6183
|
+
fileName: 'coprocessor.bin',
|
|
6184
|
+
binary: new Uint8Array([6]).buffer,
|
|
6185
|
+
targetId: 6,
|
|
6186
|
+
kind: 'firmware',
|
|
5900
6187
|
},
|
|
5901
6188
|
],
|
|
5902
6189
|
});
|
|
5903
6190
|
|
|
5904
6191
|
expect(writtenPaths).toEqual([
|
|
6192
|
+
'vol0:/application_p1.bin',
|
|
6193
|
+
'vol0:/se01.bin',
|
|
5905
6194
|
'vol0:/bootloader.bin',
|
|
5906
6195
|
'vol0:/coprocessor.bin',
|
|
5907
|
-
'vol0:/se01.bin',
|
|
5908
|
-
'vol0:/application_p1.bin',
|
|
5909
6196
|
]);
|
|
5910
6197
|
expect((method as any).verifyProtocolV2StagedFile).toHaveBeenCalledTimes(4);
|
|
5911
6198
|
expect((method as any).verifyProtocolV2StagedFile).toHaveBeenNthCalledWith(
|
|
5912
6199
|
2,
|
|
5913
|
-
'vol0:/
|
|
6200
|
+
'vol0:/se01.bin',
|
|
5914
6201
|
1
|
|
5915
6202
|
);
|
|
5916
|
-
expect((method as any).
|
|
5917
|
-
expect((method as any).protocolV2StartFirmwareUpdate).
|
|
5918
|
-
|
|
5919
|
-
});
|
|
5920
|
-
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
6203
|
+
expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
|
|
6204
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(1);
|
|
6205
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
|
|
5921
6206
|
targets: [
|
|
5922
|
-
{ target_id: 6, path: 'vol0:/coprocessor.bin' },
|
|
5923
|
-
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
5924
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
|
+
{ target_id: 6, path: 'vol0:/coprocessor.bin' },
|
|
5925
6211
|
],
|
|
5926
6212
|
});
|
|
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();
|
|
5927
6221
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'transferData');
|
|
5928
|
-
expect((method as any).
|
|
6222
|
+
expect((method as any).completeProtocolV2FinalVerification).toHaveBeenCalledTimes(1);
|
|
5929
6223
|
});
|
|
5930
6224
|
|
|
5931
|
-
test('
|
|
6225
|
+
test('uses one global transfer range for resources and firmware', async () => {
|
|
5932
6226
|
const method = new FirmwareUpdateV4({
|
|
5933
6227
|
id: 1,
|
|
5934
6228
|
payload: {
|
|
@@ -5985,18 +6279,24 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5985
6279
|
],
|
|
5986
6280
|
});
|
|
5987
6281
|
|
|
5988
|
-
expect(method.
|
|
6282
|
+
expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
|
|
6283
|
+
expect(method.postTipMessage).toHaveBeenCalledTimes(2);
|
|
5989
6284
|
expect(method.postTipMessage).toHaveBeenNthCalledWith(1, 'StartTransferData');
|
|
5990
|
-
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, '
|
|
5991
|
-
expect(method.postTipMessage).toHaveBeenNthCalledWith(3, 'ConfirmOnDevice');
|
|
6285
|
+
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'ConfirmOnDevice');
|
|
5992
6286
|
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
|
|
5993
6287
|
1,
|
|
5994
|
-
expect.objectContaining({ processedSize: 0, totalSize:
|
|
6288
|
+
expect.objectContaining({ processedSize: 0, totalSize: 3 })
|
|
5995
6289
|
);
|
|
5996
6290
|
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
|
|
5997
6291
|
2,
|
|
5998
|
-
expect.objectContaining({ processedSize:
|
|
6292
|
+
expect.objectContaining({ processedSize: 2, totalSize: 3 })
|
|
5999
6293
|
);
|
|
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
|
+
});
|
|
6000
6300
|
expect((method as any).isProtocolV2ResourceBundleUpToDate).toHaveBeenCalledWith(
|
|
6001
6301
|
expect.objectContaining({
|
|
6002
6302
|
version: [1, 2, 3],
|
|
@@ -6006,50 +6306,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6006
6306
|
);
|
|
6007
6307
|
});
|
|
6008
6308
|
|
|
6009
|
-
test('
|
|
6010
|
-
const method = new FirmwareUpdateV4({
|
|
6011
|
-
id: 1,
|
|
6012
|
-
payload: { method: 'firmwareUpdateV4' },
|
|
6013
|
-
});
|
|
6014
|
-
const phases = (method as any).buildProtocolV2ExecutionPhases({
|
|
6015
|
-
installSources: [{ kind: 'bootloader' }, { kind: 'component' }],
|
|
6016
|
-
resourceSources: [{ kind: 'resource' }],
|
|
6017
|
-
});
|
|
6018
|
-
|
|
6019
|
-
expect(phases.map((phase: { kind: string }) => phase.kind)).toEqual([
|
|
6020
|
-
'bootloader-install',
|
|
6021
|
-
'bootloader-verify',
|
|
6022
|
-
'resource-sync',
|
|
6023
|
-
'component-install',
|
|
6024
|
-
'final-verify',
|
|
6025
|
-
]);
|
|
6026
|
-
});
|
|
6027
|
-
|
|
6028
|
-
test('stages the mounted boot resource before any firmware install phase', () => {
|
|
6029
|
-
const method = new FirmwareUpdateV4({
|
|
6030
|
-
id: 1,
|
|
6031
|
-
payload: { method: 'firmwareUpdateV4' },
|
|
6032
|
-
});
|
|
6033
|
-
const bootResource = {
|
|
6034
|
-
devicePath: 'vol0:/loaders/bootloader/boot_resource.okpkg',
|
|
6035
|
-
};
|
|
6036
|
-
const phases = (method as any).buildProtocolV2ExecutionPhases({
|
|
6037
|
-
installSources: [{ kind: 'bootloader' }, { kind: 'component' }],
|
|
6038
|
-
resourceSources: [bootResource, { devicePath: 'vol0:/resource/images/images.okpkg' }],
|
|
6039
|
-
});
|
|
6040
|
-
|
|
6041
|
-
expect(phases.map((phase: { kind: string }) => phase.kind)).toEqual([
|
|
6042
|
-
'resource-sync',
|
|
6043
|
-
'bootloader-install',
|
|
6044
|
-
'bootloader-verify',
|
|
6045
|
-
'resource-sync',
|
|
6046
|
-
'component-install',
|
|
6047
|
-
'final-verify',
|
|
6048
|
-
]);
|
|
6049
|
-
expect(phases[0].resourceSources).toEqual([bootResource]);
|
|
6050
|
-
});
|
|
6051
|
-
|
|
6052
|
-
test('removes stale boot resource staging before a component-only reboot', async () => {
|
|
6309
|
+
test('removes stale boot resource staging before the combined transfer', async () => {
|
|
6053
6310
|
const method = new FirmwareUpdateV4({
|
|
6054
6311
|
id: 1,
|
|
6055
6312
|
payload: { method: 'firmwareUpdateV4' },
|
|
@@ -6076,18 +6333,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6076
6333
|
events.push('transfer-component');
|
|
6077
6334
|
return Promise.resolve();
|
|
6078
6335
|
});
|
|
6079
|
-
(method as any).
|
|
6080
|
-
events.push('
|
|
6081
|
-
return Promise.resolve();
|
|
6336
|
+
(method as any).completeProtocolV2FinalVerification = jest.fn().mockImplementation(() => {
|
|
6337
|
+
events.push('final-verify');
|
|
6338
|
+
return Promise.resolve({});
|
|
6082
6339
|
});
|
|
6083
|
-
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
6084
6340
|
|
|
6085
6341
|
await (method as any).executeProtocolV2SourceUpdate({
|
|
6086
6342
|
installSources: [{ kind: 'firmware' }],
|
|
6087
6343
|
resourceSources: [],
|
|
6088
6344
|
});
|
|
6089
6345
|
|
|
6090
|
-
expect(
|
|
6346
|
+
expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
|
|
6347
|
+
expect(events).toEqual(['delete-stale-staging', 'transfer-component', 'final-verify']);
|
|
6091
6348
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'FilesystemFileDelete', 'Success', {
|
|
6092
6349
|
path: 'vol0:/loaders/bootloader/boot_resource.okpkg.staging',
|
|
6093
6350
|
});
|
|
@@ -7434,6 +7691,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7434
7691
|
method.postProgressMessage = jest.fn();
|
|
7435
7692
|
(method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
|
|
7436
7693
|
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
7694
|
+
(method as any).ensureProtocolV2BootResourceStagingIsEmpty = jest
|
|
7695
|
+
.fn()
|
|
7696
|
+
.mockResolvedValue(undefined);
|
|
7437
7697
|
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
7438
7698
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
7439
7699
|
|
|
@@ -7560,6 +7820,86 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
7560
7820
|
});
|
|
7561
7821
|
});
|
|
7562
7822
|
|
|
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
|
+
|
|
7563
7903
|
// TODO(#850/#855): PR #855 added resume-on-retry and per-chunk retry on the
|
|
7564
7904
|
// writeProtocolV2File path. PR #850 replaced that path with FirmwareByteSource
|
|
7565
7905
|
// streaming (protocolV2SourceUpdateProcess), which restarts a failed transfer from
|