@onekeyfe/hd-core 1.2.0-alpha.69 → 1.2.0-alpha.70
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__/check-all-firmware-release-protocol-v2.test.ts +0 -28
- package/__tests__/connectSettings.test.ts +45 -5
- package/__tests__/device-initialize-timeout.test.ts +56 -0
- package/__tests__/firmware-update/check-all-firmware-release.test.ts +175 -0
- package/__tests__/firmware-update/device-update-bootloader.test.ts +87 -0
- package/__tests__/firmware-update/firmware-artifact-source.test.ts +119 -0
- package/__tests__/firmware-update/firmware-host-binding.test.ts +70 -0
- package/__tests__/firmware-update/firmware-preparation-error.test.ts +27 -0
- package/__tests__/firmware-update/firmware-update-bootloader-poll.test.ts +200 -0
- package/__tests__/firmware-update/firmware-update-capabilities.test.ts +13 -0
- package/__tests__/firmware-update/firmware-update-plan-bootloader-identity.test.ts +394 -0
- package/__tests__/firmware-update/firmware-update-plan.test.ts +593 -0
- package/__tests__/firmware-update/firmware-update-prepared-plan.test.ts +231 -0
- package/__tests__/firmware-update/firmware-update-v2-download-before-boot.test.ts +59 -31
- package/__tests__/firmware-update/firmware-update-v3-reconnect.test.ts +63 -0
- package/__tests__/firmware-update/firmware-upload-source.test.ts +70 -0
- package/__tests__/protocol-v2-firmware-targets.test.ts +1 -33
- package/__tests__/protocol-v2-resources.test.ts +6 -9
- package/__tests__/protocol-v2.test.ts +383 -174
- package/__tests__/protocolV2FileWrite.test.ts +7 -48
- package/dist/api/CheckAllFirmwareRelease.d.ts +1 -2
- package/dist/api/CheckAllFirmwareRelease.d.ts.map +1 -1
- package/dist/api/FirmwareUpdate.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV2.d.ts +10 -0
- package/dist/api/FirmwareUpdateV2.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV3.d.ts +7 -3
- package/dist/api/FirmwareUpdateV3.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +25 -6
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts +5 -1
- package/dist/api/device/DeviceUpdateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareArtifactSource.d.ts +25 -0
- package/dist/api/firmware/FirmwareArtifactSource.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts +6 -0
- package/dist/api/firmware/FirmwareHostBinding.d.ts.map +1 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts +3 -0
- package/dist/api/firmware/FirmwarePreparationError.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts +8 -0
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts +3 -0
- package/dist/api/firmware/FirmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts +25 -0
- package/dist/api/firmware/FirmwareUpdatePlan.d.ts.map +1 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts +32 -0
- package/dist/api/firmware/FirmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/api/firmware/getBinary.d.ts +7 -0
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/firmware/updateBootloader.d.ts +2 -0
- package/dist/api/firmware/updateBootloader.d.ts.map +1 -1
- package/dist/api/firmware/uploadFirmware.d.ts +9 -1
- package/dist/api/firmware/uploadFirmware.d.ts.map +1 -1
- package/dist/api/helpers/protocolV2FileWrite.d.ts.map +1 -1
- package/dist/api/stellar/StellarSignTransaction.d.ts +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -0
- package/dist/data-manager/DataManager.d.ts.map +1 -1
- package/dist/data-manager/connectSettings.d.ts.map +1 -1
- package/dist/device/Device.d.ts +1 -0
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/index.d.ts +180 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3038 -1041
- package/dist/inject.d.ts.map +1 -1
- package/dist/lowLevelInject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts +1 -1
- package/dist/protocols/protocol-v2/resources.d.ts.map +1 -1
- package/dist/topLevelInject.d.ts.map +1 -1
- package/dist/types/api/checkAllFirmwareRelease.d.ts +4 -0
- package/dist/types/api/checkAllFirmwareRelease.d.ts.map +1 -1
- package/dist/types/api/deviceUpdateBootloader.d.ts +6 -0
- package/dist/types/api/deviceUpdateBootloader.d.ts.map +1 -1
- package/dist/types/api/export.d.ts +4 -1
- package/dist/types/api/export.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +68 -0
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdateCapabilities.d.ts +9 -0
- package/dist/types/api/firmwareUpdateCapabilities.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts +28 -0
- package/dist/types/api/firmwareUpdatePlan.d.ts.map +1 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts +42 -0
- package/dist/types/api/firmwareUpdatePreparedPlan.d.ts.map +1 -0
- package/dist/types/api/index.d.ts +9 -1
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/settings.d.ts +14 -1
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesCompat.d.ts +1 -0
- package/dist/utils/deviceFeaturesCompat.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/CheckAllFirmwareRelease.ts +61 -25
- package/src/api/FirmwareUpdate.ts +13 -1
- package/src/api/FirmwareUpdateV2.ts +296 -121
- package/src/api/FirmwareUpdateV3.ts +263 -57
- package/src/api/FirmwareUpdateV4.ts +865 -287
- package/src/api/device/DeviceUpdateBootloader.ts +125 -6
- package/src/api/firmware/FirmwareArtifactSource.ts +278 -0
- package/src/api/firmware/FirmwareHostBinding.ts +100 -0
- package/src/api/firmware/FirmwarePreparationError.ts +12 -0
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +72 -1
- package/src/api/firmware/FirmwareUpdateCapabilities.ts +9 -0
- package/src/api/firmware/FirmwareUpdatePlan.ts +772 -0
- package/src/api/firmware/FirmwareUpdatePreparedPlan.ts +449 -0
- package/src/api/firmware/updateBootloader.ts +19 -4
- package/src/api/firmware/uploadFirmware.ts +140 -22
- package/src/api/helpers/protocolV2FileWrite.ts +4 -54
- package/src/data-manager/DataManager.ts +62 -54
- package/src/data-manager/connectSettings.ts +11 -0
- package/src/device/Device.ts +6 -1
- package/src/index.ts +5 -0
- package/src/inject.ts +22 -2
- package/src/lowLevelInject.ts +5 -1
- package/src/protocols/protocol-v2/resources.ts +1 -5
- package/src/topLevelInject.ts +5 -1
- package/src/types/api/checkAllFirmwareRelease.ts +4 -0
- package/src/types/api/deviceUpdateBootloader.ts +6 -0
- package/src/types/api/export.ts +18 -0
- package/src/types/api/firmwareUpdate.ts +75 -0
- package/src/types/api/firmwareUpdateCapabilities.ts +9 -0
- package/src/types/api/firmwareUpdatePlan.ts +38 -0
- package/src/types/api/firmwareUpdatePreparedPlan.ts +53 -0
- package/src/types/api/index.ts +14 -0
- package/src/types/settings.ts +14 -2
- package/src/utils/deviceFeaturesCompat.ts +6 -0
|
@@ -35,10 +35,7 @@ import ProtocolInfoRequest from '../src/api/protocol-v2/ProtocolInfoRequest';
|
|
|
35
35
|
import EVMSignTypedData from '../src/api/evm/EVMSignTypedData';
|
|
36
36
|
import EVMSignMessageEIP712 from '../src/api/evm/EVMSignMessageEIP712';
|
|
37
37
|
import FirmwareUpdateV3 from '../src/api/FirmwareUpdateV3';
|
|
38
|
-
import FirmwareUpdateV4, {
|
|
39
|
-
assertProtocolV2FirmwareTargetsSupported,
|
|
40
|
-
assertProtocolV2ReconnectIdentity,
|
|
41
|
-
} from '../src/api/FirmwareUpdateV4';
|
|
38
|
+
import FirmwareUpdateV4, { assertProtocolV2ReconnectIdentity } from '../src/api/FirmwareUpdateV4';
|
|
42
39
|
import GetPassphraseState from '../src/api/GetPassphraseState';
|
|
43
40
|
import GetOnekeyFeatures from '../src/api/GetOnekeyFeatures';
|
|
44
41
|
import { batchGetPublickeys } from '../src/api/helpers/batchGetPublickeys';
|
|
@@ -101,6 +98,7 @@ import {
|
|
|
101
98
|
getMethodVersionRange,
|
|
102
99
|
} from '../src/utils';
|
|
103
100
|
import { getDeviceFirmwareVersion } from '../src/utils/deviceVersionUtils';
|
|
101
|
+
import { openFirmwareByteSource } from '../src/api/firmware/FirmwareArtifactSource';
|
|
104
102
|
import {
|
|
105
103
|
getPassphraseState,
|
|
106
104
|
getPassphraseStateWithRefreshDeviceInfo,
|
|
@@ -257,10 +255,7 @@ describe('UploadPortfolio', () => {
|
|
|
257
255
|
'FilesystemFileWrite',
|
|
258
256
|
'FilesystemFile',
|
|
259
257
|
expect.any(Object),
|
|
260
|
-
|
|
261
|
-
timeoutMs: undefined,
|
|
262
|
-
onWriteCompleted: expect.any(Function),
|
|
263
|
-
})
|
|
258
|
+
{ timeoutMs: undefined }
|
|
264
259
|
);
|
|
265
260
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'PortfolioUpdate', 'Success', {});
|
|
266
261
|
});
|
|
@@ -303,10 +298,7 @@ describe('UploadPortfolio', () => {
|
|
|
303
298
|
append: false,
|
|
304
299
|
ui_percentage: 100,
|
|
305
300
|
},
|
|
306
|
-
|
|
307
|
-
timeoutMs: undefined,
|
|
308
|
-
onWriteCompleted: expect.any(Function),
|
|
309
|
-
})
|
|
301
|
+
{ timeoutMs: undefined }
|
|
310
302
|
);
|
|
311
303
|
expect(typedCall).toHaveBeenNthCalledWith(2, 'PortfolioUpdate', 'Success', {});
|
|
312
304
|
expect(method.postMessage).not.toHaveBeenCalled();
|
|
@@ -3801,7 +3793,6 @@ describe('API compatibility handling', () => {
|
|
|
3801
3793
|
});
|
|
3802
3794
|
|
|
3803
3795
|
describe('Protocol V2 firmware update targets', () => {
|
|
3804
|
-
const OKPP_HEADER_SIZE = 0x52a0;
|
|
3805
3796
|
let forceReloadDataSpy: jest.SpyInstance;
|
|
3806
3797
|
|
|
3807
3798
|
beforeEach(() => {
|
|
@@ -3812,38 +3803,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3812
3803
|
jest.restoreAllMocks();
|
|
3813
3804
|
});
|
|
3814
3805
|
|
|
3815
|
-
const buildOkppHeader = ({
|
|
3816
|
-
type = 'RESC',
|
|
3817
|
-
version = [1, 0, 0],
|
|
3818
|
-
payloadHash = 'ab'.repeat(64),
|
|
3819
|
-
headerHash = 'cd'.repeat(64),
|
|
3820
|
-
}: {
|
|
3821
|
-
type?: string;
|
|
3822
|
-
version?: [number, number, number];
|
|
3823
|
-
payloadHash?: string;
|
|
3824
|
-
headerHash?: string;
|
|
3825
|
-
} = {}) => {
|
|
3826
|
-
const header = new Uint8Array(OKPP_HEADER_SIZE);
|
|
3827
|
-
const view = new DataView(header.buffer);
|
|
3828
|
-
'OKPP'.split('').forEach((char, index) => {
|
|
3829
|
-
header[index] = char.charCodeAt(0);
|
|
3830
|
-
});
|
|
3831
|
-
view.setUint32(0x04, 1, true);
|
|
3832
|
-
type.split('').forEach((char, index) => {
|
|
3833
|
-
header[0x08 + index] = char.charCodeAt(0);
|
|
3834
|
-
});
|
|
3835
|
-
view.setUint32(0x0c, OKPP_HEADER_SIZE, true);
|
|
3836
|
-
view.setUint32(0x10, version[0] * 0x10000 + version[1] * 0x100 + version[2], true);
|
|
3837
|
-
const writeHex = (offset: number, hex: string) => {
|
|
3838
|
-
for (let i = 0; i < hex.length / 2; i++) {
|
|
3839
|
-
header[offset + i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
3840
|
-
}
|
|
3841
|
-
};
|
|
3842
|
-
writeHex(0x200, payloadHash);
|
|
3843
|
-
writeHex(0x240, headerHash);
|
|
3844
|
-
return header;
|
|
3845
|
-
};
|
|
3846
|
-
|
|
3847
3806
|
test('keeps Protocol V2 firmware updates off the firmwareUpdateV3 path', () => {
|
|
3848
3807
|
const method = new FirmwareUpdateV3({
|
|
3849
3808
|
id: 1,
|
|
@@ -3951,7 +3910,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3951
3910
|
});
|
|
3952
3911
|
});
|
|
3953
3912
|
|
|
3954
|
-
test('
|
|
3913
|
+
test('delegates Protocol V2 mode transition to the phase executor', async () => {
|
|
3955
3914
|
const method = new FirmwareUpdateV4({
|
|
3956
3915
|
id: 1,
|
|
3957
3916
|
payload: {
|
|
@@ -3986,7 +3945,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3986
3945
|
|
|
3987
3946
|
await method.run();
|
|
3988
3947
|
|
|
3989
|
-
expect((method as any).enterProtocolV2BootloaderMode).
|
|
3948
|
+
expect((method as any).enterProtocolV2BootloaderMode).not.toHaveBeenCalled();
|
|
3990
3949
|
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
3991
3950
|
fwBinaryMap: [
|
|
3992
3951
|
{
|
|
@@ -3997,7 +3956,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
3997
3956
|
],
|
|
3998
3957
|
bootloaderBinary: null,
|
|
3999
3958
|
});
|
|
4000
|
-
expect(method.postTipMessage).toHaveBeenCalledWith('SwitchFirmwareReconnectDevice');
|
|
4001
3959
|
});
|
|
4002
3960
|
|
|
4003
3961
|
test('enters Protocol V2 bootloader before reading and downloading remote resources', async () => {
|
|
@@ -4031,10 +3989,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4031
3989
|
});
|
|
4032
3990
|
|
|
4033
3991
|
const order: string[] = [];
|
|
4034
|
-
(method as any).prepareProtocolV2BootResources = jest.fn().mockImplementation(() => {
|
|
4035
|
-
order.push('prepare-startup-resources');
|
|
4036
|
-
return Promise.resolve([]);
|
|
4037
|
-
});
|
|
4038
3992
|
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockImplementation(() => {
|
|
4039
3993
|
order.push('enter-bootloader');
|
|
4040
3994
|
return Promise.resolve(true);
|
|
@@ -4063,12 +4017,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4063
4017
|
|
|
4064
4018
|
await method.run();
|
|
4065
4019
|
|
|
4066
|
-
expect(order).toEqual([
|
|
4067
|
-
'prepare-startup-resources',
|
|
4068
|
-
'enter-bootloader',
|
|
4069
|
-
'prepare-resources',
|
|
4070
|
-
'execute-update',
|
|
4071
|
-
]);
|
|
4020
|
+
expect(order).toEqual(['enter-bootloader', 'prepare-resources', 'execute-update']);
|
|
4072
4021
|
});
|
|
4073
4022
|
|
|
4074
4023
|
test('reboots Protocol V2 normal-mode device to bootloader before transfer', async () => {
|
|
@@ -4312,41 +4261,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4312
4261
|
expect(typedCall.mock.calls.map(call => call[0])).toEqual(['DeviceInfoGet']);
|
|
4313
4262
|
});
|
|
4314
4263
|
|
|
4315
|
-
test('polls again when Protocol V2 bootloader serial is temporarily unavailable', async () => {
|
|
4316
|
-
const method = new FirmwareUpdateV4({
|
|
4317
|
-
id: 1,
|
|
4318
|
-
payload: {
|
|
4319
|
-
method: 'firmwareUpdateV4',
|
|
4320
|
-
},
|
|
4321
|
-
});
|
|
4322
|
-
const typedCall = jest
|
|
4323
|
-
.fn()
|
|
4324
|
-
.mockResolvedValueOnce({
|
|
4325
|
-
type: 'DeviceInfo',
|
|
4326
|
-
message: { protocol_version: 1, hw: {} },
|
|
4327
|
-
})
|
|
4328
|
-
.mockResolvedValueOnce({
|
|
4329
|
-
type: 'DeviceInfo',
|
|
4330
|
-
message: protocolV2BootloaderDeviceInfo,
|
|
4331
|
-
});
|
|
4332
|
-
const reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
4333
|
-
(method as any).device = stubDevice({
|
|
4334
|
-
originalDescriptor: { id: 'ble-id', protocolType: 'V2' },
|
|
4335
|
-
getCommands: () => ({ typedCall }),
|
|
4336
|
-
probeProtocolV2RuntimeState: jest.fn().mockResolvedValue({
|
|
4337
|
-
mode: 'bootloader',
|
|
4338
|
-
bootloaderMode: true,
|
|
4339
|
-
}),
|
|
4340
|
-
});
|
|
4341
|
-
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
4342
|
-
(method as any).protocolV2ExpectedSerialNumber = 'PR9999999999';
|
|
4343
|
-
|
|
4344
|
-
await (method as any).waitForProtocolV2BootloaderMode(60 * 1000, 0);
|
|
4345
|
-
|
|
4346
|
-
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(2);
|
|
4347
|
-
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
4348
|
-
});
|
|
4349
|
-
|
|
4350
4264
|
test('does not run generic initialize during Protocol V2 USB firmware reconnect', async () => {
|
|
4351
4265
|
const method = new FirmwareUpdateV4({
|
|
4352
4266
|
id: 1,
|
|
@@ -4774,9 +4688,9 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4774
4688
|
const typedCall = jest
|
|
4775
4689
|
.fn()
|
|
4776
4690
|
.mockRejectedValue(
|
|
4777
|
-
|
|
4778
|
-
"Failed to execute 'open' on 'USBDevice': The device was disconnected",
|
|
4779
|
-
'NotFoundError'
|
|
4691
|
+
Object.assign(
|
|
4692
|
+
new Error("Failed to execute 'open' on 'USBDevice': The device was disconnected"),
|
|
4693
|
+
{ name: 'NotFoundError' }
|
|
4780
4694
|
)
|
|
4781
4695
|
);
|
|
4782
4696
|
|
|
@@ -4997,6 +4911,181 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
4997
4911
|
expect((method as any).probeProtocolV2NormalMode).toHaveBeenCalledWith(deviceInfo);
|
|
4998
4912
|
});
|
|
4999
4913
|
|
|
4914
|
+
test('rejects a finished target whose payload version conflicts with the plan', () => {
|
|
4915
|
+
const method = new FirmwareUpdateV4({
|
|
4916
|
+
id: 1,
|
|
4917
|
+
payload: {
|
|
4918
|
+
method: 'firmwareUpdateV4',
|
|
4919
|
+
platform: 'desktop',
|
|
4920
|
+
expectedTargetVersions: {
|
|
4921
|
+
app_v1: '2.0.0',
|
|
4922
|
+
},
|
|
4923
|
+
},
|
|
4924
|
+
});
|
|
4925
|
+
method.init();
|
|
4926
|
+
method.postProgressMessage = jest.fn();
|
|
4927
|
+
|
|
4928
|
+
expect(() =>
|
|
4929
|
+
(method as any).assertProtocolV2TargetStatus(
|
|
4930
|
+
[{ target_id: 4, status: 2, payload_version: 0x010000 }],
|
|
4931
|
+
new Set([4])
|
|
4932
|
+
)
|
|
4933
|
+
).toThrow('expected 131072');
|
|
4934
|
+
});
|
|
4935
|
+
|
|
4936
|
+
test('fails install polling immediately with the original firmware verification error', async () => {
|
|
4937
|
+
const method = new FirmwareUpdateV4({
|
|
4938
|
+
id: 1,
|
|
4939
|
+
payload: {
|
|
4940
|
+
method: 'firmwareUpdateV4',
|
|
4941
|
+
platform: 'desktop',
|
|
4942
|
+
expectedTargetVersions: {
|
|
4943
|
+
app_v1: '2.0.0',
|
|
4944
|
+
},
|
|
4945
|
+
},
|
|
4946
|
+
});
|
|
4947
|
+
method.init();
|
|
4948
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
4949
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
4950
|
+
message: {
|
|
4951
|
+
records: [{ target_id: 4, status: 2, payload_version: 0x010000 }],
|
|
4952
|
+
},
|
|
4953
|
+
});
|
|
4954
|
+
const dateNowSpy = jest
|
|
4955
|
+
.spyOn(Date, 'now')
|
|
4956
|
+
.mockReturnValueOnce(0)
|
|
4957
|
+
.mockReturnValueOnce(0)
|
|
4958
|
+
.mockReturnValue(300_001);
|
|
4959
|
+
const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
|
|
4960
|
+
callback: () => void
|
|
4961
|
+
) => {
|
|
4962
|
+
callback();
|
|
4963
|
+
return 0 as any;
|
|
4964
|
+
}) as typeof setTimeout);
|
|
4965
|
+
|
|
4966
|
+
(method as any).device = stubDevice({
|
|
4967
|
+
getCommands: () => ({ typedCall }),
|
|
4968
|
+
});
|
|
4969
|
+
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
4970
|
+
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
|
|
4971
|
+
|
|
4972
|
+
try {
|
|
4973
|
+
await expect(
|
|
4974
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
4975
|
+
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
4976
|
+
])
|
|
4977
|
+
).rejects.toMatchObject({
|
|
4978
|
+
errorCode: HardwareErrorCode.FirmwareVerificationFailed,
|
|
4979
|
+
message: expect.stringContaining('reached payload version 65536, expected 131072'),
|
|
4980
|
+
});
|
|
4981
|
+
} finally {
|
|
4982
|
+
dateNowSpy.mockRestore();
|
|
4983
|
+
setTimeoutSpy.mockRestore();
|
|
4984
|
+
}
|
|
4985
|
+
|
|
4986
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
4987
|
+
});
|
|
4988
|
+
|
|
4989
|
+
test('fails install polling immediately with the original status conflict error', async () => {
|
|
4990
|
+
const method = new FirmwareUpdateV4({
|
|
4991
|
+
id: 1,
|
|
4992
|
+
payload: {
|
|
4993
|
+
method: 'firmwareUpdateV4',
|
|
4994
|
+
},
|
|
4995
|
+
});
|
|
4996
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
4997
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
4998
|
+
message: {
|
|
4999
|
+
records: [{ target_id: 4, status: 2, path: 'vol0:/unexpected.bin' }],
|
|
5000
|
+
},
|
|
5001
|
+
});
|
|
5002
|
+
const dateNowSpy = jest
|
|
5003
|
+
.spyOn(Date, 'now')
|
|
5004
|
+
.mockReturnValueOnce(0)
|
|
5005
|
+
.mockReturnValueOnce(0)
|
|
5006
|
+
.mockReturnValue(300_001);
|
|
5007
|
+
const setTimeoutSpy = jest.spyOn(global, 'setTimeout').mockImplementation(((
|
|
5008
|
+
callback: () => void
|
|
5009
|
+
) => {
|
|
5010
|
+
callback();
|
|
5011
|
+
return 0 as any;
|
|
5012
|
+
}) as typeof setTimeout);
|
|
5013
|
+
|
|
5014
|
+
(method as any).device = stubDevice({
|
|
5015
|
+
getCommands: () => ({ typedCall }),
|
|
5016
|
+
});
|
|
5017
|
+
(method as any).reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
5018
|
+
(method as any).verifyProtocolV2ReconnectIdentity = jest.fn().mockResolvedValue(undefined);
|
|
5019
|
+
|
|
5020
|
+
try {
|
|
5021
|
+
await expect(
|
|
5022
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
5023
|
+
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
5024
|
+
])
|
|
5025
|
+
).rejects.toMatchObject({
|
|
5026
|
+
errorCode: HardwareErrorCode.RuntimeError,
|
|
5027
|
+
message: 'Protocol V2 install status conflicts with target 4',
|
|
5028
|
+
params: {
|
|
5029
|
+
firmwareUpdateCode: 'FirmwareInstallStatusConflict',
|
|
5030
|
+
},
|
|
5031
|
+
});
|
|
5032
|
+
} finally {
|
|
5033
|
+
dateNowSpy.mockRestore();
|
|
5034
|
+
setTimeoutSpy.mockRestore();
|
|
5035
|
+
}
|
|
5036
|
+
|
|
5037
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
5038
|
+
});
|
|
5039
|
+
|
|
5040
|
+
test('does not fail a completed multi-app install when final app versions are unobservable', () => {
|
|
5041
|
+
const method = new FirmwareUpdateV4({
|
|
5042
|
+
id: 1,
|
|
5043
|
+
payload: {
|
|
5044
|
+
method: 'firmwareUpdateV4',
|
|
5045
|
+
platform: 'desktop',
|
|
5046
|
+
targetsToUpdate: ['app_v1', 'app_v2'],
|
|
5047
|
+
expectedTargetVersions: {
|
|
5048
|
+
app_v1: '1.0.0',
|
|
5049
|
+
app_v2: '2.0.0',
|
|
5050
|
+
},
|
|
5051
|
+
},
|
|
5052
|
+
});
|
|
5053
|
+
method.init();
|
|
5054
|
+
(method as any).protocolV2LatestFinalFeatures = {
|
|
5055
|
+
major_version: 3,
|
|
5056
|
+
minor_version: 0,
|
|
5057
|
+
patch_version: 0,
|
|
5058
|
+
};
|
|
5059
|
+
(method as any).protocolV2FinalStatusVerified = true;
|
|
5060
|
+
|
|
5061
|
+
expect(() => (method as any).assertExpectedProtocolV2Versions()).not.toThrow();
|
|
5062
|
+
});
|
|
5063
|
+
|
|
5064
|
+
test('rejects unobservable final versions after the status endpoint fallback', () => {
|
|
5065
|
+
const method = new FirmwareUpdateV4({
|
|
5066
|
+
id: 1,
|
|
5067
|
+
payload: {
|
|
5068
|
+
method: 'firmwareUpdateV4',
|
|
5069
|
+
platform: 'desktop',
|
|
5070
|
+
targetsToUpdate: ['app_v1', 'app_v2'],
|
|
5071
|
+
expectedTargetVersions: {
|
|
5072
|
+
app_v1: '1.0.0',
|
|
5073
|
+
app_v2: '2.0.0',
|
|
5074
|
+
},
|
|
5075
|
+
},
|
|
5076
|
+
});
|
|
5077
|
+
method.init();
|
|
5078
|
+
(method as any).protocolV2LatestFinalFeatures = {
|
|
5079
|
+
major_version: 3,
|
|
5080
|
+
minor_version: 0,
|
|
5081
|
+
patch_version: 0,
|
|
5082
|
+
};
|
|
5083
|
+
|
|
5084
|
+
expect(() => (method as any).assertExpectedProtocolV2Versions()).toThrow(
|
|
5085
|
+
'has no observable final version after status fallback'
|
|
5086
|
+
);
|
|
5087
|
+
});
|
|
5088
|
+
|
|
5000
5089
|
test('polls target status after update ACK and finishes when all targets complete', async () => {
|
|
5001
5090
|
const method = new FirmwareUpdateV4({
|
|
5002
5091
|
id: 1,
|
|
@@ -5218,7 +5307,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5218
5307
|
).toBe(true);
|
|
5219
5308
|
|
|
5220
5309
|
(method.postProgressMessage as jest.Mock).mockClear();
|
|
5221
|
-
expect(
|
|
5310
|
+
expect(() =>
|
|
5222
5311
|
(method as any).assertProtocolV2TargetStatus(
|
|
5223
5312
|
[
|
|
5224
5313
|
{ target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED' },
|
|
@@ -5226,8 +5315,8 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5226
5315
|
],
|
|
5227
5316
|
new Set([4, 10])
|
|
5228
5317
|
)
|
|
5229
|
-
).
|
|
5230
|
-
expect(method.postProgressMessage).
|
|
5318
|
+
).toThrow('Protocol V2 install status conflicts with target 4');
|
|
5319
|
+
expect(method.postProgressMessage).not.toHaveBeenCalled();
|
|
5231
5320
|
expect(method.postProgressMessage).not.toHaveBeenCalledWith(100, 'installingFirmware');
|
|
5232
5321
|
|
|
5233
5322
|
expect(
|
|
@@ -5331,10 +5420,14 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5331
5420
|
const writtenPaths: string[] = [];
|
|
5332
5421
|
method.postTipMessage = jest.fn();
|
|
5333
5422
|
method.postProgressMessage = jest.fn();
|
|
5334
|
-
(method as any).
|
|
5423
|
+
(method as any).protocolV2SourceUpdateProcess = jest.fn().mockImplementation(params => {
|
|
5335
5424
|
writtenPaths.push(params.filePath);
|
|
5336
|
-
return Number(params.processedSize ?? 0) + Number(params.
|
|
5425
|
+
return Number(params.processedSize ?? 0) + Number(params.source.size);
|
|
5337
5426
|
});
|
|
5427
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
5428
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
5429
|
+
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({});
|
|
5430
|
+
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
5338
5431
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
5339
5432
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
5340
5433
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
@@ -5374,10 +5467,12 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5374
5467
|
'vol0:/coprocessor.bin',
|
|
5375
5468
|
1
|
|
5376
5469
|
);
|
|
5377
|
-
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(
|
|
5378
|
-
expect((method as any).protocolV2StartFirmwareUpdate).
|
|
5470
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledTimes(2);
|
|
5471
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
|
|
5472
|
+
targets: [{ target_id: 3, path: 'vol0:/bootloader.bin' }],
|
|
5473
|
+
});
|
|
5474
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
5379
5475
|
targets: [
|
|
5380
|
-
{ target_id: 3, path: 'vol0:/bootloader.bin' },
|
|
5381
5476
|
{ target_id: 6, path: 'vol0:/coprocessor.bin' },
|
|
5382
5477
|
{ target_id: 7, path: 'vol0:/se01.bin' },
|
|
5383
5478
|
{ target_id: 4, path: 'vol0:/application_p1.bin' },
|
|
@@ -5387,7 +5482,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5387
5482
|
expect((method as any).waitForProtocolV2FirmwareUpdateComplete).toHaveBeenCalled();
|
|
5388
5483
|
});
|
|
5389
5484
|
|
|
5390
|
-
test('announces
|
|
5485
|
+
test('announces separate resource and component transfer phases', async () => {
|
|
5391
5486
|
const method = new FirmwareUpdateV4({
|
|
5392
5487
|
id: 1,
|
|
5393
5488
|
payload: {
|
|
@@ -5397,11 +5492,14 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5397
5492
|
|
|
5398
5493
|
method.postTipMessage = jest.fn();
|
|
5399
5494
|
method.postProgressMessage = jest.fn();
|
|
5400
|
-
(method as any).
|
|
5495
|
+
(method as any).protocolV2SourceUpdateProcess = jest
|
|
5401
5496
|
.fn()
|
|
5402
5497
|
.mockImplementation(params =>
|
|
5403
|
-
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.
|
|
5498
|
+
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.source.size))
|
|
5404
5499
|
);
|
|
5500
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
5501
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
5502
|
+
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
5405
5503
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
5406
5504
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
5407
5505
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
@@ -5421,21 +5519,22 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5421
5519
|
{
|
|
5422
5520
|
fileName: 'application_p1.bin',
|
|
5423
5521
|
binary: new Uint8Array([3]).buffer,
|
|
5424
|
-
targetId:
|
|
5522
|
+
targetId: 4,
|
|
5425
5523
|
},
|
|
5426
5524
|
],
|
|
5427
5525
|
});
|
|
5428
5526
|
|
|
5429
|
-
expect(method.postTipMessage).toHaveBeenCalledTimes(
|
|
5527
|
+
expect(method.postTipMessage).toHaveBeenCalledTimes(3);
|
|
5430
5528
|
expect(method.postTipMessage).toHaveBeenNthCalledWith(1, 'StartTransferData');
|
|
5431
|
-
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, '
|
|
5432
|
-
expect(
|
|
5529
|
+
expect(method.postTipMessage).toHaveBeenNthCalledWith(2, 'StartTransferData');
|
|
5530
|
+
expect(method.postTipMessage).toHaveBeenNthCalledWith(3, 'ConfirmOnDevice');
|
|
5531
|
+
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
|
|
5433
5532
|
1,
|
|
5434
|
-
expect.objectContaining({ processedSize: 0, totalSize:
|
|
5533
|
+
expect.objectContaining({ processedSize: 0, totalSize: 2 })
|
|
5435
5534
|
);
|
|
5436
|
-
expect((method as any).
|
|
5535
|
+
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenNthCalledWith(
|
|
5437
5536
|
2,
|
|
5438
|
-
expect.objectContaining({ processedSize:
|
|
5537
|
+
expect.objectContaining({ processedSize: 0, totalSize: 1 })
|
|
5439
5538
|
);
|
|
5440
5539
|
});
|
|
5441
5540
|
|
|
@@ -5449,7 +5548,8 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5449
5548
|
|
|
5450
5549
|
method.postTipMessage = jest.fn();
|
|
5451
5550
|
method.postProgressMessage = jest.fn();
|
|
5452
|
-
(method as any).
|
|
5551
|
+
(method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
|
|
5552
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
5453
5553
|
(method as any).device = stubDevice({
|
|
5454
5554
|
getCommands: () => ({
|
|
5455
5555
|
typedCall: jest.fn().mockResolvedValue({
|
|
@@ -5495,11 +5595,14 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5495
5595
|
|
|
5496
5596
|
method.postTipMessage = jest.fn();
|
|
5497
5597
|
method.postProgressMessage = jest.fn();
|
|
5498
|
-
(method as any).
|
|
5598
|
+
(method as any).protocolV2SourceUpdateProcess = jest
|
|
5499
5599
|
.fn()
|
|
5500
5600
|
.mockImplementation(params =>
|
|
5501
|
-
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.
|
|
5601
|
+
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.source.size))
|
|
5502
5602
|
);
|
|
5603
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
5604
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
5605
|
+
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
5503
5606
|
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
5504
5607
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
5505
5608
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
@@ -5822,6 +5925,58 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5822
5925
|
});
|
|
5823
5926
|
});
|
|
5824
5927
|
|
|
5928
|
+
test('rejects prepared RESC bundle paths before opening an artifact source', async () => {
|
|
5929
|
+
const method = new FirmwareUpdateV4({
|
|
5930
|
+
id: 1,
|
|
5931
|
+
payload: {
|
|
5932
|
+
method: 'firmwareUpdateV4',
|
|
5933
|
+
},
|
|
5934
|
+
});
|
|
5935
|
+
const openPreparedSource = jest.fn();
|
|
5936
|
+
(method as any).params = {
|
|
5937
|
+
targetsToUpdate: ['resource'],
|
|
5938
|
+
resourceBundleArtifacts: [
|
|
5939
|
+
{
|
|
5940
|
+
name: 'images',
|
|
5941
|
+
artifact: {
|
|
5942
|
+
artifactRef: `fw:${'a'.repeat(64)}`,
|
|
5943
|
+
size: 1,
|
|
5944
|
+
sha256: 'a'.repeat(64),
|
|
5945
|
+
},
|
|
5946
|
+
},
|
|
5947
|
+
],
|
|
5948
|
+
};
|
|
5949
|
+
(method as any).openProtocolV2PreparedSource = openPreparedSource;
|
|
5950
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
5951
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
5952
|
+
.mockReturnValue({
|
|
5953
|
+
required: false,
|
|
5954
|
+
version: [1, 0, 0],
|
|
5955
|
+
url: '',
|
|
5956
|
+
resourceBundles: [
|
|
5957
|
+
{
|
|
5958
|
+
name: 'images',
|
|
5959
|
+
url: 'https://example.com/images.okpkg',
|
|
5960
|
+
devicePath: 'vol0:/resource/../images.okpkg',
|
|
5961
|
+
},
|
|
5962
|
+
],
|
|
5963
|
+
fingerprint: '',
|
|
5964
|
+
changelog: {
|
|
5965
|
+
'zh-CN': '',
|
|
5966
|
+
'en-US': '',
|
|
5967
|
+
},
|
|
5968
|
+
});
|
|
5969
|
+
|
|
5970
|
+
await expect(
|
|
5971
|
+
(method as any).prepareProtocolV2ResourceSources('universal', {
|
|
5972
|
+
deviceType: 'pro2',
|
|
5973
|
+
})
|
|
5974
|
+
).rejects.toThrow('resourceBundles[].devicePath');
|
|
5975
|
+
expect(openPreparedSource).not.toHaveBeenCalled();
|
|
5976
|
+
|
|
5977
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
5978
|
+
});
|
|
5979
|
+
|
|
5825
5980
|
test('stops a remote update before reboot when the latest config cannot be refreshed', async () => {
|
|
5826
5981
|
const method = new FirmwareUpdateV4({
|
|
5827
5982
|
id: 1,
|
|
@@ -5977,7 +6132,10 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5977
6132
|
|
|
5978
6133
|
method.postTipMessage = jest.fn();
|
|
5979
6134
|
method.postProgressMessage = jest.fn();
|
|
5980
|
-
(method as any).
|
|
6135
|
+
(method as any).protocolV2SourceUpdateProcess = jest.fn().mockResolvedValue(3);
|
|
6136
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(undefined);
|
|
6137
|
+
(method as any).completeProtocolV2FinalVerification = jest.fn().mockResolvedValue({});
|
|
6138
|
+
(method as any).verifyProtocolV2StagedFile = jest.fn().mockResolvedValue(undefined);
|
|
5981
6139
|
(method as any).protocolV2StartFirmwareUpdate = jest.fn();
|
|
5982
6140
|
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest.fn();
|
|
5983
6141
|
|
|
@@ -5993,7 +6151,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
5993
6151
|
fwBinaryMap: [],
|
|
5994
6152
|
});
|
|
5995
6153
|
|
|
5996
|
-
expect((method as any).
|
|
6154
|
+
expect((method as any).protocolV2SourceUpdateProcess).toHaveBeenCalledTimes(1);
|
|
5997
6155
|
expect((method as any).protocolV2StartFirmwareUpdate).not.toHaveBeenCalled();
|
|
5998
6156
|
expect((method as any).waitForProtocolV2FirmwareUpdateComplete).not.toHaveBeenCalled();
|
|
5999
6157
|
});
|
|
@@ -6026,12 +6184,19 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6026
6184
|
});
|
|
6027
6185
|
method.postMessage = jest.fn();
|
|
6028
6186
|
|
|
6029
|
-
|
|
6030
|
-
|
|
6031
|
-
filePath: 'vol1:firmware.bin',
|
|
6032
|
-
processedSize: 0,
|
|
6033
|
-
totalSize: 4097,
|
|
6187
|
+
const source = await openFirmwareByteSource({
|
|
6188
|
+
binary: new Uint8Array(4097).buffer,
|
|
6034
6189
|
});
|
|
6190
|
+
try {
|
|
6191
|
+
await (method as any).protocolV2SourceUpdateProcess({
|
|
6192
|
+
source,
|
|
6193
|
+
filePath: 'vol1:firmware.bin',
|
|
6194
|
+
processedSize: 0,
|
|
6195
|
+
totalSize: 4097,
|
|
6196
|
+
});
|
|
6197
|
+
} finally {
|
|
6198
|
+
await source?.close();
|
|
6199
|
+
}
|
|
6035
6200
|
|
|
6036
6201
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
6037
6202
|
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 4000]);
|
|
@@ -6099,12 +6264,16 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6099
6264
|
method.postProgressMessage = jest.fn();
|
|
6100
6265
|
|
|
6101
6266
|
try {
|
|
6102
|
-
|
|
6103
|
-
|
|
6267
|
+
const source = await openFirmwareByteSource({
|
|
6268
|
+
binary: new Uint8Array(8001).buffer,
|
|
6269
|
+
});
|
|
6270
|
+
await (method as any).protocolV2SourceUpdateProcess({
|
|
6271
|
+
source,
|
|
6104
6272
|
filePath: 'vol0:/firmware.bin',
|
|
6105
6273
|
processedSize: 0,
|
|
6106
6274
|
totalSize: 8001,
|
|
6107
6275
|
});
|
|
6276
|
+
await source?.close();
|
|
6108
6277
|
} finally {
|
|
6109
6278
|
setTimeoutSpy.mockRestore();
|
|
6110
6279
|
}
|
|
@@ -6139,16 +6308,21 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6139
6308
|
});
|
|
6140
6309
|
method.postProgressMessage = jest.fn();
|
|
6141
6310
|
method.postTipMessage = jest.fn();
|
|
6311
|
+
(method as any).recoverProtocolV2FileTransfer = jest.fn().mockResolvedValue(undefined);
|
|
6142
6312
|
|
|
6313
|
+
const source = await openFirmwareByteSource({
|
|
6314
|
+
binary: new Uint8Array(4097).buffer,
|
|
6315
|
+
});
|
|
6143
6316
|
await expect(
|
|
6144
|
-
(method as any).
|
|
6145
|
-
|
|
6317
|
+
(method as any).protocolV2SourceUpdateProcess({
|
|
6318
|
+
source,
|
|
6146
6319
|
filePath: 'vol0:/firmware.bin',
|
|
6147
6320
|
processedSize: 0,
|
|
6148
6321
|
totalSize: 4097,
|
|
6149
6322
|
})
|
|
6150
6323
|
).rejects.toMatchObject({ errorCode: HardwareErrorCode.EmmcFileWriteFirmwareError });
|
|
6151
|
-
|
|
6324
|
+
await source?.close();
|
|
6325
|
+
expect(typedCall).toHaveBeenCalledTimes(6);
|
|
6152
6326
|
});
|
|
6153
6327
|
|
|
6154
6328
|
test('caps native BLE firmware upload chunks below the WebUSB limit', async () => {
|
|
@@ -6181,26 +6355,27 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
6181
6355
|
});
|
|
6182
6356
|
method.postProgressMessage = jest.fn();
|
|
6183
6357
|
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
filePath: 'vol1:ble-firmware.bin',
|
|
6187
|
-
processedSize: 0,
|
|
6188
|
-
totalSize: 1801,
|
|
6358
|
+
const source = await openFirmwareByteSource({
|
|
6359
|
+
binary: new Uint8Array(1801).buffer,
|
|
6189
6360
|
});
|
|
6361
|
+
try {
|
|
6362
|
+
await (method as any).protocolV2SourceUpdateProcess({
|
|
6363
|
+
source,
|
|
6364
|
+
filePath: 'vol1:ble-firmware.bin',
|
|
6365
|
+
processedSize: 0,
|
|
6366
|
+
totalSize: 1801,
|
|
6367
|
+
});
|
|
6368
|
+
} finally {
|
|
6369
|
+
await source?.close();
|
|
6370
|
+
}
|
|
6190
6371
|
|
|
6191
6372
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
6192
6373
|
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1800]);
|
|
6193
6374
|
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([1800, 1]);
|
|
6194
6375
|
expect(writePayloads.map(payload => payload.ui_percentage)).toEqual([0, 100]);
|
|
6195
6376
|
expect(typedCall.mock.calls.map(call => call[3])).toEqual([
|
|
6196
|
-
|
|
6197
|
-
|
|
6198
|
-
onWriteCompleted: expect.any(Function),
|
|
6199
|
-
}),
|
|
6200
|
-
expect.objectContaining({
|
|
6201
|
-
writeWithResponse: false,
|
|
6202
|
-
onWriteCompleted: expect.any(Function),
|
|
6203
|
-
}),
|
|
6377
|
+
{ writeWithResponse: true },
|
|
6378
|
+
{ writeWithResponse: true },
|
|
6204
6379
|
]);
|
|
6205
6380
|
});
|
|
6206
6381
|
|
|
@@ -6313,9 +6488,9 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
6313
6488
|
const typedCall = jest
|
|
6314
6489
|
.fn()
|
|
6315
6490
|
.mockRejectedValue(
|
|
6316
|
-
|
|
6317
|
-
"Failed to execute 'open' on 'USBDevice': The device was disconnected",
|
|
6318
|
-
'NotFoundError'
|
|
6491
|
+
Object.assign(
|
|
6492
|
+
new Error("Failed to execute 'open' on 'USBDevice': The device was disconnected"),
|
|
6493
|
+
{ name: 'NotFoundError' }
|
|
6319
6494
|
)
|
|
6320
6495
|
);
|
|
6321
6496
|
|
|
@@ -6538,6 +6713,21 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6538
6713
|
});
|
|
6539
6714
|
};
|
|
6540
6715
|
|
|
6716
|
+
test('keeps the host plan identity as the V4 reconnect anchor', () => {
|
|
6717
|
+
const method = new FirmwareUpdateV4({
|
|
6718
|
+
id: 1,
|
|
6719
|
+
payload: {
|
|
6720
|
+
method: 'firmwareUpdateV4',
|
|
6721
|
+
platform: 'native',
|
|
6722
|
+
expectedDeviceId: 'persisted-device-id',
|
|
6723
|
+
},
|
|
6724
|
+
});
|
|
6725
|
+
|
|
6726
|
+
method.init();
|
|
6727
|
+
|
|
6728
|
+
expect((method as any).params.expectedDeviceId).toBe('persisted-device-id');
|
|
6729
|
+
});
|
|
6730
|
+
|
|
6541
6731
|
test('rejects a different physical serial before firmware transfer resumes', () => {
|
|
6542
6732
|
expect(() => assertProtocolV2ReconnectIdentity('expected-serial', 'other-serial')).toThrow(
|
|
6543
6733
|
'identity mismatch'
|
|
@@ -6547,15 +6737,15 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6547
6737
|
).not.toThrow();
|
|
6548
6738
|
});
|
|
6549
6739
|
|
|
6550
|
-
test('
|
|
6551
|
-
expect(() =>
|
|
6552
|
-
|
|
6553
|
-
)
|
|
6554
|
-
expect(() =>
|
|
6555
|
-
|
|
6556
|
-
)
|
|
6740
|
+
test('fails closed when the physical serial is unavailable', () => {
|
|
6741
|
+
expect(() => assertProtocolV2ReconnectIdentity('expected-serial', undefined)).toThrow(
|
|
6742
|
+
expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
|
|
6743
|
+
);
|
|
6744
|
+
expect(() => assertProtocolV2ReconnectIdentity(undefined, 'actual-serial')).toThrow(
|
|
6745
|
+
expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
|
|
6746
|
+
);
|
|
6557
6747
|
expect(() => assertProtocolV2ReconnectIdentity(undefined, undefined)).toThrow(
|
|
6558
|
-
|
|
6748
|
+
expect.objectContaining({ errorCode: HardwareErrorCode.DeviceNotFound })
|
|
6559
6749
|
);
|
|
6560
6750
|
});
|
|
6561
6751
|
|
|
@@ -6574,7 +6764,6 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6574
6764
|
},
|
|
6575
6765
|
});
|
|
6576
6766
|
(method as any).device = stubDevice({
|
|
6577
|
-
originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
6578
6767
|
features: { deviceId: 'wallet-derived-id' },
|
|
6579
6768
|
getCommands: () => ({ typedCall }),
|
|
6580
6769
|
});
|
|
@@ -6590,7 +6779,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6590
6779
|
);
|
|
6591
6780
|
});
|
|
6592
6781
|
|
|
6593
|
-
test('
|
|
6782
|
+
test('rejects firmware update startup when DeviceInfo has no physical serial', async () => {
|
|
6594
6783
|
const method = new FirmwareUpdateV4({
|
|
6595
6784
|
id: 1,
|
|
6596
6785
|
payload: { method: 'firmwareUpdateV4' },
|
|
@@ -6600,13 +6789,42 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6600
6789
|
message: { protocol_version: 1, hw: {} },
|
|
6601
6790
|
});
|
|
6602
6791
|
(method as any).device = stubDevice({
|
|
6603
|
-
originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
6604
6792
|
getCommands: () => ({ typedCall }),
|
|
6605
6793
|
});
|
|
6606
6794
|
|
|
6607
|
-
await expect((method as any).captureProtocolV2PhysicalIdentity()).
|
|
6608
|
-
|
|
6609
|
-
|
|
6795
|
+
await expect((method as any).captureProtocolV2PhysicalIdentity()).rejects.toMatchObject({
|
|
6796
|
+
errorCode: HardwareErrorCode.DeviceNotFound,
|
|
6797
|
+
});
|
|
6798
|
+
});
|
|
6799
|
+
|
|
6800
|
+
test('uses the BLE read limit when reading an existing firmware bundle header', async () => {
|
|
6801
|
+
const method = new FirmwareUpdateV4({
|
|
6802
|
+
id: 1,
|
|
6803
|
+
payload: {
|
|
6804
|
+
method: 'firmwareUpdateV4',
|
|
6805
|
+
platform: 'native',
|
|
6806
|
+
chunkSize: 1800,
|
|
6807
|
+
},
|
|
6808
|
+
});
|
|
6809
|
+
method.init();
|
|
6810
|
+
const readChunkLengths: number[] = [];
|
|
6811
|
+
const typedCall = jest.fn((requestType: string, _responseType: string, request: any) => {
|
|
6812
|
+
if (requestType === 'FilesystemPathInfoQuery') {
|
|
6813
|
+
return Promise.resolve({ message: { exist: true, directory: false, size: 0x52a0 } });
|
|
6814
|
+
}
|
|
6815
|
+
if (requestType === 'FilesystemFileRead') {
|
|
6816
|
+
readChunkLengths.push(request.chunk_len);
|
|
6817
|
+
return Promise.resolve({ message: { data: new Uint8Array(request.chunk_len) } });
|
|
6818
|
+
}
|
|
6819
|
+
throw new Error(`Unexpected request: ${requestType}`);
|
|
6820
|
+
});
|
|
6821
|
+
(method as any).device = stubDevice({
|
|
6822
|
+
getCommands: () => ({ typedCall }),
|
|
6823
|
+
});
|
|
6824
|
+
|
|
6825
|
+
await expect((method as any).readProtocolV2DeviceFileHeader('bundle.okpp')).resolves.toBeNull();
|
|
6826
|
+
expect(readChunkLengths.length).toBeGreaterThan(1);
|
|
6827
|
+
expect(Math.max(...readChunkLengths)).toBe(900);
|
|
6610
6828
|
});
|
|
6611
6829
|
|
|
6612
6830
|
test('uses filesystem reads instead of ResourceInventoryGet for resource comparison', async () => {
|
|
@@ -6723,7 +6941,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6723
6941
|
resourcesSpy.mockRestore();
|
|
6724
6942
|
});
|
|
6725
6943
|
|
|
6726
|
-
test('does not resolve boot resources
|
|
6944
|
+
test('does not resolve boot resources unless the optional target is selected', async () => {
|
|
6727
6945
|
const method = new FirmwareUpdateV4({
|
|
6728
6946
|
id: 1,
|
|
6729
6947
|
payload: { method: 'firmwareUpdateV4', platform: 'web' },
|
|
@@ -6738,7 +6956,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6738
6956
|
expect(downloadSpy).not.toHaveBeenCalled();
|
|
6739
6957
|
});
|
|
6740
6958
|
|
|
6741
|
-
test('
|
|
6959
|
+
test('downloads and maps selected boot resources as direct RES files', async () => {
|
|
6742
6960
|
const bytes = new Uint8Array([1, 2, 3, 4]);
|
|
6743
6961
|
const binary = bytes.buffer as ArrayBuffer;
|
|
6744
6962
|
const fileHash = Array.from(sha256(bytes), byte => byte.toString(16).padStart(2, '0')).join('');
|
|
@@ -6760,7 +6978,7 @@ describe('Protocol V2 firmware reconnect identity', () => {
|
|
|
6760
6978
|
payload: {
|
|
6761
6979
|
method: 'firmwareUpdateV4',
|
|
6762
6980
|
platform: 'web',
|
|
6763
|
-
targetsToUpdate: ['
|
|
6981
|
+
targetsToUpdate: ['boot_resources'],
|
|
6764
6982
|
},
|
|
6765
6983
|
});
|
|
6766
6984
|
method.init();
|
|
@@ -7703,10 +7921,7 @@ describe('Protocol V2 file write method', () => {
|
|
|
7703
7921
|
append: false,
|
|
7704
7922
|
ui_percentage: 100,
|
|
7705
7923
|
},
|
|
7706
|
-
|
|
7707
|
-
timeoutMs: undefined,
|
|
7708
|
-
onWriteCompleted: expect.any(Function),
|
|
7709
|
-
})
|
|
7924
|
+
{ timeoutMs: undefined }
|
|
7710
7925
|
);
|
|
7711
7926
|
expect(method.postMessage).toHaveBeenCalledWith({
|
|
7712
7927
|
event: 'UI_EVENT',
|
|
@@ -7755,10 +7970,7 @@ describe('Protocol V2 file write method', () => {
|
|
|
7755
7970
|
append: false,
|
|
7756
7971
|
ui_percentage: 0,
|
|
7757
7972
|
},
|
|
7758
|
-
|
|
7759
|
-
timeoutMs: undefined,
|
|
7760
|
-
onWriteCompleted: expect.any(Function),
|
|
7761
|
-
})
|
|
7973
|
+
{ timeoutMs: undefined }
|
|
7762
7974
|
);
|
|
7763
7975
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
7764
7976
|
2,
|
|
@@ -7775,10 +7987,7 @@ describe('Protocol V2 file write method', () => {
|
|
|
7775
7987
|
append: false,
|
|
7776
7988
|
ui_percentage: 100,
|
|
7777
7989
|
},
|
|
7778
|
-
|
|
7779
|
-
timeoutMs: undefined,
|
|
7780
|
-
onWriteCompleted: expect.any(Function),
|
|
7781
|
-
})
|
|
7990
|
+
{ timeoutMs: undefined }
|
|
7782
7991
|
);
|
|
7783
7992
|
expect(result).toMatchObject({
|
|
7784
7993
|
path: 'vol1:test.bin',
|