@onekeyfe/hd-core 1.2.0-alpha.169 → 1.2.0-alpha.170
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__/firmware-update/firmware-update-v4-install-poll.test.ts +58 -12
- package/__tests__/protocol-v2-resources.test.ts +7 -0
- package/__tests__/protocol-v2.test.ts +191 -106
- package/dist/api/FirmwareUpdateV4.d.ts +2 -1
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/device/DeviceConnector.d.ts.map +1 -1
- package/dist/index.js +88 -73
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FirmwareUpdateV4.ts +115 -86
- package/src/core/index.ts +5 -2
- package/src/device/DeviceConnector.ts +2 -3
- package/src/protocols/protocol-v2/resources.ts +9 -1
|
@@ -14,7 +14,7 @@ describe('FirmwareUpdateV4 install polling', () => {
|
|
|
14
14
|
jest.restoreAllMocks();
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
test('uses Stage,
|
|
17
|
+
test('uses Stage, waits for the empty Request response, then polls status', async () => {
|
|
18
18
|
const method = new FirmwareUpdateV4({
|
|
19
19
|
id: 1,
|
|
20
20
|
payload: {
|
|
@@ -26,10 +26,7 @@ describe('FirmwareUpdateV4 install polling', () => {
|
|
|
26
26
|
const typedCall = jest
|
|
27
27
|
.fn()
|
|
28
28
|
.mockResolvedValueOnce({ type: 'Success', message: {} })
|
|
29
|
-
.mockResolvedValueOnce({
|
|
30
|
-
type: 'DeviceFirmwareUpdateStatus',
|
|
31
|
-
message: { records: [] },
|
|
32
|
-
})
|
|
29
|
+
.mockResolvedValueOnce({ type: 'Success', message: {} })
|
|
33
30
|
.mockResolvedValueOnce({
|
|
34
31
|
type: 'DeviceFirmwareUpdateStatus',
|
|
35
32
|
message: {
|
|
@@ -42,10 +39,9 @@ describe('FirmwareUpdateV4 install polling', () => {
|
|
|
42
39
|
],
|
|
43
40
|
},
|
|
44
41
|
});
|
|
45
|
-
const call = jest.fn().mockResolvedValue({ type: 'WriteCompleted', message: {} });
|
|
46
42
|
|
|
47
43
|
method.device = {
|
|
48
|
-
getCommands: () => ({ typedCall
|
|
44
|
+
getCommands: () => ({ typedCall }),
|
|
49
45
|
createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
|
|
50
46
|
toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2-ble' }),
|
|
51
47
|
setCancelableAction: jest.fn(),
|
|
@@ -68,9 +64,12 @@ describe('FirmwareUpdateV4 install polling', () => {
|
|
|
68
64
|
await firmwareUpdate.waitForProtocolV2FirmwareUpdateComplete(targets);
|
|
69
65
|
|
|
70
66
|
expect(typedCall.mock.calls[0]).toEqual(['DeviceFirmwareUpdateStage', 'Success', { targets }]);
|
|
71
|
-
expect(
|
|
72
|
-
expect(
|
|
73
|
-
expect(
|
|
67
|
+
expect(typedCall.mock.calls[1]).toEqual(['DeviceFirmwareUpdateRequest', 'Success', {}]);
|
|
68
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(1, 'installingFirmware');
|
|
69
|
+
expect(typedCall.mock.calls[2]?.[0]).toBe('DeviceFirmwareUpdateStatusGet');
|
|
70
|
+
expect(typedCall.mock.invocationCallOrder[1]).toBeLessThan(
|
|
71
|
+
typedCall.mock.invocationCallOrder[2]
|
|
72
|
+
);
|
|
74
73
|
});
|
|
75
74
|
|
|
76
75
|
test('does not send Request when Stage is rejected', async () => {
|
|
@@ -83,10 +82,9 @@ describe('FirmwareUpdateV4 install polling', () => {
|
|
|
83
82
|
});
|
|
84
83
|
const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
|
|
85
84
|
const typedCall = jest.fn().mockRejectedValue(new Error('stage rejected'));
|
|
86
|
-
const call = jest.fn();
|
|
87
85
|
|
|
88
86
|
method.device = {
|
|
89
|
-
getCommands: () => ({ typedCall
|
|
87
|
+
getCommands: () => ({ typedCall }),
|
|
90
88
|
} as unknown as Device;
|
|
91
89
|
|
|
92
90
|
await expect(
|
|
@@ -97,7 +95,55 @@ describe('FirmwareUpdateV4 install polling', () => {
|
|
|
97
95
|
).protocolV2StartFirmwareUpdate({ targets })
|
|
98
96
|
).rejects.toThrow('stage rejected');
|
|
99
97
|
|
|
98
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('does not enter install state when the device cancels the Request', async () => {
|
|
102
|
+
const method = new FirmwareUpdateV4({
|
|
103
|
+
id: 1,
|
|
104
|
+
payload: {
|
|
105
|
+
method: 'firmwareUpdateV4',
|
|
106
|
+
connectId: 'pro2-usb',
|
|
107
|
+
},
|
|
108
|
+
});
|
|
109
|
+
const targets = [{ target_id: 4, path: 'vol0:/application_p1.bin' }];
|
|
110
|
+
const actionCancelledError = ERRORS.TypedError(HardwareErrorCode.ActionCancelled);
|
|
111
|
+
const typedCall = jest.fn().mockImplementation((type: string) => {
|
|
112
|
+
if (type === 'DeviceFirmwareUpdateRequest') {
|
|
113
|
+
return Promise.reject(actionCancelledError);
|
|
114
|
+
}
|
|
115
|
+
if (type === 'DeviceFirmwareUpdateStatusGet') {
|
|
116
|
+
return Promise.resolve({ type: 'Success', message: {} });
|
|
117
|
+
}
|
|
118
|
+
return Promise.resolve({ type: 'Success', message: {} });
|
|
119
|
+
});
|
|
120
|
+
const call = jest.fn().mockResolvedValue({
|
|
121
|
+
type: 'Failure',
|
|
122
|
+
message: { code: 'Failure_ActionCancelled' },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
method.device = {
|
|
126
|
+
getCommands: () => ({ typedCall, call }),
|
|
127
|
+
createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
|
|
128
|
+
toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2-usb' }),
|
|
129
|
+
setCancelableAction: jest.fn(),
|
|
130
|
+
} as unknown as Device;
|
|
131
|
+
method.postMessage = jest.fn();
|
|
132
|
+
method.postProgressMessage = jest.fn();
|
|
133
|
+
|
|
134
|
+
await expect(
|
|
135
|
+
(
|
|
136
|
+
method as unknown as {
|
|
137
|
+
protocolV2StartFirmwareUpdate: (params: { targets: typeof targets }) => Promise<void>;
|
|
138
|
+
}
|
|
139
|
+
).protocolV2StartFirmwareUpdate({ targets })
|
|
140
|
+
).rejects.toMatchObject({
|
|
141
|
+
errorCode: HardwareErrorCode.ActionCancelled,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
|
|
100
145
|
expect(call).not.toHaveBeenCalled();
|
|
146
|
+
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
101
147
|
});
|
|
102
148
|
|
|
103
149
|
test('does not hide an explicit workflow cancellation during status polling', async () => {
|
|
@@ -109,6 +109,11 @@ describe('Pro2 resource configuration', () => {
|
|
|
109
109
|
expect(
|
|
110
110
|
isProtocolV2ResourceArchiveEntryName('bundles/firmware_logo-pro2-prod_resource-signed.okpkg')
|
|
111
111
|
).toBe(true);
|
|
112
|
+
expect(
|
|
113
|
+
isProtocolV2ResourceArchiveEntryName(
|
|
114
|
+
'pro2-prod_resource/bundles/firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
115
|
+
)
|
|
116
|
+
).toBe(true);
|
|
112
117
|
expect(
|
|
113
118
|
isProtocolV2ResourceArchiveEntryName(
|
|
114
119
|
'__MACOSX/pro2-prod_resource 2/bundles/._firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
@@ -119,6 +124,8 @@ describe('Pro2 resource configuration', () => {
|
|
|
119
124
|
'bundles/._firmware_logo-pro2-prod_resource-signed.okpkg'
|
|
120
125
|
)
|
|
121
126
|
).toBe(false);
|
|
127
|
+
expect(isProtocolV2ResourceArchiveEntryName('pro2-prod_resource/manifest.json')).toBe(false);
|
|
128
|
+
expect(isProtocolV2ResourceArchiveEntryName('pro2-prod_resource/.DS_Store')).toBe(false);
|
|
122
129
|
});
|
|
123
130
|
|
|
124
131
|
test('reads the version, hashes, and direct device path from a RESC package', () => {
|
|
@@ -4925,6 +4925,59 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4925
4925
|
expect(initialize).not.toHaveBeenCalled();
|
|
4926
4926
|
});
|
|
4927
4927
|
|
|
4928
|
+
test('reacquires the same WebUSB device without a protocol probe during install recovery', async () => {
|
|
4929
|
+
const method = new FirmwareUpdateV4({
|
|
4930
|
+
id: 1,
|
|
4931
|
+
payload: {
|
|
4932
|
+
method: 'firmwareUpdateV4',
|
|
4933
|
+
},
|
|
4934
|
+
});
|
|
4935
|
+
const acquire = jest.fn().mockResolvedValue('usb-path');
|
|
4936
|
+
const enumerate = jest.fn().mockResolvedValue({
|
|
4937
|
+
descriptors: [
|
|
4938
|
+
{ id: 'other-usb', path: 'other-usb', protocolType: 'V2' },
|
|
4939
|
+
{ id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
4940
|
+
],
|
|
4941
|
+
});
|
|
4942
|
+
const commands = { disposed: true, mainId: '' };
|
|
4943
|
+
const device = stubDevice({
|
|
4944
|
+
originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
4945
|
+
deviceConnector: { acquire, enumerate },
|
|
4946
|
+
updateDescriptor: jest.fn(),
|
|
4947
|
+
acquire: jest.fn(),
|
|
4948
|
+
commands,
|
|
4949
|
+
getCommands: () => commands,
|
|
4950
|
+
mainId: 'old-usb-path',
|
|
4951
|
+
});
|
|
4952
|
+
const getDevices = jest.spyOn(DevicePool, 'getDevices');
|
|
4953
|
+
const isBrowserWebUsb = jest.spyOn(DataManager, 'isBrowserWebUsb').mockReturnValue(false);
|
|
4954
|
+
const isDesktopWebUsb = jest.spyOn(DataManager, 'isDesktopWebUsb').mockReturnValue(true);
|
|
4955
|
+
(method as any).device = device;
|
|
4956
|
+
(method as any).protocolV2ExpectedPath = 'usb-path';
|
|
4957
|
+
|
|
4958
|
+
try {
|
|
4959
|
+
await (method as any).reconnectProtocolV2Device({ skipProtocolProbe: true });
|
|
4960
|
+
} finally {
|
|
4961
|
+
getDevices.mockRestore();
|
|
4962
|
+
isBrowserWebUsb.mockRestore();
|
|
4963
|
+
isDesktopWebUsb.mockRestore();
|
|
4964
|
+
}
|
|
4965
|
+
|
|
4966
|
+
expect(getDevices).not.toHaveBeenCalled();
|
|
4967
|
+
expect(acquire).toHaveBeenCalledWith(
|
|
4968
|
+
'usb-path',
|
|
4969
|
+
null,
|
|
4970
|
+
undefined,
|
|
4971
|
+
'V2',
|
|
4972
|
+
undefined,
|
|
4973
|
+
undefined,
|
|
4974
|
+
true
|
|
4975
|
+
);
|
|
4976
|
+
expect(device.acquire).not.toHaveBeenCalled();
|
|
4977
|
+
expect(commands.disposed).toBe(false);
|
|
4978
|
+
expect(commands.mainId).toBe('usb-path');
|
|
4979
|
+
});
|
|
4980
|
+
|
|
4928
4981
|
test('scans and reacquires the same BLE peripheral without probing during install recovery', async () => {
|
|
4929
4982
|
const method = new FirmwareUpdateV4({
|
|
4930
4983
|
id: 1,
|
|
@@ -4945,7 +4998,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4945
4998
|
getCommands: () => commands,
|
|
4946
4999
|
});
|
|
4947
5000
|
|
|
4948
|
-
await (method as any).reconnectProtocolV2Device({
|
|
5001
|
+
await (method as any).reconnectProtocolV2Device({ skipProtocolProbe: true });
|
|
4949
5002
|
|
|
4950
5003
|
expect(enumerate).toHaveBeenCalledTimes(1);
|
|
4951
5004
|
expect(acquire).toHaveBeenCalledWith('ble-id', null, true, 'V2', undefined, undefined, true);
|
|
@@ -5372,7 +5425,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5372
5425
|
});
|
|
5373
5426
|
const targets = [{ target_id: 4, path: 'vol1:firmware.bin' }];
|
|
5374
5427
|
const typedCall = jest.fn().mockResolvedValue({ type: 'Success', message: {} });
|
|
5375
|
-
const call = jest.fn().mockResolvedValue({ type: 'WriteCompleted', message: {} });
|
|
5376
5428
|
const cancelDevice = jest.fn().mockResolvedValue(undefined);
|
|
5377
5429
|
let cancelableAction: (() => Promise<unknown>) | undefined;
|
|
5378
5430
|
const interaction = {
|
|
@@ -5385,7 +5437,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5385
5437
|
};
|
|
5386
5438
|
|
|
5387
5439
|
(method as any).device = stubDevice({
|
|
5388
|
-
getCommands: () => ({ typedCall,
|
|
5440
|
+
getCommands: () => ({ typedCall, cancelDevice }),
|
|
5389
5441
|
createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(interaction),
|
|
5390
5442
|
toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2' }),
|
|
5391
5443
|
setCancelableAction: jest.fn(action => {
|
|
@@ -5414,13 +5466,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5414
5466
|
interaction,
|
|
5415
5467
|
},
|
|
5416
5468
|
});
|
|
5417
|
-
expect(
|
|
5418
|
-
|
|
5469
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
|
|
5470
|
+
const requestCallIndex = typedCall.mock.calls.findIndex(
|
|
5471
|
+
([type]) => type === 'DeviceFirmwareUpdateRequest'
|
|
5472
|
+
);
|
|
5473
|
+
expect(typedCall.mock.invocationCallOrder[0]).toBeLessThan(
|
|
5474
|
+
typedCall.mock.invocationCallOrder[requestCallIndex]
|
|
5475
|
+
);
|
|
5419
5476
|
expect((method.postMessage as jest.Mock).mock.invocationCallOrder[0]).toBeLessThan(
|
|
5420
|
-
|
|
5477
|
+
typedCall.mock.invocationCallOrder[requestCallIndex]
|
|
5421
5478
|
);
|
|
5422
5479
|
expect(method.postTipMessage).not.toHaveBeenCalled();
|
|
5423
|
-
expect(method.postProgressMessage).
|
|
5480
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(1, 'installingFirmware');
|
|
5424
5481
|
await cancelableAction?.();
|
|
5425
5482
|
expect(cancelDevice).toHaveBeenCalledTimes(1);
|
|
5426
5483
|
});
|
|
@@ -5433,10 +5490,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5433
5490
|
},
|
|
5434
5491
|
});
|
|
5435
5492
|
const typedCall = jest.fn().mockRejectedValue(new Error('stage failed'));
|
|
5436
|
-
const call = jest.fn();
|
|
5437
5493
|
|
|
5438
5494
|
(method as any).device = stubDevice({
|
|
5439
|
-
getCommands: () => ({ typedCall
|
|
5495
|
+
getCommands: () => ({ typedCall }),
|
|
5440
5496
|
});
|
|
5441
5497
|
method.postMessage = jest.fn();
|
|
5442
5498
|
method.postTipMessage = jest.fn();
|
|
@@ -5448,24 +5504,28 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5448
5504
|
})
|
|
5449
5505
|
).rejects.toThrow('stage failed');
|
|
5450
5506
|
|
|
5451
|
-
expect(
|
|
5507
|
+
expect(typedCall).not.toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
|
|
5452
5508
|
expect(method.postMessage).not.toHaveBeenCalled();
|
|
5453
5509
|
expect(method.postTipMessage).not.toHaveBeenCalled();
|
|
5454
5510
|
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
5455
5511
|
});
|
|
5456
5512
|
|
|
5457
|
-
test('does not enter install state when
|
|
5513
|
+
test('does not enter install state when the empty request fails', async () => {
|
|
5458
5514
|
const method = new FirmwareUpdateV4({
|
|
5459
5515
|
id: 1,
|
|
5460
5516
|
payload: {
|
|
5461
5517
|
method: 'firmwareUpdateV4',
|
|
5462
5518
|
},
|
|
5463
5519
|
});
|
|
5464
|
-
const typedCall = jest.fn().
|
|
5465
|
-
|
|
5520
|
+
const typedCall = jest.fn().mockImplementation((type: string) => {
|
|
5521
|
+
if (type === 'DeviceFirmwareUpdateRequest') {
|
|
5522
|
+
return Promise.reject(new Error('request failed'));
|
|
5523
|
+
}
|
|
5524
|
+
return Promise.resolve({ type: 'Success', message: {} });
|
|
5525
|
+
});
|
|
5466
5526
|
|
|
5467
5527
|
(method as any).device = stubDevice({
|
|
5468
|
-
getCommands: () => ({ typedCall
|
|
5528
|
+
getCommands: () => ({ typedCall }),
|
|
5469
5529
|
createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
|
|
5470
5530
|
toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2' }),
|
|
5471
5531
|
});
|
|
@@ -5477,9 +5537,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5477
5537
|
(method as any).protocolV2StartFirmwareUpdate({
|
|
5478
5538
|
targets: [{ target_id: 4, path: 'vol0:/application_p1.bin' }],
|
|
5479
5539
|
})
|
|
5480
|
-
).rejects.toThrow('
|
|
5540
|
+
).rejects.toThrow('request failed');
|
|
5481
5541
|
|
|
5482
|
-
expect(
|
|
5542
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
|
|
5483
5543
|
expect(method.postTipMessage).not.toHaveBeenCalled();
|
|
5484
5544
|
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
5485
5545
|
});
|
|
@@ -5491,11 +5551,15 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5491
5551
|
method: 'firmwareUpdateV4',
|
|
5492
5552
|
},
|
|
5493
5553
|
});
|
|
5494
|
-
const typedCall = jest.fn().
|
|
5495
|
-
|
|
5554
|
+
const typedCall = jest.fn().mockImplementation((type: string) => {
|
|
5555
|
+
if (type === 'DeviceFirmwareUpdateRequest') {
|
|
5556
|
+
return Promise.reject(new Error('React Native BLE transport released'));
|
|
5557
|
+
}
|
|
5558
|
+
return Promise.resolve({ type: 'Success', message: {} });
|
|
5559
|
+
});
|
|
5496
5560
|
|
|
5497
5561
|
(method as any).device = stubDevice({
|
|
5498
|
-
getCommands: () => ({ typedCall
|
|
5562
|
+
getCommands: () => ({ typedCall }),
|
|
5499
5563
|
createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
|
|
5500
5564
|
toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2' }),
|
|
5501
5565
|
});
|
|
@@ -5508,7 +5572,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5508
5572
|
})
|
|
5509
5573
|
).resolves.toBeUndefined();
|
|
5510
5574
|
|
|
5511
|
-
expect(
|
|
5575
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
|
|
5512
5576
|
expect((method as any).protocolV2InstallNeedsBleReconnect).toBe(true);
|
|
5513
5577
|
});
|
|
5514
5578
|
|
|
@@ -5562,7 +5626,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5562
5626
|
setTimeoutSpy.mockRestore();
|
|
5563
5627
|
}
|
|
5564
5628
|
|
|
5565
|
-
expect(reconnectProtocolV2Device).toHaveBeenCalledWith({
|
|
5629
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledWith({ skipProtocolProbe: true });
|
|
5566
5630
|
expect(verifyProtocolV2ReconnectIdentity).not.toHaveBeenCalled();
|
|
5567
5631
|
expect(typedCall.mock.calls.map(callArgs => callArgs[0])).toEqual([
|
|
5568
5632
|
'DeviceFirmwareUpdateStatusGet',
|
|
@@ -5813,76 +5877,40 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5813
5877
|
expect(typedCall).toHaveBeenCalledTimes(3);
|
|
5814
5878
|
});
|
|
5815
5879
|
|
|
5816
|
-
test('ignores
|
|
5880
|
+
test('ignores leftover failed records while another leftover target is still pending', async () => {
|
|
5817
5881
|
const method = new FirmwareUpdateV4({
|
|
5818
5882
|
id: 1,
|
|
5819
5883
|
payload: {
|
|
5820
5884
|
method: 'firmwareUpdateV4',
|
|
5821
5885
|
},
|
|
5822
5886
|
});
|
|
5823
|
-
const targets = [
|
|
5824
|
-
{ target_id: 3, path: 'vol0:/bootloader.bin' },
|
|
5825
|
-
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
5826
|
-
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
5827
|
-
{ target_id: 8, path: 'vol0:/se02.bin' },
|
|
5828
|
-
];
|
|
5829
|
-
const staleRecords = [
|
|
5830
|
-
{
|
|
5831
|
-
target_id: 'FW_MGMT_TARGET_BOOTLOADER',
|
|
5832
|
-
status: 'FW_MGMT_UPDATER_TASK_STATUS_PENDING',
|
|
5833
|
-
payload_version: 0,
|
|
5834
|
-
path: 'vol0:/bootloader.bin',
|
|
5835
|
-
},
|
|
5836
|
-
{
|
|
5837
|
-
target_id: 'FW_MGMT_TARGET_APPLICATION_P1',
|
|
5838
|
-
status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED',
|
|
5839
|
-
payload_version: 0x010000,
|
|
5840
|
-
path: 'vol0:/application_p1.bin',
|
|
5841
|
-
},
|
|
5842
|
-
{
|
|
5843
|
-
target_id: 'FW_MGMT_TARGET_SE01',
|
|
5844
|
-
status: 'FW_MGMT_UPDATER_TASK_STATUS_FAILED_VERIFY',
|
|
5845
|
-
payload_version: 0,
|
|
5846
|
-
path: 'vol0:/se01.bin',
|
|
5847
|
-
},
|
|
5848
|
-
{
|
|
5849
|
-
target_id: 'FW_MGMT_TARGET_SE02',
|
|
5850
|
-
status: 'FW_MGMT_UPDATER_TASK_STATUS_FAILED_VERIFY',
|
|
5851
|
-
payload_version: 0,
|
|
5852
|
-
path: 'vol0:/se02.bin',
|
|
5853
|
-
},
|
|
5854
|
-
];
|
|
5855
5887
|
const typedCall = jest
|
|
5856
5888
|
.fn()
|
|
5857
|
-
.mockResolvedValueOnce({
|
|
5858
|
-
type: 'DeviceFirmwareUpdateStatus',
|
|
5859
|
-
message: { records: staleRecords },
|
|
5860
|
-
})
|
|
5861
5889
|
.mockResolvedValueOnce({
|
|
5862
5890
|
type: 'DeviceFirmwareUpdateStatus',
|
|
5863
5891
|
message: {
|
|
5864
|
-
records:
|
|
5892
|
+
records: [
|
|
5893
|
+
{ target_id: 3, status: 0, path: 'vol0:/bootloader.bin' },
|
|
5894
|
+
{ target_id: 4, status: 3, path: 'vol0:/application_p1.bin' },
|
|
5895
|
+
],
|
|
5865
5896
|
},
|
|
5866
5897
|
})
|
|
5867
5898
|
.mockResolvedValueOnce({
|
|
5868
5899
|
type: 'DeviceFirmwareUpdateStatus',
|
|
5869
5900
|
message: {
|
|
5870
5901
|
records: [
|
|
5871
|
-
{
|
|
5872
|
-
{
|
|
5873
|
-
{ ...targets[2], status: 0, payload_version: 0 },
|
|
5874
|
-
{ ...targets[3], status: 0, payload_version: 0 },
|
|
5902
|
+
{ target_id: 3, status: 0, path: 'vol0:/bootloader.bin' },
|
|
5903
|
+
{ target_id: 4, status: 0, path: 'vol0:/application_p1.bin' },
|
|
5875
5904
|
],
|
|
5876
5905
|
},
|
|
5877
5906
|
})
|
|
5878
5907
|
.mockResolvedValueOnce({
|
|
5879
5908
|
type: 'DeviceFirmwareUpdateStatus',
|
|
5880
5909
|
message: {
|
|
5881
|
-
records:
|
|
5882
|
-
|
|
5883
|
-
status: 2,
|
|
5884
|
-
|
|
5885
|
-
})),
|
|
5910
|
+
records: [
|
|
5911
|
+
{ target_id: 3, status: 2, path: 'vol0:/bootloader.bin' },
|
|
5912
|
+
{ target_id: 4, status: 2, path: 'vol0:/application_p1.bin' },
|
|
5913
|
+
],
|
|
5886
5914
|
},
|
|
5887
5915
|
});
|
|
5888
5916
|
const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
|
|
@@ -5897,54 +5925,57 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5897
5925
|
});
|
|
5898
5926
|
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
5899
5927
|
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
|
|
5900
|
-
(method as any).protocolV2InstallStatusBaseline = staleRecords;
|
|
5901
5928
|
method.postProgressMessage = jest.fn();
|
|
5902
5929
|
|
|
5903
5930
|
try {
|
|
5904
|
-
await
|
|
5905
|
-
|
|
5906
|
-
|
|
5931
|
+
await (method as any).waitForProtocolV2FirmwareUpdateComplete(
|
|
5932
|
+
[
|
|
5933
|
+
{ target_id: 3, path: 'vol0:/bootloader.bin' },
|
|
5934
|
+
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
5935
|
+
],
|
|
5936
|
+
true
|
|
5937
|
+
);
|
|
5907
5938
|
} finally {
|
|
5908
5939
|
setTimeoutSpy.mockRestore();
|
|
5909
5940
|
}
|
|
5910
5941
|
|
|
5911
|
-
expect(typedCall).toHaveBeenCalledTimes(
|
|
5942
|
+
expect(typedCall).toHaveBeenCalledTimes(3);
|
|
5912
5943
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
5913
5944
|
});
|
|
5914
5945
|
|
|
5915
|
-
test('reports
|
|
5946
|
+
test('reports failed only after that target was live in the current install', async () => {
|
|
5916
5947
|
const method = new FirmwareUpdateV4({
|
|
5917
5948
|
id: 1,
|
|
5918
5949
|
payload: {
|
|
5919
5950
|
method: 'firmwareUpdateV4',
|
|
5920
5951
|
},
|
|
5921
5952
|
});
|
|
5922
|
-
const targets = [
|
|
5923
|
-
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
5924
|
-
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
5925
|
-
];
|
|
5926
|
-
const staleRecords = [
|
|
5927
|
-
{ ...targets[0], status: 2, payload_version: 0x010000 },
|
|
5928
|
-
{ ...targets[1], status: 6, payload_version: 0 },
|
|
5929
|
-
];
|
|
5930
5953
|
const typedCall = jest
|
|
5931
5954
|
.fn()
|
|
5932
5955
|
.mockResolvedValueOnce({
|
|
5933
5956
|
type: 'DeviceFirmwareUpdateStatus',
|
|
5934
|
-
message: {
|
|
5957
|
+
message: {
|
|
5958
|
+
records: [
|
|
5959
|
+
{ target_id: 3, status: 0, path: 'vol0:/bootloader.bin' },
|
|
5960
|
+
{ target_id: 4, status: 3, path: 'vol0:/application_p1.bin' },
|
|
5961
|
+
],
|
|
5962
|
+
},
|
|
5935
5963
|
})
|
|
5936
5964
|
.mockResolvedValueOnce({
|
|
5937
5965
|
type: 'DeviceFirmwareUpdateStatus',
|
|
5938
5966
|
message: {
|
|
5939
|
-
records:
|
|
5967
|
+
records: [
|
|
5968
|
+
{ target_id: 3, status: 0, path: 'vol0:/bootloader.bin' },
|
|
5969
|
+
{ target_id: 4, status: 0, path: 'vol0:/application_p1.bin' },
|
|
5970
|
+
],
|
|
5940
5971
|
},
|
|
5941
5972
|
})
|
|
5942
5973
|
.mockResolvedValueOnce({
|
|
5943
5974
|
type: 'DeviceFirmwareUpdateStatus',
|
|
5944
5975
|
message: {
|
|
5945
5976
|
records: [
|
|
5946
|
-
{
|
|
5947
|
-
{
|
|
5977
|
+
{ target_id: 3, status: 0, path: 'vol0:/bootloader.bin' },
|
|
5978
|
+
{ target_id: 4, status: 3, path: 'vol0:/application_p1.bin' },
|
|
5948
5979
|
],
|
|
5949
5980
|
},
|
|
5950
5981
|
});
|
|
@@ -5960,14 +5991,19 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5960
5991
|
});
|
|
5961
5992
|
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
5962
5993
|
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
|
|
5963
|
-
(method as any).protocolV2InstallStatusBaseline = staleRecords;
|
|
5964
5994
|
|
|
5965
5995
|
try {
|
|
5966
5996
|
await expect(
|
|
5967
|
-
(method as any).waitForProtocolV2FirmwareUpdateComplete(
|
|
5997
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete(
|
|
5998
|
+
[
|
|
5999
|
+
{ target_id: 3, path: 'vol0:/bootloader.bin' },
|
|
6000
|
+
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
6001
|
+
],
|
|
6002
|
+
true
|
|
6003
|
+
)
|
|
5968
6004
|
).rejects.toMatchObject({
|
|
5969
6005
|
errorCode: HardwareErrorCode.FirmwareError,
|
|
5970
|
-
|
|
6006
|
+
message: 'Protocol V2 firmware install failed',
|
|
5971
6007
|
});
|
|
5972
6008
|
} finally {
|
|
5973
6009
|
setTimeoutSpy.mockRestore();
|
|
@@ -6119,6 +6155,54 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6119
6155
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'installingFirmware');
|
|
6120
6156
|
});
|
|
6121
6157
|
|
|
6158
|
+
test('uses the confirmed install request as evidence after the same device returns', async () => {
|
|
6159
|
+
const method = new FirmwareUpdateV4({
|
|
6160
|
+
id: 1,
|
|
6161
|
+
payload: {
|
|
6162
|
+
method: 'firmwareUpdateV4',
|
|
6163
|
+
},
|
|
6164
|
+
});
|
|
6165
|
+
const typedCall = jest
|
|
6166
|
+
.fn()
|
|
6167
|
+
.mockRejectedValueOnce(new Error('The USB device was disconnected'))
|
|
6168
|
+
.mockResolvedValueOnce({
|
|
6169
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
6170
|
+
message: { records: [] },
|
|
6171
|
+
});
|
|
6172
|
+
const deviceInfo = { hw: { serial_no: 'PRO2-PHYSICAL-1' } };
|
|
6173
|
+
const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
|
|
6174
|
+
callback: () => void
|
|
6175
|
+
) => {
|
|
6176
|
+
callback();
|
|
6177
|
+
return 0 as any;
|
|
6178
|
+
}) as typeof setTimeout);
|
|
6179
|
+
|
|
6180
|
+
(method as any).device = stubDevice({
|
|
6181
|
+
getCommands: () => ({ typedCall }),
|
|
6182
|
+
});
|
|
6183
|
+
(method as any).protocolV2InstallRequestConfirmed = true;
|
|
6184
|
+
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
6185
|
+
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(deviceInfo);
|
|
6186
|
+
(method as any).probeProtocolV2NormalMode = jest.fn().mockResolvedValue(true);
|
|
6187
|
+
method.postProgressMessage = jest.fn();
|
|
6188
|
+
|
|
6189
|
+
try {
|
|
6190
|
+
await expect(
|
|
6191
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete(
|
|
6192
|
+
[{ target_id: 4, path: 'vol0:/application_p1.bin' }],
|
|
6193
|
+
true
|
|
6194
|
+
)
|
|
6195
|
+
).resolves.toBeUndefined();
|
|
6196
|
+
} finally {
|
|
6197
|
+
setTimeoutSpy.mockRestore();
|
|
6198
|
+
}
|
|
6199
|
+
|
|
6200
|
+
expect((method as any).reconnectProtocolV2Device).toHaveBeenCalledWith({
|
|
6201
|
+
skipProtocolProbe: true,
|
|
6202
|
+
});
|
|
6203
|
+
expect(method.postProgressMessage).toHaveBeenLastCalledWith(100, 'installingFirmware');
|
|
6204
|
+
});
|
|
6205
|
+
|
|
6122
6206
|
test.each([
|
|
6123
6207
|
[{ mode: 'normal', bootloaderMode: false }, true],
|
|
6124
6208
|
[{ mode: 'notInitialized', bootloaderMode: false }, true],
|
|
@@ -8987,24 +9071,25 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
8987
9071
|
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([1960, 1]);
|
|
8988
9072
|
});
|
|
8989
9073
|
|
|
8990
|
-
test('keeps
|
|
9074
|
+
test('keeps confirmation active until the empty Request responds, then enters install', async () => {
|
|
8991
9075
|
const method = new FirmwareUpdateV4({
|
|
8992
9076
|
id: 1,
|
|
8993
9077
|
payload: {
|
|
8994
9078
|
method: 'firmwareUpdateV4',
|
|
8995
9079
|
},
|
|
8996
9080
|
});
|
|
8997
|
-
let
|
|
8998
|
-
const typedCall = jest.fn().
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9081
|
+
let resolveRequest: ((value: unknown) => void) | undefined;
|
|
9082
|
+
const typedCall = jest.fn().mockImplementation((type: string) => {
|
|
9083
|
+
if (type === 'DeviceFirmwareUpdateRequest') {
|
|
9084
|
+
return new Promise(resolve => {
|
|
9085
|
+
resolveRequest = resolve;
|
|
9086
|
+
});
|
|
9087
|
+
}
|
|
9088
|
+
return Promise.resolve({ type: 'Success', message: {} });
|
|
9089
|
+
});
|
|
9005
9090
|
|
|
9006
9091
|
(method as any).device = stubDevice({
|
|
9007
|
-
getCommands: () => ({ typedCall
|
|
9092
|
+
getCommands: () => ({ typedCall }),
|
|
9008
9093
|
createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
|
|
9009
9094
|
toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2' }),
|
|
9010
9095
|
});
|
|
@@ -9024,17 +9109,17 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
9024
9109
|
'Success',
|
|
9025
9110
|
{ targets: [{ target_id: 4, path: 'vol1:firmware.bin' }] },
|
|
9026
9111
|
]);
|
|
9027
|
-
expect(
|
|
9112
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
|
|
9028
9113
|
expect(method.postTipMessage).not.toHaveBeenCalled();
|
|
9029
9114
|
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
9030
9115
|
|
|
9031
|
-
|
|
9116
|
+
resolveRequest?.({ type: 'Success', message: {} });
|
|
9032
9117
|
await startPromise;
|
|
9033
9118
|
expect(method.postTipMessage).not.toHaveBeenCalled();
|
|
9034
|
-
expect(method.postProgressMessage).
|
|
9119
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(1, 'installingFirmware');
|
|
9035
9120
|
});
|
|
9036
9121
|
|
|
9037
|
-
test('
|
|
9122
|
+
test('returns after the empty Protocol V2 firmware install request responds', async () => {
|
|
9038
9123
|
const method = new FirmwareUpdateV4({
|
|
9039
9124
|
id: 1,
|
|
9040
9125
|
payload: {
|
|
@@ -9045,10 +9130,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
9045
9130
|
type: 'Success',
|
|
9046
9131
|
message: { message: 'accepted' },
|
|
9047
9132
|
});
|
|
9048
|
-
const call = jest.fn().mockResolvedValue({ type: 'WriteCompleted', message: {} });
|
|
9049
9133
|
|
|
9050
9134
|
(method as any).device = stubDevice({
|
|
9051
|
-
getCommands: () => ({ typedCall
|
|
9135
|
+
getCommands: () => ({ typedCall }),
|
|
9052
9136
|
createProtocolV2UiPhaseMetadata: jest.fn().mockReturnValue(undefined),
|
|
9053
9137
|
toMessageObject: jest.fn().mockReturnValue({ connectId: 'pro2' }),
|
|
9054
9138
|
});
|
|
@@ -9061,8 +9145,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
9061
9145
|
});
|
|
9062
9146
|
|
|
9063
9147
|
expect(typedCall.mock.calls[0][0]).toBe('DeviceFirmwareUpdateStage');
|
|
9064
|
-
expect(
|
|
9148
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFirmwareUpdateRequest', 'Success', {});
|
|
9065
9149
|
expect(method.postTipMessage).not.toHaveBeenCalled();
|
|
9150
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(1, 'installingFirmware');
|
|
9066
9151
|
});
|
|
9067
9152
|
});
|
|
9068
9153
|
|
|
@@ -17,8 +17,8 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
17
17
|
private protocolV2LatestFinalFeatures?;
|
|
18
18
|
private protocolV2LatestFinalDeviceInfo?;
|
|
19
19
|
private protocolV2InstallBaselineVersions;
|
|
20
|
-
private protocolV2InstallStatusBaseline?;
|
|
21
20
|
private protocolV2InstallNeedsBleReconnect;
|
|
21
|
+
private protocolV2InstallRequestConfirmed;
|
|
22
22
|
private protocolV2LastRuntimeProbeFeatures?;
|
|
23
23
|
private protocolV2LastTransferProgress?;
|
|
24
24
|
private protocolV2LastTransferProgressAt;
|
|
@@ -78,6 +78,7 @@ export default class FirmwareUpdateV4 extends FirmwareUpdateBaseMethod<FirmwareU
|
|
|
78
78
|
private protocolV2SourceUpdateProcess;
|
|
79
79
|
private getProtocolV2InstallItemStagingPath;
|
|
80
80
|
private verifyProtocolV2StagedFile;
|
|
81
|
+
private collectProtocolV2LiveTargetIds;
|
|
81
82
|
private assertProtocolV2TargetStatus;
|
|
82
83
|
private getProtocolV2MissingTargetIds;
|
|
83
84
|
private getProtocolV2ObservableTargetVersions;
|