@onekeyfe/hd-core 1.2.0-alpha.3 → 1.2.0-alpha.4
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__/preInitialize.test.ts +33 -0
- package/__tests__/protocol-v2.test.ts +1377 -480
- package/dist/api/FileRead.d.ts.map +1 -1
- package/dist/api/FileWrite.d.ts.map +1 -1
- package/dist/api/FirmwareUpdateV4.d.ts +20 -2
- package/dist/api/FirmwareUpdateV4.d.ts.map +1 -1
- package/dist/api/firmware/FirmwareUpdateBaseMethod.d.ts.map +1 -1
- package/dist/api/firmware/getBinary.d.ts +4 -0
- package/dist/api/firmware/getBinary.d.ts.map +1 -1
- package/dist/api/index.d.ts +5 -10
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts +6 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts +7 -0
- package/dist/api/protocol-v2/DeviceFactoryInfoSet.d.ts.map +1 -0
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts +1 -1
- package/dist/api/protocol-v2/DeviceFirmwareUpdate.d.ts.map +1 -1
- package/dist/api/protocol-v2/DeviceGetFirmwareUpdateStatus.d.ts +1 -1
- package/dist/api/protocol-v2/{DeviceGetDeviceInfo.d.ts → DeviceInfoGet.d.ts} +10 -10
- package/dist/api/protocol-v2/DeviceInfoGet.d.ts.map +1 -0
- package/dist/api/protocol-v2/{FilesystemFixPermission.d.ts → FilesystemPermissionFix.d.ts} +2 -2
- package/dist/api/protocol-v2/FilesystemPermissionFix.d.ts.map +1 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts +6 -0
- package/dist/api/protocol-v2/ProtocolInfoRequest.d.ts.map +1 -0
- package/dist/api/protocol-v2/helpers.d.ts +24 -18
- package/dist/api/protocol-v2/helpers.d.ts.map +1 -1
- package/dist/api/ton/TonSignData.d.ts +1 -5
- package/dist/api/ton/TonSignData.d.ts.map +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/data-manager/DataManager.d.ts +1 -1
- 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 +4 -1
- package/dist/device/Device.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceFeatures.d.ts.map +1 -1
- package/dist/deviceProfile/buildDeviceProfile.d.ts.map +1 -1
- package/dist/index.d.ts +79 -54
- package/dist/index.js +1537 -1528
- package/dist/inject.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/features.d.ts +12 -14
- package/dist/protocols/protocol-v2/features.d.ts.map +1 -1
- package/dist/protocols/protocol-v2/firmware.d.ts +10 -9
- package/dist/protocols/protocol-v2/firmware.d.ts.map +1 -1
- package/dist/types/api/firmwareUpdate.d.ts +1 -6
- package/dist/types/api/firmwareUpdate.d.ts.map +1 -1
- package/dist/types/api/getDeviceInfo.d.ts +2 -0
- package/dist/types/api/getDeviceInfo.d.ts.map +1 -1
- package/dist/types/api/index.d.ts +6 -11
- package/dist/types/api/index.d.ts.map +1 -1
- package/dist/types/api/protocolV2.d.ts +12 -17
- package/dist/types/api/protocolV2.d.ts.map +1 -1
- package/dist/types/device.d.ts +1 -0
- package/dist/types/device.d.ts.map +1 -1
- package/dist/types/params.d.ts +1 -0
- package/dist/types/params.d.ts.map +1 -1
- package/dist/types/settings.d.ts +29 -2
- package/dist/types/settings.d.ts.map +1 -1
- package/dist/utils/deviceFeaturesUtils.d.ts.map +1 -1
- package/dist/utils/patch.d.ts +1 -1
- package/dist/utils/patch.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/api/FileRead.ts +14 -1
- package/src/api/FileWrite.ts +14 -1
- package/src/api/FirmwareUpdateV4.ts +933 -197
- package/src/api/GetOnekeyFeatures.ts +1 -1
- package/src/api/device/DeviceRebootToBoardloader.ts +4 -4
- package/src/api/device/DeviceRebootToBootloader.ts +4 -4
- package/src/api/firmware/FirmwareUpdateBaseMethod.ts +11 -4
- package/src/api/index.ts +5 -10
- package/src/api/protocol-v2/{FactoryGetDeviceInfo.ts → DeviceFactoryInfoGet.ts} +3 -3
- package/src/api/protocol-v2/DeviceFactoryInfoSet.ts +32 -0
- package/src/api/protocol-v2/DeviceFirmwareUpdate.ts +3 -5
- package/src/api/protocol-v2/DeviceGetFirmwareUpdateStatus.ts +2 -2
- package/src/api/protocol-v2/{DeviceGetDeviceInfo.ts → DeviceInfoGet.ts} +52 -22
- package/src/api/protocol-v2/DeviceReboot.ts +1 -1
- package/src/api/protocol-v2/{FilesystemFixPermission.ts → FilesystemPermissionFix.ts} +2 -2
- package/src/api/protocol-v2/{GetProtoVersion.ts → ProtocolInfoRequest.ts} +2 -2
- package/src/api/protocol-v2/helpers.ts +59 -43
- package/src/api/ton/TonSignData.ts +1 -5
- package/src/core/index.ts +19 -5
- package/src/data/messages/messages-protocol-v2.json +486 -1072
- package/src/data-manager/DataManager.ts +7 -0
- package/src/data-manager/connectSettings.ts +0 -6
- package/src/device/Device.ts +25 -14
- package/src/deviceProfile/buildDeviceFeatures.ts +60 -63
- package/src/deviceProfile/buildDeviceProfile.ts +77 -58
- package/src/inject.ts +10 -16
- package/src/protocols/protocol-v2/features.ts +26 -63
- package/src/protocols/protocol-v2/firmware.ts +11 -36
- package/src/types/api/firmwareUpdate.ts +10 -20
- package/src/types/api/getDeviceInfo.ts +2 -0
- package/src/types/api/index.ts +10 -20
- package/src/types/api/protocolV2.ts +22 -42
- package/src/types/device.ts +1 -0
- package/src/types/params.ts +4 -0
- package/src/types/settings.ts +44 -35
- package/src/utils/deviceFeaturesUtils.ts +45 -10
- package/dist/api/protocol-v2/DevReboot.d.ts +0 -7
- package/dist/api/protocol-v2/DevReboot.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts +0 -6
- package/dist/api/protocol-v2/DeviceGetOnboardingStatus.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts +0 -7
- package/dist/api/protocol-v2/FactoryDeviceInfoSettings.d.ts.map +0 -1
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts +0 -6
- package/dist/api/protocol-v2/FactoryGetDeviceInfo.d.ts.map +0 -1
- package/dist/api/protocol-v2/FilesystemFixPermission.d.ts.map +0 -1
- package/dist/api/protocol-v2/GetProtoVersion.d.ts +0 -6
- package/dist/api/protocol-v2/GetProtoVersion.d.ts.map +0 -1
- package/src/api/protocol-v2/DevReboot.ts +0 -24
- package/src/api/protocol-v2/DeviceGetOnboardingStatus.ts +0 -16
- package/src/api/protocol-v2/FactoryDeviceInfoSettings.ts +0 -29
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { HardwareErrorCode } from '@onekeyfe/hd-shared';
|
|
2
|
-
import {
|
|
2
|
+
import { DeviceRebootType } from '@onekeyfe/hd-transport';
|
|
3
3
|
|
|
4
|
-
import
|
|
4
|
+
import * as firmwareBinaryApi from '../src/api/firmware/getBinary';
|
|
5
5
|
import DnxGetAddress from '../src/api/dynex/DnxGetAddress';
|
|
6
6
|
import DnxSignTransaction from '../src/api/dynex/DnxSignTransaction';
|
|
7
7
|
import DirList from '../src/api/DirList';
|
|
8
8
|
import FileRead from '../src/api/FileRead';
|
|
9
9
|
import FileWrite from '../src/api/FileWrite';
|
|
10
|
-
import
|
|
10
|
+
import DeviceFactoryInfoGet from '../src/api/protocol-v2/DeviceFactoryInfoGet';
|
|
11
|
+
import DeviceFactoryInfoSet from '../src/api/protocol-v2/DeviceFactoryInfoSet';
|
|
11
12
|
import DeviceFirmwareUpdate from '../src/api/protocol-v2/DeviceFirmwareUpdate';
|
|
12
|
-
import
|
|
13
|
-
import DeviceGetOnboardingStatus from '../src/api/protocol-v2/DeviceGetOnboardingStatus';
|
|
13
|
+
import DeviceInfoGet from '../src/api/protocol-v2/DeviceInfoGet';
|
|
14
14
|
import DeviceReboot from '../src/api/protocol-v2/DeviceReboot';
|
|
15
|
+
import FilesystemPermissionFix from '../src/api/protocol-v2/FilesystemPermissionFix';
|
|
16
|
+
import ProtocolInfoRequest from '../src/api/protocol-v2/ProtocolInfoRequest';
|
|
15
17
|
import EVMSignTypedData from '../src/api/evm/EVMSignTypedData';
|
|
16
18
|
import EVMSignMessageEIP712 from '../src/api/evm/EVMSignMessageEIP712';
|
|
17
19
|
import FirmwareUpdateV3 from '../src/api/FirmwareUpdateV3';
|
|
@@ -35,8 +37,6 @@ import TonSignData from '../src/api/ton/TonSignData';
|
|
|
35
37
|
import TonSignMessage from '../src/api/ton/TonSignMessage';
|
|
36
38
|
import TonSignProof from '../src/api/ton/TonSignProof';
|
|
37
39
|
import TronGetAddress from '../src/api/tron/TronGetAddress';
|
|
38
|
-
import TronSignMessage from '../src/api/tron/TronSignMessage';
|
|
39
|
-
import XrpSignTransaction from '../src/api/xrp/XrpSignTransaction';
|
|
40
40
|
import StellarGetAddress from '../src/api/stellar/StellarGetAddress';
|
|
41
41
|
import BenfenSignMessage from '../src/api/benfen/BenfenSignMessage';
|
|
42
42
|
import { getBitcoinForkVersionRange } from '../src/api/btc/helpers/versionLimit';
|
|
@@ -45,17 +45,14 @@ import { Device } from '../src/device/Device';
|
|
|
45
45
|
import { UI_REQUEST } from '../src/events/ui-request';
|
|
46
46
|
import {
|
|
47
47
|
PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
|
|
48
|
-
buildMockProtocolV2DeviceInfo,
|
|
49
48
|
requestProtocolV2DeviceInfo,
|
|
50
|
-
setProtocolV2DeviceInfoMock,
|
|
51
49
|
} from '../src/protocols/protocol-v2/features';
|
|
52
|
-
import {
|
|
53
|
-
buildProfileFromProtocolV2,
|
|
54
|
-
buildProtocolV2FeaturesPayload,
|
|
55
|
-
} from '../src/deviceProfile';
|
|
50
|
+
import { buildProfileFromProtocolV2, buildProtocolV2FeaturesPayload } from '../src/deviceProfile';
|
|
56
51
|
import {
|
|
57
52
|
getDeviceType,
|
|
58
53
|
getFirmwareType,
|
|
54
|
+
getFirmwareUpdateField,
|
|
55
|
+
getFirmwareUpdateFieldArray,
|
|
59
56
|
getMethodVersionRange,
|
|
60
57
|
isMethodVersionRangeUnsupported,
|
|
61
58
|
} from '../src/utils';
|
|
@@ -74,12 +71,6 @@ jest.mock('../src/data/config', () => ({
|
|
|
74
71
|
DEFAULT_DOMAIN: 'https://jssdk.onekey.so/1.0.0/',
|
|
75
72
|
}));
|
|
76
73
|
|
|
77
|
-
// DeviceGetDeviceInfo mock 开关默认开启(早期工程板固件没有该消息)。
|
|
78
|
-
// 本测试文件验证的是真实 wire 调用行为,统一关闭;mock 行为单独有用例覆盖。
|
|
79
|
-
beforeAll(() => {
|
|
80
|
-
setProtocolV2DeviceInfoMock(false);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
74
|
const descriptor = {
|
|
84
75
|
id: 'ble-id',
|
|
85
76
|
path: 'usb-path',
|
|
@@ -101,8 +92,7 @@ function stubDevice<T extends Record<string, any>>(device: T): T {
|
|
|
101
92
|
d.getCurrentSafetyChecks ??= () => d.features?.safetyChecks;
|
|
102
93
|
d.getCurrentDeviceId ??= () => d.features?.deviceId || undefined;
|
|
103
94
|
d.getCurrentSerialNo ??= () => d.features?.serialNo || '';
|
|
104
|
-
d.getCurrentPassphraseProtection ??= () =>
|
|
105
|
-
d.features?.passphraseProtection;
|
|
95
|
+
d.getCurrentPassphraseProtection ??= () => d.features?.passphraseProtection;
|
|
106
96
|
d.getCurrentMethodVersionRange ??= (fn: (model: any) => any) => {
|
|
107
97
|
const deviceType = d.getCurrentDeviceType();
|
|
108
98
|
const range = fn(deviceType);
|
|
@@ -121,7 +111,19 @@ function normalizeProtocolV2Features(_descriptor: unknown, deviceInfo?: Protocol
|
|
|
121
111
|
return buildProtocolV2FeaturesPayload(deviceInfo);
|
|
122
112
|
}
|
|
123
113
|
|
|
124
|
-
|
|
114
|
+
const protocolV2BootloaderDeviceInfo: ProtocolV2DeviceInfo = {
|
|
115
|
+
protocol_version: 1,
|
|
116
|
+
hw: {
|
|
117
|
+
serial_no: 'PR9999999999',
|
|
118
|
+
},
|
|
119
|
+
fw: {
|
|
120
|
+
bootloader: {
|
|
121
|
+
version: '0.0.1',
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
async function requestProtocolV2Features({
|
|
125
127
|
commands,
|
|
126
128
|
descriptor: inputDescriptor,
|
|
127
129
|
}: {
|
|
@@ -133,63 +135,38 @@ async function requestProtocolV2LegacyFeatures({
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
describe('Protocol V2 feature adapter', () => {
|
|
136
|
-
test('skips DeviceGetDeviceInfo wire call and returns mock when mock is enabled', async () => {
|
|
137
|
-
const typedCall = jest.fn();
|
|
138
|
-
setProtocolV2DeviceInfoMock(true);
|
|
139
|
-
try {
|
|
140
|
-
const deviceInfo = await requestProtocolV2DeviceInfo({
|
|
141
|
-
commands: { typedCall } as unknown as DeviceCommands,
|
|
142
|
-
});
|
|
143
|
-
expect(typedCall).not.toHaveBeenCalled();
|
|
144
|
-
expect(deviceInfo).toEqual(buildMockProtocolV2DeviceInfo());
|
|
145
|
-
const profile = buildProfileFromProtocolV2({
|
|
146
|
-
deviceInfo,
|
|
147
|
-
});
|
|
148
|
-
expect(profile).toMatchObject({
|
|
149
|
-
protocol: 'V2',
|
|
150
|
-
deviceId: '',
|
|
151
|
-
serialNo: '',
|
|
152
|
-
status: { initialized: true },
|
|
153
|
-
versions: { firmware: '0.1.0' },
|
|
154
|
-
});
|
|
155
|
-
} finally {
|
|
156
|
-
setProtocolV2DeviceInfoMock(false);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
138
|
test('normalizes Protocol V2 DeviceInfo into existing Features fields', () => {
|
|
161
139
|
const features = normalizeProtocolV2Features(descriptor as any, {
|
|
162
140
|
protocol_version: 1,
|
|
163
141
|
hw: {
|
|
164
142
|
serial_no: 'PR2SERIAL',
|
|
165
|
-
device_id: 'PRO2-DEVICE-ID',
|
|
166
143
|
},
|
|
167
144
|
fw: {
|
|
168
|
-
|
|
145
|
+
application_data: {
|
|
169
146
|
version: '0.1.0',
|
|
170
147
|
hash: [1, 2, 255],
|
|
171
148
|
},
|
|
172
|
-
|
|
149
|
+
bootloader: {
|
|
173
150
|
version: '0.2.0',
|
|
174
151
|
build_id: 'boot-build',
|
|
175
152
|
hash: new Uint8Array([10, 11]),
|
|
176
153
|
},
|
|
177
|
-
|
|
154
|
+
application: {
|
|
178
155
|
version: '1.2.3',
|
|
179
156
|
build_id: 'app-build',
|
|
180
157
|
hash: 'abc123',
|
|
181
158
|
},
|
|
182
159
|
},
|
|
183
|
-
|
|
184
|
-
|
|
160
|
+
coprocessor: {
|
|
161
|
+
application: {
|
|
185
162
|
version: '4.5.6',
|
|
186
163
|
build_id: 'bt-build',
|
|
187
164
|
hash: [12, 13],
|
|
188
165
|
},
|
|
189
|
-
|
|
166
|
+
bt_adv_name: 'Pro2 BLE',
|
|
190
167
|
},
|
|
191
168
|
se1: {
|
|
192
|
-
|
|
169
|
+
application: {
|
|
193
170
|
version: '7.8.9',
|
|
194
171
|
build_id: 'se-build',
|
|
195
172
|
hash: [14, 15],
|
|
@@ -197,18 +174,19 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
197
174
|
state: 85,
|
|
198
175
|
},
|
|
199
176
|
se2: {
|
|
200
|
-
|
|
177
|
+
application: {
|
|
201
178
|
version: '8.0.0',
|
|
202
179
|
},
|
|
203
180
|
state: 0,
|
|
204
181
|
},
|
|
205
182
|
status: {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
bt_enable: true,
|
|
183
|
+
device_id: 'PRO2-DEVICE-ID',
|
|
184
|
+
unlocked: true,
|
|
209
185
|
init_states: false,
|
|
210
186
|
backup_required: true,
|
|
211
|
-
|
|
187
|
+
passphrase_enabled: true,
|
|
188
|
+
attach_to_pin_enabled: true,
|
|
189
|
+
unlocked_by_attach_to_pin: true,
|
|
212
190
|
},
|
|
213
191
|
});
|
|
214
192
|
|
|
@@ -228,24 +206,47 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
228
206
|
expect(features.verify?.bleHash).toBe('0c0d');
|
|
229
207
|
expect(features.se01Version).toBe('7.8.9');
|
|
230
208
|
expect(features.verify?.se01Hash).toBe('0e0f');
|
|
231
|
-
expect(features.label).
|
|
232
|
-
expect(features.language).
|
|
209
|
+
expect(features.label).toBeNull();
|
|
210
|
+
expect(features.language).toBeNull();
|
|
233
211
|
expect(features.initialized).toBe(false);
|
|
212
|
+
expect(features.unlocked).toBe(true);
|
|
234
213
|
expect(features.backupRequired).toBe(true);
|
|
235
214
|
expect(features.passphraseProtection).toBe(true);
|
|
236
|
-
expect(features.
|
|
215
|
+
expect(features.attachToPinEnabled).toBe(true);
|
|
216
|
+
expect(features.unlockedAttachPin).toBe(true);
|
|
217
|
+
expect(features.bleEnabled).toBeNull();
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
test('marks Protocol V2 DeviceInfo without status as bootloader mode', () => {
|
|
221
|
+
const deviceInfo = {
|
|
222
|
+
protocol_version: 1,
|
|
223
|
+
hw: {
|
|
224
|
+
serial_no: 'PR2BOOT',
|
|
225
|
+
},
|
|
226
|
+
fw: {
|
|
227
|
+
bootloader: {
|
|
228
|
+
version: '0.2.0',
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
const features = normalizeProtocolV2Features(descriptor as any, deviceInfo);
|
|
233
|
+
const profile = buildProfileFromProtocolV2({ deviceInfo });
|
|
234
|
+
|
|
235
|
+
expect(features.mode).toBe('bootloader');
|
|
236
|
+
expect(features.bootloaderMode).toBe(true);
|
|
237
|
+
expect(features.initialized).toBeNull();
|
|
238
|
+
expect(profile.status.mode).toBe('bootloader');
|
|
239
|
+
expect(profile.status.bootloaderMode).toBe(true);
|
|
237
240
|
});
|
|
238
241
|
|
|
239
|
-
test('uses
|
|
242
|
+
test('uses DeviceSessionGet for Protocol V2 passphrase sessions', async () => {
|
|
240
243
|
const features = normalizeProtocolV2Features(descriptor as any);
|
|
241
|
-
// Pro2 版本线独立于 Pro 系列:V2 设备无论固件版本都支持 GetPassphraseState
|
|
242
244
|
features.firmwareVersion = '1.2.3';
|
|
243
245
|
const typedCall = jest.fn().mockResolvedValue({
|
|
244
|
-
type: '
|
|
246
|
+
type: 'DeviceSession',
|
|
245
247
|
message: {
|
|
246
|
-
|
|
248
|
+
btc_test_address: 'state-1',
|
|
247
249
|
session_id: 'session-1',
|
|
248
|
-
unlocked_attach_pin: false,
|
|
249
250
|
},
|
|
250
251
|
});
|
|
251
252
|
const commands = { typedCall } as unknown as DeviceCommands;
|
|
@@ -256,25 +257,18 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
256
257
|
await getPassphraseState(device, {
|
|
257
258
|
expectPassphraseState: 'state-1',
|
|
258
259
|
});
|
|
259
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
260
|
-
passphrase_state: 'state-1',
|
|
261
|
-
});
|
|
260
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
262
261
|
|
|
263
262
|
await getPassphraseState(device, {
|
|
264
263
|
expectPassphraseState: 'state-2',
|
|
265
264
|
allowCreateAttachPin: true,
|
|
266
265
|
});
|
|
267
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
268
|
-
passphrase_state: 'state-2',
|
|
269
|
-
allow_create_attach_pin: true,
|
|
270
|
-
});
|
|
266
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
271
267
|
|
|
272
268
|
await getPassphraseState(device, {
|
|
273
269
|
onlyMainPin: true,
|
|
274
270
|
});
|
|
275
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
276
|
-
_only_main_pin: true,
|
|
277
|
-
});
|
|
271
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
278
272
|
});
|
|
279
273
|
|
|
280
274
|
test('returns unified GetPassphraseState object payload for existing Pro devices', async () => {
|
|
@@ -302,7 +296,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
302
296
|
},
|
|
303
297
|
});
|
|
304
298
|
method.device = stubDevice({
|
|
305
|
-
originalDescriptor: { ...descriptor, protocolType: '
|
|
299
|
+
originalDescriptor: { ...descriptor, protocolType: 'V1' },
|
|
306
300
|
features,
|
|
307
301
|
commands: { typedCall },
|
|
308
302
|
updateInternalState,
|
|
@@ -335,11 +329,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
335
329
|
unlockedAttachPin: true,
|
|
336
330
|
};
|
|
337
331
|
const typedCall = jest.fn().mockResolvedValue({
|
|
338
|
-
type: '
|
|
332
|
+
type: 'DeviceSession',
|
|
339
333
|
message: {
|
|
340
|
-
|
|
334
|
+
btc_test_address: 'state-pro2',
|
|
341
335
|
session_id: 'session-pro2',
|
|
342
|
-
unlocked_attach_pin: false,
|
|
343
336
|
},
|
|
344
337
|
});
|
|
345
338
|
const updateInternalState = jest.fn();
|
|
@@ -364,7 +357,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
364
357
|
await expect(method.run()).resolves.toEqual({
|
|
365
358
|
passphraseState: 'state-pro2',
|
|
366
359
|
sessionId: 'session-pro2',
|
|
367
|
-
unlockedAttachPin:
|
|
360
|
+
unlockedAttachPin: true,
|
|
368
361
|
passphraseProtection: true,
|
|
369
362
|
});
|
|
370
363
|
expect(getFeatures).not.toHaveBeenCalled();
|
|
@@ -380,10 +373,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
380
373
|
unlockedAttachPin: false,
|
|
381
374
|
};
|
|
382
375
|
const typedCall = jest.fn().mockResolvedValue({
|
|
383
|
-
type: '
|
|
376
|
+
type: 'DeviceSession',
|
|
384
377
|
message: {
|
|
385
|
-
|
|
386
|
-
unlocked_attach_pin: false,
|
|
378
|
+
btc_test_address: 'state-pro2-new',
|
|
387
379
|
},
|
|
388
380
|
});
|
|
389
381
|
const clearInternalState = jest.fn();
|
|
@@ -424,19 +416,18 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
424
416
|
test('stores Pro2 passphrase session cache without synthetic device id', async () => {
|
|
425
417
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
426
418
|
const typedCall = jest.fn().mockResolvedValue({
|
|
427
|
-
type: '
|
|
419
|
+
type: 'DeviceSession',
|
|
428
420
|
message: {
|
|
429
|
-
|
|
421
|
+
btc_test_address: 'state-profile',
|
|
430
422
|
session_id: 'session-profile',
|
|
431
|
-
unlocked_attach_pin: false,
|
|
432
423
|
},
|
|
433
424
|
});
|
|
434
425
|
|
|
435
426
|
(device as any).features = {
|
|
436
427
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
|
|
437
428
|
hw: { serial_no: 'PR2SERIAL' },
|
|
438
|
-
fw: {
|
|
439
|
-
status: {
|
|
429
|
+
fw: { application: { version: '4.15.0' } },
|
|
430
|
+
status: { passphrase_enabled: true },
|
|
440
431
|
}),
|
|
441
432
|
unlocked: true,
|
|
442
433
|
};
|
|
@@ -446,17 +437,16 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
446
437
|
|
|
447
438
|
device.passphraseState = 'state-profile';
|
|
448
439
|
expect(device.getInternalState()).toBe('session-profile');
|
|
449
|
-
expect(device.getInternalState('
|
|
440
|
+
expect(device.getInternalState('CACHED-ID')).toBeUndefined();
|
|
450
441
|
});
|
|
451
442
|
|
|
452
443
|
test('stores Pro2 passphrase sessions without selecting them implicitly', async () => {
|
|
453
444
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
454
445
|
const typedCall = jest.fn().mockResolvedValue({
|
|
455
|
-
type: '
|
|
446
|
+
type: 'DeviceSession',
|
|
456
447
|
message: {
|
|
457
|
-
|
|
448
|
+
btc_test_address: 'state-auto',
|
|
458
449
|
session_id: 'session-auto',
|
|
459
|
-
unlocked_attach_pin: false,
|
|
460
450
|
},
|
|
461
451
|
});
|
|
462
452
|
|
|
@@ -485,18 +475,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
485
475
|
expect(device.getInternalState()).toBeUndefined();
|
|
486
476
|
device.passphraseState = 'state-auto';
|
|
487
477
|
expect(device.getInternalState()).toBe('session-auto');
|
|
488
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
489
|
-
passphrase_state: undefined,
|
|
490
|
-
});
|
|
478
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
491
479
|
});
|
|
492
480
|
|
|
493
481
|
test('does not mark Pro2 passphrase enabled from a main PIN session alone', async () => {
|
|
494
482
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
495
483
|
const typedCall = jest.fn().mockResolvedValue({
|
|
496
|
-
type: '
|
|
484
|
+
type: 'DeviceSession',
|
|
497
485
|
message: {
|
|
498
486
|
session_id: 'main-pin-session',
|
|
499
|
-
unlocked_attach_pin: false,
|
|
500
487
|
},
|
|
501
488
|
});
|
|
502
489
|
|
|
@@ -507,7 +494,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
507
494
|
} as any,
|
|
508
495
|
{
|
|
509
496
|
status: {
|
|
510
|
-
|
|
497
|
+
passphrase_enabled: false,
|
|
511
498
|
},
|
|
512
499
|
}
|
|
513
500
|
);
|
|
@@ -525,19 +512,16 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
525
512
|
expect(device.features?.passphraseProtection).toBe(false);
|
|
526
513
|
expect(device.features?.sessionId).toBeNull();
|
|
527
514
|
expect(device.getInternalState()).toBeUndefined();
|
|
528
|
-
expect(typedCall).toHaveBeenLastCalledWith('
|
|
529
|
-
_only_main_pin: true,
|
|
530
|
-
});
|
|
515
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceSessionGet', 'DeviceSession', {});
|
|
531
516
|
});
|
|
532
517
|
|
|
533
518
|
test('does not let skipPassphraseCheck hide Pro2 passphrase state mismatch', async () => {
|
|
534
519
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
535
520
|
const typedCall = jest.fn().mockResolvedValue({
|
|
536
|
-
type: '
|
|
521
|
+
type: 'DeviceSession',
|
|
537
522
|
message: {
|
|
538
|
-
|
|
523
|
+
btc_test_address: 'wrong-state',
|
|
539
524
|
session_id: 'wrong-session',
|
|
540
|
-
unlocked_attach_pin: false,
|
|
541
525
|
},
|
|
542
526
|
});
|
|
543
527
|
|
|
@@ -554,9 +538,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
554
538
|
);
|
|
555
539
|
|
|
556
540
|
expect(device.getInternalState()).toBeUndefined();
|
|
557
|
-
expect(typedCall).toHaveBeenNthCalledWith(1, '
|
|
558
|
-
passphrase_state: 'expected-state',
|
|
559
|
-
});
|
|
541
|
+
expect(typedCall).toHaveBeenNthCalledWith(1, 'DeviceSessionGet', 'DeviceSession', {});
|
|
560
542
|
});
|
|
561
543
|
|
|
562
544
|
test('marks fallback features as unavailable when DeviceInfo is missing', () => {
|
|
@@ -569,7 +551,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
569
551
|
expect(features.firmwarePresent).toBeNull();
|
|
570
552
|
});
|
|
571
553
|
|
|
572
|
-
test('uses Protocol V2
|
|
554
|
+
test('uses Protocol V2 status.device_id and does not fall back to serial_no', () => {
|
|
573
555
|
const features = normalizeProtocolV2Features(
|
|
574
556
|
{
|
|
575
557
|
id: 'PR2000000000',
|
|
@@ -579,6 +561,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
579
561
|
{
|
|
580
562
|
hw: {
|
|
581
563
|
serial_no: 'PR9999999999',
|
|
564
|
+
},
|
|
565
|
+
status: {
|
|
582
566
|
device_id: 'DEVICE-ID-9999',
|
|
583
567
|
},
|
|
584
568
|
}
|
|
@@ -610,17 +594,17 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
610
594
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
611
595
|
(device as any).features = {
|
|
612
596
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
613
|
-
serialNo: '
|
|
614
|
-
label: '
|
|
615
|
-
bleName: '
|
|
597
|
+
serialNo: 'CACHED-SERIAL',
|
|
598
|
+
label: 'Cached Label',
|
|
599
|
+
bleName: 'Cached BLE',
|
|
616
600
|
passphraseProtection: true,
|
|
617
601
|
};
|
|
618
602
|
|
|
619
603
|
expect(device.toMessageObject()).toMatchObject({
|
|
620
|
-
uuid: '
|
|
604
|
+
uuid: 'CACHED-SERIAL',
|
|
621
605
|
deviceId: null,
|
|
622
|
-
bleName: '
|
|
623
|
-
label: '
|
|
606
|
+
bleName: 'Cached BLE',
|
|
607
|
+
label: 'Cached Label',
|
|
624
608
|
deviceType: 'pro2',
|
|
625
609
|
});
|
|
626
610
|
expect(device.getCurrentPassphraseProtection()).toBe(true);
|
|
@@ -632,52 +616,53 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
632
616
|
(cached as any).features = {
|
|
633
617
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
634
618
|
deviceId: null,
|
|
635
|
-
serialNo: '
|
|
619
|
+
serialNo: 'CACHED-SERIAL',
|
|
636
620
|
};
|
|
637
621
|
|
|
638
622
|
const current = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
639
623
|
current.updateFromCache(cached);
|
|
640
624
|
|
|
641
625
|
expect(current.getCurrentDeviceId()).toBeUndefined();
|
|
642
|
-
expect(current.getCurrentSerialNo()).toBe('
|
|
626
|
+
expect(current.getCurrentSerialNo()).toBe('CACHED-SERIAL');
|
|
643
627
|
expect(current.toMessageObject()).toMatchObject({
|
|
644
|
-
uuid: '
|
|
628
|
+
uuid: 'CACHED-SERIAL',
|
|
645
629
|
deviceId: null,
|
|
646
630
|
});
|
|
647
631
|
});
|
|
648
632
|
|
|
649
|
-
test('initializes Protocol V2 features from lightweight
|
|
633
|
+
test('initializes Protocol V2 features from lightweight DeviceInfoGet', async () => {
|
|
650
634
|
const commands = {
|
|
651
635
|
typedCall: jest.fn().mockResolvedValueOnce({
|
|
652
636
|
type: 'DeviceInfo',
|
|
653
637
|
message: {
|
|
654
638
|
hw: { serial_no: 'PR2SERIAL' },
|
|
655
639
|
status: {
|
|
640
|
+
device_id: 'PRO2-STATUS-ID',
|
|
656
641
|
init_states: true,
|
|
657
|
-
|
|
642
|
+
passphrase_enabled: true,
|
|
658
643
|
},
|
|
659
644
|
},
|
|
660
645
|
}),
|
|
661
646
|
};
|
|
662
647
|
|
|
663
|
-
const features = await
|
|
648
|
+
const features = await requestProtocolV2Features({
|
|
664
649
|
commands: commands as unknown as DeviceCommands,
|
|
665
650
|
descriptor: descriptor as any,
|
|
666
651
|
});
|
|
667
652
|
|
|
668
|
-
expect(features.deviceId).
|
|
653
|
+
expect(features.deviceId).toBe('PRO2-STATUS-ID');
|
|
669
654
|
expect(features.initialized).toBe(true);
|
|
670
655
|
expect(features.passphraseProtection).toBe(true);
|
|
671
656
|
expect(commands.typedCall).toHaveBeenCalledTimes(1);
|
|
672
657
|
expect(commands.typedCall).toHaveBeenNthCalledWith(
|
|
673
658
|
1,
|
|
674
|
-
'
|
|
659
|
+
'DeviceInfoGet',
|
|
675
660
|
'DeviceInfo',
|
|
676
661
|
{
|
|
677
662
|
targets: {
|
|
678
663
|
hw: true,
|
|
679
664
|
fw: true,
|
|
680
|
-
|
|
665
|
+
coprocessor: true,
|
|
681
666
|
status: true,
|
|
682
667
|
},
|
|
683
668
|
types: {
|
|
@@ -689,13 +674,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
689
674
|
);
|
|
690
675
|
});
|
|
691
676
|
|
|
692
|
-
test('fails initialization when Protocol V2
|
|
677
|
+
test('fails initialization when Protocol V2 DeviceInfoGet fails', async () => {
|
|
693
678
|
const commands = {
|
|
694
679
|
typedCall: jest.fn().mockRejectedValueOnce(new Error('DeviceInfo not supported')),
|
|
695
680
|
};
|
|
696
681
|
|
|
697
682
|
await expect(
|
|
698
|
-
|
|
683
|
+
requestProtocolV2Features({
|
|
699
684
|
commands: commands as unknown as DeviceCommands,
|
|
700
685
|
descriptor: descriptor as any,
|
|
701
686
|
})
|
|
@@ -708,20 +693,21 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
708
693
|
message: {
|
|
709
694
|
hw: { serial_no: 'PR2SERIAL' },
|
|
710
695
|
fw: {
|
|
711
|
-
|
|
696
|
+
application: {
|
|
712
697
|
version: '5.6.7',
|
|
713
698
|
},
|
|
714
699
|
},
|
|
715
|
-
|
|
716
|
-
|
|
700
|
+
coprocessor: {
|
|
701
|
+
application: {
|
|
717
702
|
version: '8.9.10',
|
|
718
703
|
},
|
|
719
|
-
|
|
704
|
+
bt_adv_name: 'Raw Pro2 BLE',
|
|
720
705
|
},
|
|
721
706
|
status: {
|
|
707
|
+
device_id: 'PRO2-BASIC-ID',
|
|
708
|
+
unlocked_by_attach_to_pin: true,
|
|
722
709
|
init_states: true,
|
|
723
|
-
|
|
724
|
-
passphrase_protection: true,
|
|
710
|
+
passphrase_enabled: true,
|
|
725
711
|
},
|
|
726
712
|
},
|
|
727
713
|
});
|
|
@@ -738,9 +724,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
738
724
|
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
739
725
|
features: {
|
|
740
726
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
741
|
-
device_id: '
|
|
742
|
-
serial_no: '
|
|
743
|
-
label: '
|
|
727
|
+
device_id: 'CACHED-ID',
|
|
728
|
+
serial_no: 'CACHED-SERIAL',
|
|
729
|
+
label: 'Cached Pro2',
|
|
744
730
|
onekey_firmware_version: '1.2.3',
|
|
745
731
|
onekey_ble_version: '2.3.4',
|
|
746
732
|
},
|
|
@@ -751,13 +737,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
751
737
|
const result = await method.run();
|
|
752
738
|
|
|
753
739
|
expect(typedCall).toHaveBeenCalledWith(
|
|
754
|
-
'
|
|
740
|
+
'DeviceInfoGet',
|
|
755
741
|
'DeviceInfo',
|
|
756
742
|
{
|
|
757
743
|
targets: {
|
|
758
744
|
hw: true,
|
|
759
745
|
fw: true,
|
|
760
|
-
|
|
746
|
+
coprocessor: true,
|
|
761
747
|
status: true,
|
|
762
748
|
},
|
|
763
749
|
types: {
|
|
@@ -770,12 +756,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
770
756
|
expect(result).toMatchObject({
|
|
771
757
|
protocol: 'V2',
|
|
772
758
|
deviceType: 'pro2',
|
|
773
|
-
deviceId: '',
|
|
759
|
+
deviceId: 'PRO2-BASIC-ID',
|
|
774
760
|
serialNo: 'PR2SERIAL',
|
|
775
|
-
label:
|
|
761
|
+
label: null,
|
|
776
762
|
bleName: 'Raw Pro2 BLE',
|
|
777
763
|
status: {
|
|
778
764
|
initialized: true,
|
|
765
|
+
unlockedAttachPin: true,
|
|
779
766
|
passphraseProtection: true,
|
|
780
767
|
},
|
|
781
768
|
versions: {
|
|
@@ -785,7 +772,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
785
772
|
});
|
|
786
773
|
});
|
|
787
774
|
|
|
788
|
-
test('does not fill Protocol V2 DeviceProfile from
|
|
775
|
+
test('does not fill Protocol V2 DeviceProfile from cached V1-shaped fields', async () => {
|
|
789
776
|
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
790
777
|
type: 'DeviceInfo',
|
|
791
778
|
message: {
|
|
@@ -806,10 +793,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
806
793
|
originalDescriptor: { ...descriptor, protocolType: 'V2' },
|
|
807
794
|
features: {
|
|
808
795
|
...normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any),
|
|
809
|
-
device_id: '
|
|
810
|
-
serial_no: '
|
|
811
|
-
label: '
|
|
812
|
-
ble_name: '
|
|
796
|
+
device_id: 'STALE-ID',
|
|
797
|
+
serial_no: 'STALE-SERIAL',
|
|
798
|
+
label: 'Stale Pro2',
|
|
799
|
+
ble_name: 'Stale BLE',
|
|
813
800
|
passphrase_protection: true,
|
|
814
801
|
onekey_firmware_version: '1.2.3',
|
|
815
802
|
onekey_ble_version: '2.3.4',
|
|
@@ -821,7 +808,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
821
808
|
|
|
822
809
|
expect(result).toMatchObject({
|
|
823
810
|
protocol: 'V2',
|
|
824
|
-
//
|
|
811
|
+
// 身份字段不得取自缓存的 V1-shaped features(STALE-ID / STALE-SERIAL),
|
|
825
812
|
// hw.serial_no 缺失时也不回退 descriptor.path。
|
|
826
813
|
deviceId: '',
|
|
827
814
|
serialNo: '',
|
|
@@ -864,13 +851,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
864
851
|
await method.run();
|
|
865
852
|
|
|
866
853
|
expect(typedCall).toHaveBeenCalledWith(
|
|
867
|
-
'
|
|
854
|
+
'DeviceInfoGet',
|
|
868
855
|
'DeviceInfo',
|
|
869
856
|
{
|
|
870
857
|
targets: {
|
|
871
858
|
hw: true,
|
|
872
859
|
fw: true,
|
|
873
|
-
|
|
860
|
+
coprocessor: true,
|
|
874
861
|
se1: true,
|
|
875
862
|
se2: true,
|
|
876
863
|
se3: true,
|
|
@@ -894,12 +881,12 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
894
881
|
message: {
|
|
895
882
|
hw: { serial_no: 'PR2SERIAL' },
|
|
896
883
|
se1: {
|
|
897
|
-
|
|
898
|
-
|
|
884
|
+
application: { version: '1.0.1' },
|
|
885
|
+
bootloader: { version: '1.0.0' },
|
|
899
886
|
},
|
|
900
887
|
se2: {
|
|
901
|
-
|
|
902
|
-
|
|
888
|
+
application: { version: '2.0.1' },
|
|
889
|
+
bootloader: { version: '2.0.0' },
|
|
903
890
|
},
|
|
904
891
|
status: { init_states: true },
|
|
905
892
|
},
|
|
@@ -921,13 +908,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
921
908
|
const result = await method.run();
|
|
922
909
|
|
|
923
910
|
expect(typedCall).toHaveBeenCalledWith(
|
|
924
|
-
'
|
|
911
|
+
'DeviceInfoGet',
|
|
925
912
|
'DeviceInfo',
|
|
926
913
|
{
|
|
927
914
|
targets: {
|
|
928
915
|
hw: true,
|
|
929
916
|
fw: true,
|
|
930
|
-
|
|
917
|
+
coprocessor: true,
|
|
931
918
|
se1: true,
|
|
932
919
|
se2: true,
|
|
933
920
|
se3: true,
|
|
@@ -966,10 +953,23 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
966
953
|
expect(checkedTypes).toEqual(['pro2']);
|
|
967
954
|
});
|
|
968
955
|
|
|
956
|
+
test('uses firmware-v1 as the Pro2 remote firmware config field', () => {
|
|
957
|
+
const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any);
|
|
958
|
+
|
|
959
|
+
expect(
|
|
960
|
+
getFirmwareUpdateField({
|
|
961
|
+
features,
|
|
962
|
+
updateType: 'firmware',
|
|
963
|
+
firmwareType: 'universal',
|
|
964
|
+
})
|
|
965
|
+
).toBe('firmware-v1');
|
|
966
|
+
expect(getFirmwareUpdateFieldArray(features, 'firmware')).toEqual(['firmware-v1']);
|
|
967
|
+
});
|
|
968
|
+
|
|
969
969
|
test('marks known unsupported public-chain methods as unsupported on Protocol V2', () => {
|
|
970
970
|
const features = normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
|
|
971
971
|
fw: {
|
|
972
|
-
|
|
972
|
+
application: {
|
|
973
973
|
version: '9.9.9',
|
|
974
974
|
},
|
|
975
975
|
},
|
|
@@ -1019,7 +1019,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1019
1019
|
expect(isMethodVersionRangeUnsupported(neuraiRange)).toBe(true);
|
|
1020
1020
|
});
|
|
1021
1021
|
|
|
1022
|
-
test('does not block
|
|
1022
|
+
test('does not block batch public key support checks on Protocol V2', async () => {
|
|
1023
1023
|
const paths = [{ address_n: [0x8000002c, 0x80000000, 0x80000000] }] as any;
|
|
1024
1024
|
const typedCall = jest.fn().mockResolvedValue({
|
|
1025
1025
|
type: 'EcdsaPublicKeys',
|
|
@@ -1033,7 +1033,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1033
1033
|
originalDescriptor: { protocolType: 'V2' },
|
|
1034
1034
|
features: normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, {
|
|
1035
1035
|
fw: {
|
|
1036
|
-
|
|
1036
|
+
application: {
|
|
1037
1037
|
version: '4.14.0',
|
|
1038
1038
|
},
|
|
1039
1039
|
},
|
|
@@ -1055,7 +1055,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1055
1055
|
});
|
|
1056
1056
|
});
|
|
1057
1057
|
|
|
1058
|
-
test('does not call
|
|
1058
|
+
test('does not call V1 OnekeyGetFeatures for Protocol V2 devices', async () => {
|
|
1059
1059
|
const method = new GetOnekeyFeatures({
|
|
1060
1060
|
id: 1,
|
|
1061
1061
|
payload: {
|
|
@@ -1067,9 +1067,9 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1067
1067
|
message: {
|
|
1068
1068
|
protocol_version: 1,
|
|
1069
1069
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1070
|
-
fw: {
|
|
1071
|
-
|
|
1072
|
-
status: {
|
|
1070
|
+
fw: { application: { version: '1.2.3', build_id: 'app-build' } },
|
|
1071
|
+
coprocessor: { bt_adv_name: 'Pro2 BLE' },
|
|
1072
|
+
status: { init_states: true },
|
|
1073
1073
|
},
|
|
1074
1074
|
});
|
|
1075
1075
|
|
|
@@ -1080,10 +1080,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1080
1080
|
|
|
1081
1081
|
const message = await method.run();
|
|
1082
1082
|
|
|
1083
|
-
// V2 走
|
|
1083
|
+
// V2 走 DeviceInfoGet 完整请求(含 SE 与 hash/build_id),而不是 V1 OnekeyGetFeatures
|
|
1084
1084
|
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
1085
1085
|
expect(typedCall).toHaveBeenCalledWith(
|
|
1086
|
-
'
|
|
1086
|
+
'DeviceInfoGet',
|
|
1087
1087
|
'DeviceInfo',
|
|
1088
1088
|
expect.objectContaining({
|
|
1089
1089
|
targets: expect.objectContaining({ se1: true, se2: true, se3: true, se4: true }),
|
|
@@ -1106,9 +1106,10 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1106
1106
|
type: 'DeviceInfo',
|
|
1107
1107
|
message: {
|
|
1108
1108
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1109
|
-
fw: {
|
|
1109
|
+
fw: { application: { version: '1.2.3' } },
|
|
1110
1110
|
status: {
|
|
1111
|
-
|
|
1111
|
+
device_id: 'PRO2-REFRESH-ID',
|
|
1112
|
+
passphrase_enabled: true,
|
|
1112
1113
|
},
|
|
1113
1114
|
},
|
|
1114
1115
|
})
|
|
@@ -1118,8 +1119,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1118
1119
|
message: {
|
|
1119
1120
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1120
1121
|
status: {
|
|
1121
|
-
|
|
1122
|
-
|
|
1122
|
+
device_id: 'PRO2-REFRESH-ID',
|
|
1123
|
+
passphrase_enabled: false,
|
|
1123
1124
|
},
|
|
1124
1125
|
},
|
|
1125
1126
|
});
|
|
@@ -1130,27 +1131,27 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1130
1131
|
await device.initialize();
|
|
1131
1132
|
|
|
1132
1133
|
expect(device.features).toMatchObject({
|
|
1133
|
-
deviceId:
|
|
1134
|
+
deviceId: 'PRO2-REFRESH-ID',
|
|
1134
1135
|
firmwareVersion: '1.2.3',
|
|
1135
1136
|
passphraseProtection: false,
|
|
1136
|
-
label:
|
|
1137
|
+
label: null,
|
|
1137
1138
|
});
|
|
1138
1139
|
expect((device as any).profile).toBeUndefined();
|
|
1139
1140
|
// status 字段被第二次刷新更新
|
|
1140
1141
|
expect(device.features?.passphraseProtection).toBe(false);
|
|
1141
|
-
expect(device.features?.label).
|
|
1142
|
+
expect(device.features?.label).toBeNull();
|
|
1142
1143
|
// 轻量刷新不含 fw target,已有版本信息按字段级合并保留
|
|
1143
1144
|
expect(device.features?.firmwareVersion).toBe('1.2.3');
|
|
1144
1145
|
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
1145
1146
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1146
1147
|
1,
|
|
1147
|
-
'
|
|
1148
|
+
'DeviceInfoGet',
|
|
1148
1149
|
'DeviceInfo',
|
|
1149
1150
|
{
|
|
1150
1151
|
targets: {
|
|
1151
1152
|
hw: true,
|
|
1152
1153
|
fw: true,
|
|
1153
|
-
|
|
1154
|
+
coprocessor: true,
|
|
1154
1155
|
status: true,
|
|
1155
1156
|
},
|
|
1156
1157
|
types: {
|
|
@@ -1162,15 +1163,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1162
1163
|
timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS,
|
|
1163
1164
|
}
|
|
1164
1165
|
);
|
|
1165
|
-
// 第二次为 status-only 轻量请求(hw/
|
|
1166
|
+
// 第二次为 status-only 轻量请求(hw/coprocessor 仅用于身份字段,避免顶层覆盖清空)
|
|
1166
1167
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1167
1168
|
2,
|
|
1168
|
-
'
|
|
1169
|
+
'DeviceInfoGet',
|
|
1169
1170
|
'DeviceInfo',
|
|
1170
1171
|
{
|
|
1171
1172
|
targets: {
|
|
1172
1173
|
hw: true,
|
|
1173
|
-
|
|
1174
|
+
coprocessor: true,
|
|
1174
1175
|
status: true,
|
|
1175
1176
|
},
|
|
1176
1177
|
types: {
|
|
@@ -1184,7 +1185,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1184
1185
|
);
|
|
1185
1186
|
});
|
|
1186
1187
|
|
|
1187
|
-
test('refreshes Protocol V2 features without falling back to
|
|
1188
|
+
test('refreshes Protocol V2 features without falling back to V1 GetFeatures', async () => {
|
|
1188
1189
|
const device = Device.fromDescriptor({
|
|
1189
1190
|
path: 'usb-path',
|
|
1190
1191
|
protocolType: 'V2',
|
|
@@ -1195,7 +1196,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1195
1196
|
type: 'DeviceInfo',
|
|
1196
1197
|
message: {
|
|
1197
1198
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1198
|
-
fw: {
|
|
1199
|
+
fw: { application: { version: '1.2.3' } },
|
|
1199
1200
|
status: { init_states: true },
|
|
1200
1201
|
},
|
|
1201
1202
|
})
|
|
@@ -1203,8 +1204,8 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1203
1204
|
type: 'DeviceInfo',
|
|
1204
1205
|
message: {
|
|
1205
1206
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1206
|
-
fw: {
|
|
1207
|
-
status: { init_states: true,
|
|
1207
|
+
fw: { application: { version: '1.2.4' } },
|
|
1208
|
+
status: { init_states: true, passphrase_enabled: true },
|
|
1208
1209
|
},
|
|
1209
1210
|
});
|
|
1210
1211
|
|
|
@@ -1227,13 +1228,13 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1227
1228
|
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
1228
1229
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1229
1230
|
2,
|
|
1230
|
-
'
|
|
1231
|
+
'DeviceInfoGet',
|
|
1231
1232
|
'DeviceInfo',
|
|
1232
1233
|
{
|
|
1233
1234
|
targets: {
|
|
1234
1235
|
hw: true,
|
|
1235
1236
|
fw: true,
|
|
1236
|
-
|
|
1237
|
+
coprocessor: true,
|
|
1237
1238
|
status: true,
|
|
1238
1239
|
},
|
|
1239
1240
|
types: {
|
|
@@ -1246,14 +1247,14 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1246
1247
|
expect(typedCall).not.toHaveBeenCalledWith('GetFeatures', 'Features', {});
|
|
1247
1248
|
});
|
|
1248
1249
|
|
|
1249
|
-
test('keeps Protocol V2 features available for
|
|
1250
|
+
test('keeps Protocol V2 features available for method internals such as evmSignTypedData', async () => {
|
|
1250
1251
|
const device = Device.fromDescriptor({ ...descriptor, protocolType: 'V2' } as any);
|
|
1251
1252
|
const typedCall = jest.fn().mockResolvedValueOnce({
|
|
1252
1253
|
type: 'DeviceInfo',
|
|
1253
1254
|
message: {
|
|
1254
1255
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1255
|
-
fw: {
|
|
1256
|
-
status: { init_states: true,
|
|
1256
|
+
fw: { application: { version: '1.2.4' } },
|
|
1257
|
+
status: { init_states: true, passphrase_enabled: true },
|
|
1257
1258
|
},
|
|
1258
1259
|
});
|
|
1259
1260
|
(device as any).commands = { typedCall };
|
|
@@ -1307,7 +1308,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1307
1308
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
1308
1309
|
{
|
|
1309
1310
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1310
|
-
fw: {
|
|
1311
|
+
fw: { application: { version: '1.2.3' } },
|
|
1311
1312
|
status: { init_states: true },
|
|
1312
1313
|
}
|
|
1313
1314
|
);
|
|
@@ -1331,14 +1332,15 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1331
1332
|
{ ...descriptor, protocolType: 'V2' } as any,
|
|
1332
1333
|
{
|
|
1333
1334
|
hw: { serial_no: 'PR2SERIAL' },
|
|
1334
|
-
fw: {
|
|
1335
|
-
status: {
|
|
1335
|
+
fw: { application: { version: '4.15.0' } },
|
|
1336
|
+
status: { passphrase_enabled: false },
|
|
1336
1337
|
}
|
|
1337
1338
|
);
|
|
1338
1339
|
const typedCall = jest.fn().mockResolvedValue({
|
|
1339
1340
|
type: 'UnLockDeviceResponse',
|
|
1340
1341
|
message: {
|
|
1341
1342
|
unlocked: true,
|
|
1343
|
+
unlocked_attach_pin: true,
|
|
1342
1344
|
passphrase_protection: true,
|
|
1343
1345
|
},
|
|
1344
1346
|
});
|
|
@@ -1348,6 +1350,7 @@ describe('Protocol V2 feature adapter', () => {
|
|
|
1348
1350
|
|
|
1349
1351
|
expect((device as any).profile).toBeUndefined();
|
|
1350
1352
|
expect(device.features?.passphraseProtection).toBe(true);
|
|
1353
|
+
expect(device.features?.unlockedAttachPin).toBe(true);
|
|
1351
1354
|
});
|
|
1352
1355
|
});
|
|
1353
1356
|
|
|
@@ -1384,25 +1387,7 @@ describe('API compatibility handling', () => {
|
|
|
1384
1387
|
);
|
|
1385
1388
|
});
|
|
1386
1389
|
|
|
1387
|
-
test('
|
|
1388
|
-
const method = new TronSignMessage({
|
|
1389
|
-
id: 1,
|
|
1390
|
-
payload: {
|
|
1391
|
-
method: 'tronSignMessage',
|
|
1392
|
-
path: "m/44'/195'/0'/0/0",
|
|
1393
|
-
messageHex: '0x1234',
|
|
1394
|
-
messageType: 'V1',
|
|
1395
|
-
},
|
|
1396
|
-
});
|
|
1397
|
-
|
|
1398
|
-
expect(() => method.init()).toThrow(
|
|
1399
|
-
expect.objectContaining({
|
|
1400
|
-
errorCode: HardwareErrorCode.DeviceNotSupportMethod,
|
|
1401
|
-
})
|
|
1402
|
-
);
|
|
1403
|
-
});
|
|
1404
|
-
|
|
1405
|
-
test('does not mark Pro2 Tron, Solana, TON, SUI and Polkadot methods as unsupported', () => {
|
|
1390
|
+
test('does not mark supported Pro2 Tron, Solana, TON, SUI and Polkadot methods as unsupported', () => {
|
|
1406
1391
|
const features = {
|
|
1407
1392
|
deviceType: 'pro2',
|
|
1408
1393
|
} as Features;
|
|
@@ -1523,9 +1508,7 @@ describe('API compatibility handling', () => {
|
|
|
1523
1508
|
});
|
|
1524
1509
|
expect(
|
|
1525
1510
|
getMethodVersionRange(features, type => tonSignDataMethod.getVersionRange()[type])
|
|
1526
|
-
).
|
|
1527
|
-
min: '0.0.0',
|
|
1528
|
-
});
|
|
1511
|
+
).toBeUndefined();
|
|
1529
1512
|
expect(
|
|
1530
1513
|
getMethodVersionRange(features, type => suiGetAddressMethod.getVersionRange()[type])
|
|
1531
1514
|
).toEqual({
|
|
@@ -1619,15 +1602,6 @@ describe('API compatibility handling', () => {
|
|
|
1619
1602
|
});
|
|
1620
1603
|
});
|
|
1621
1604
|
|
|
1622
|
-
test('includes TON signData in the Protocol V2 protobuf schema', () => {
|
|
1623
|
-
const protocolV2Messages = DataManager.getProtobufMessages('v2Schema') as any;
|
|
1624
|
-
|
|
1625
|
-
expect(protocolV2Messages.nested.TonSignData).toBeDefined();
|
|
1626
|
-
expect(protocolV2Messages.nested.TonSignedData).toBeDefined();
|
|
1627
|
-
expect(protocolV2Messages.nested.MessageType.values.MessageType_TonSignData).toBe(11908);
|
|
1628
|
-
expect(protocolV2Messages.nested.MessageType.values.MessageType_TonSignedData).toBe(11909);
|
|
1629
|
-
});
|
|
1630
|
-
|
|
1631
1605
|
test('uses chunk transfer for large Sui transactions on Protocol V2', async () => {
|
|
1632
1606
|
const rawTx = '0x'.concat('ab'.repeat(5000));
|
|
1633
1607
|
const typedCall = jest.fn(() => ({
|
|
@@ -1672,54 +1646,6 @@ describe('API compatibility handling', () => {
|
|
|
1672
1646
|
expect(params.data_initial_chunk).toHaveLength(2048);
|
|
1673
1647
|
});
|
|
1674
1648
|
|
|
1675
|
-
test('accepts string XRP payment amount values', () => {
|
|
1676
|
-
const method = new XrpSignTransaction({
|
|
1677
|
-
id: 1,
|
|
1678
|
-
payload: {
|
|
1679
|
-
method: 'xrpSignTransaction',
|
|
1680
|
-
path: "m/44'/144'/0'/0/0",
|
|
1681
|
-
transaction: {
|
|
1682
|
-
fee: '100000',
|
|
1683
|
-
flags: 2147483648,
|
|
1684
|
-
sequence: 25,
|
|
1685
|
-
maxLedgerVersion: 8820051,
|
|
1686
|
-
payment: {
|
|
1687
|
-
amount: '100000000',
|
|
1688
|
-
destination: 'rBKz5MC2iXdoS3XgnNSYmF69K1Yo4NS3Ws',
|
|
1689
|
-
},
|
|
1690
|
-
},
|
|
1691
|
-
},
|
|
1692
|
-
});
|
|
1693
|
-
|
|
1694
|
-
expect(() => method.init()).not.toThrow();
|
|
1695
|
-
expect(method.params.payment?.amount).toBe('100000000');
|
|
1696
|
-
});
|
|
1697
|
-
|
|
1698
|
-
test('accepts Conflux base32 recipient addresses without hex formatting them', () => {
|
|
1699
|
-
const to = 'cfx:aak2rra2njvd77ezwjvx04kkds9fzagfe6ku8scz91';
|
|
1700
|
-
const method = new ConfluxSignTransaction({
|
|
1701
|
-
id: 1,
|
|
1702
|
-
payload: {
|
|
1703
|
-
method: 'confluxSignTransaction',
|
|
1704
|
-
path: "m/44'/503'/0'/0/0",
|
|
1705
|
-
transaction: {
|
|
1706
|
-
to,
|
|
1707
|
-
value: '0x0',
|
|
1708
|
-
data: '0x',
|
|
1709
|
-
chainId: 1,
|
|
1710
|
-
nonce: '0x00',
|
|
1711
|
-
epochHeight: '0x00',
|
|
1712
|
-
gasLimit: '0x5208',
|
|
1713
|
-
storageLimit: '0x5208',
|
|
1714
|
-
gasPrice: '0xbebc200',
|
|
1715
|
-
},
|
|
1716
|
-
},
|
|
1717
|
-
});
|
|
1718
|
-
|
|
1719
|
-
expect(() => method.init()).not.toThrow();
|
|
1720
|
-
expect(method.formattedTx?.to).toBe(to);
|
|
1721
|
-
});
|
|
1722
|
-
|
|
1723
1649
|
test('returns a typed unsupported error for Dynex signing on Protocol V2', async () => {
|
|
1724
1650
|
const method = new DnxSignTransaction({
|
|
1725
1651
|
id: 1,
|
|
@@ -1775,7 +1701,41 @@ describe('API compatibility handling', () => {
|
|
|
1775
1701
|
});
|
|
1776
1702
|
|
|
1777
1703
|
describe('Protocol V2 firmware update targets', () => {
|
|
1778
|
-
|
|
1704
|
+
const OKPP_HEADER_SIZE = 0x52a0;
|
|
1705
|
+
|
|
1706
|
+
const buildOkppHeader = ({
|
|
1707
|
+
type = 'RESC',
|
|
1708
|
+
version = [1, 0, 0],
|
|
1709
|
+
payloadHash = 'ab'.repeat(64),
|
|
1710
|
+
headerHash = 'cd'.repeat(64),
|
|
1711
|
+
}: {
|
|
1712
|
+
type?: string;
|
|
1713
|
+
version?: [number, number, number];
|
|
1714
|
+
payloadHash?: string;
|
|
1715
|
+
headerHash?: string;
|
|
1716
|
+
} = {}) => {
|
|
1717
|
+
const header = new Uint8Array(OKPP_HEADER_SIZE);
|
|
1718
|
+
const view = new DataView(header.buffer);
|
|
1719
|
+
'OKPP'.split('').forEach((char, index) => {
|
|
1720
|
+
header[index] = char.charCodeAt(0);
|
|
1721
|
+
});
|
|
1722
|
+
view.setUint32(0x04, 1, true);
|
|
1723
|
+
type.split('').forEach((char, index) => {
|
|
1724
|
+
header[0x08 + index] = char.charCodeAt(0);
|
|
1725
|
+
});
|
|
1726
|
+
view.setUint32(0x0c, OKPP_HEADER_SIZE, true);
|
|
1727
|
+
view.setUint32(0x10, version[0] * 0x10000 + version[1] * 0x100 + version[2], true);
|
|
1728
|
+
const writeHex = (offset: number, hex: string) => {
|
|
1729
|
+
for (let i = 0; i < hex.length / 2; i++) {
|
|
1730
|
+
header[offset + i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
writeHex(0x200, payloadHash);
|
|
1734
|
+
writeHex(0x240, headerHash);
|
|
1735
|
+
return header;
|
|
1736
|
+
};
|
|
1737
|
+
|
|
1738
|
+
test('keeps Protocol V2 firmware updates off the firmwareUpdateV3 path', async () => {
|
|
1779
1739
|
const method = new FirmwareUpdateV3({
|
|
1780
1740
|
id: 1,
|
|
1781
1741
|
payload: {
|
|
@@ -1789,25 +1749,26 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1789
1749
|
await expect(method.run()).rejects.toThrow('firmwareUpdateV4');
|
|
1790
1750
|
});
|
|
1791
1751
|
|
|
1792
|
-
test('uses Protocol V2 features after BLE final reconnect without
|
|
1752
|
+
test('uses Protocol V2 features after BLE final reconnect without V1 Initialize', async () => {
|
|
1793
1753
|
const method = new FirmwareUpdateV4({
|
|
1794
1754
|
id: 1,
|
|
1795
1755
|
payload: {
|
|
1796
1756
|
method: 'firmwareUpdateV4',
|
|
1757
|
+
coprocessorBinary: new Uint8Array([1, 2, 3]).buffer,
|
|
1797
1758
|
},
|
|
1798
1759
|
});
|
|
1799
1760
|
const acquire = jest.fn().mockResolvedValue({ uuid: 'ble-session' });
|
|
1800
1761
|
const typedCall = jest.fn().mockImplementation((name: string) => {
|
|
1801
|
-
if (name === '
|
|
1762
|
+
if (name === 'DeviceInfoGet') {
|
|
1802
1763
|
return Promise.resolve({
|
|
1803
1764
|
type: 'DeviceInfo',
|
|
1804
1765
|
message: {
|
|
1805
1766
|
fw: {
|
|
1806
|
-
|
|
1807
|
-
|
|
1767
|
+
bootloader: { version: '0.0.0' },
|
|
1768
|
+
application: { version: '0.0.0' },
|
|
1808
1769
|
},
|
|
1809
|
-
|
|
1810
|
-
|
|
1770
|
+
coprocessor: {
|
|
1771
|
+
application: { version: '0.0.0' },
|
|
1811
1772
|
},
|
|
1812
1773
|
},
|
|
1813
1774
|
});
|
|
@@ -1821,7 +1782,13 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1821
1782
|
originalDescriptor: { id: 'ble-id', path: 'ble-path', protocolType: 'V2' },
|
|
1822
1783
|
deviceConnector: { acquire },
|
|
1823
1784
|
getCommands: () => commands,
|
|
1824
|
-
|
|
1785
|
+
updateProtocolV2Features: jest.fn(() => ({
|
|
1786
|
+
bootloaderMode: false,
|
|
1787
|
+
mode: 'normal',
|
|
1788
|
+
firmwareVersion: '0.0.0',
|
|
1789
|
+
bootloaderVersion: '0.0.0',
|
|
1790
|
+
bleVersion: '0.0.0',
|
|
1791
|
+
})),
|
|
1825
1792
|
});
|
|
1826
1793
|
|
|
1827
1794
|
const versions = await (method as any).waitForProtocolV2FinalFeatures();
|
|
@@ -1830,13 +1797,13 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1830
1797
|
// 更新完成判定使用 VERSIONS 请求(含 SE targets),scope 与请求内容一致
|
|
1831
1798
|
expect(typedCall).toHaveBeenNthCalledWith(
|
|
1832
1799
|
1,
|
|
1833
|
-
'
|
|
1800
|
+
'DeviceInfoGet',
|
|
1834
1801
|
'DeviceInfo',
|
|
1835
1802
|
{
|
|
1836
1803
|
targets: {
|
|
1837
1804
|
hw: true,
|
|
1838
1805
|
fw: true,
|
|
1839
|
-
|
|
1806
|
+
coprocessor: true,
|
|
1840
1807
|
se1: true,
|
|
1841
1808
|
se2: true,
|
|
1842
1809
|
se3: true,
|
|
@@ -1859,7 +1826,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1859
1826
|
});
|
|
1860
1827
|
});
|
|
1861
1828
|
|
|
1862
|
-
test('
|
|
1829
|
+
test('enters Protocol V2 bootloader before upload', async () => {
|
|
1863
1830
|
const method = new FirmwareUpdateV4({
|
|
1864
1831
|
id: 1,
|
|
1865
1832
|
payload: {
|
|
@@ -1872,14 +1839,15 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1872
1839
|
originalDescriptor: { id: 'ble-id', path: 'ble-path', protocolType: 'V2' },
|
|
1873
1840
|
features: { capabilities: [] },
|
|
1874
1841
|
});
|
|
1875
|
-
(method as any).
|
|
1876
|
-
(method as any).
|
|
1842
|
+
(method as any).prepareResourceBinaries = jest.fn().mockResolvedValue([]);
|
|
1843
|
+
(method as any).prepareBootloaderBinary = jest.fn().mockReturnValue(null);
|
|
1844
|
+
(method as any).collectExplicitTargetBinaries = jest.fn().mockReturnValue([
|
|
1877
1845
|
{
|
|
1878
|
-
fileName: '
|
|
1846
|
+
fileName: 'coprocessor.bin',
|
|
1879
1847
|
binary: new Uint8Array([1, 2, 3]).buffer,
|
|
1848
|
+
targetId: 6,
|
|
1880
1849
|
},
|
|
1881
1850
|
]);
|
|
1882
|
-
(method as any).prepareBootloaderBinary = jest.fn().mockResolvedValue(null);
|
|
1883
1851
|
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
1884
1852
|
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
1885
1853
|
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
@@ -1893,17 +1861,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1893
1861
|
|
|
1894
1862
|
await method.run();
|
|
1895
1863
|
|
|
1864
|
+
expect((method as any).enterProtocolV2BootloaderMode).toHaveBeenCalledTimes(1);
|
|
1896
1865
|
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
1897
|
-
|
|
1866
|
+
resourceBinaryMap: [],
|
|
1898
1867
|
fwBinaryMap: [
|
|
1899
1868
|
{
|
|
1900
|
-
fileName: '
|
|
1869
|
+
fileName: 'coprocessor.bin',
|
|
1901
1870
|
binary: expect.any(ArrayBuffer),
|
|
1871
|
+
targetId: 6,
|
|
1902
1872
|
},
|
|
1903
1873
|
],
|
|
1904
1874
|
bootloaderBinary: null,
|
|
1905
1875
|
});
|
|
1906
|
-
expect((method as any).enterProtocolV2BootloaderMode).not.toHaveBeenCalled();
|
|
1907
1876
|
});
|
|
1908
1877
|
|
|
1909
1878
|
test('reboots Protocol V2 normal-mode device to bootloader before transfer', async () => {
|
|
@@ -1913,31 +1882,70 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1913
1882
|
method: 'firmwareUpdateV4',
|
|
1914
1883
|
},
|
|
1915
1884
|
});
|
|
1916
|
-
const acquire = jest.fn().mockResolvedValue(undefined);
|
|
1917
1885
|
(method as any).device = stubDevice({
|
|
1918
1886
|
originalDescriptor: { id: 'usb-id', path: 'usb-path', protocolType: 'V2' },
|
|
1919
1887
|
features: { bootloader_mode: false, capabilities: [] },
|
|
1920
1888
|
isBootloader: () => false,
|
|
1921
|
-
acquire,
|
|
1922
1889
|
});
|
|
1923
1890
|
(method as any).protocolV2Reboot = jest.fn().mockResolvedValue({
|
|
1924
1891
|
message: 'Device rebooted successfully',
|
|
1925
1892
|
});
|
|
1926
|
-
(method as any).checkDeviceToBootloader = jest.fn(
|
|
1927
|
-
|
|
1893
|
+
(method as any).checkDeviceToBootloader = jest.fn();
|
|
1894
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
1895
|
+
type: 'DeviceInfo',
|
|
1896
|
+
message: protocolV2BootloaderDeviceInfo,
|
|
1897
|
+
});
|
|
1898
|
+
const reconnectProtocolV2Device = jest.fn().mockImplementation(() => {
|
|
1899
|
+
(method as any).device.isBootloader = () => true;
|
|
1900
|
+
return Promise.resolve();
|
|
1928
1901
|
});
|
|
1902
|
+
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
1903
|
+
(method as any).device.getCommands = () => ({ typedCall });
|
|
1929
1904
|
method.postTipMessage = jest.fn();
|
|
1930
1905
|
|
|
1931
1906
|
await (method as any).enterProtocolV2BootloaderMode();
|
|
1932
1907
|
|
|
1933
1908
|
expect(method.postTipMessage).toHaveBeenCalledWith('AutoRebootToBootloader');
|
|
1934
|
-
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(
|
|
1909
|
+
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(DeviceRebootType.Bootloader);
|
|
1935
1910
|
expect(method.postTipMessage).toHaveBeenCalledWith('GoToBootloaderSuccess');
|
|
1936
|
-
expect((method as any).checkDeviceToBootloader).
|
|
1937
|
-
expect(
|
|
1911
|
+
expect((method as any).checkDeviceToBootloader).not.toHaveBeenCalled();
|
|
1912
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
1913
|
+
});
|
|
1914
|
+
|
|
1915
|
+
test('polls until Protocol V2 bootloader descriptor is ready after reboot', async () => {
|
|
1916
|
+
const method = new FirmwareUpdateV4({
|
|
1917
|
+
id: 1,
|
|
1918
|
+
payload: {
|
|
1919
|
+
method: 'firmwareUpdateV4',
|
|
1920
|
+
},
|
|
1921
|
+
});
|
|
1922
|
+
(method as any).device = stubDevice({
|
|
1923
|
+
originalDescriptor: { id: 'usb-id', path: 'app-path', protocolType: 'V2' },
|
|
1924
|
+
features: { bootloader_mode: false, capabilities: [] },
|
|
1925
|
+
isBootloader: () => false,
|
|
1926
|
+
});
|
|
1927
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
1928
|
+
type: 'DeviceInfo',
|
|
1929
|
+
message: protocolV2BootloaderDeviceInfo,
|
|
1930
|
+
});
|
|
1931
|
+
const reconnectProtocolV2Device = jest
|
|
1932
|
+
.fn()
|
|
1933
|
+
.mockRejectedValueOnce(new Error('Device not found'))
|
|
1934
|
+
.mockRejectedValueOnce(new Error('Device not found'))
|
|
1935
|
+
.mockImplementation(() => {
|
|
1936
|
+
(method as any).device.isBootloader = () => true;
|
|
1937
|
+
return Promise.resolve();
|
|
1938
|
+
});
|
|
1939
|
+
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
1940
|
+
(method as any).device.getCommands = () => ({ typedCall });
|
|
1941
|
+
|
|
1942
|
+
await (method as any).waitForProtocolV2BootloaderMode(60 * 1000, 0);
|
|
1943
|
+
|
|
1944
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(3);
|
|
1945
|
+
expect(typedCall).toHaveBeenCalledTimes(1);
|
|
1938
1946
|
});
|
|
1939
1947
|
|
|
1940
|
-
test('reboots Protocol V2 firmware flow back to normal
|
|
1948
|
+
test('reboots Protocol V2 firmware flow back to normal after install status is finished', async () => {
|
|
1941
1949
|
const method = new FirmwareUpdateV4({
|
|
1942
1950
|
id: 1,
|
|
1943
1951
|
payload: {
|
|
@@ -1952,7 +1960,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1952
1960
|
await (method as any).exitProtocolV2BootloaderToNormal();
|
|
1953
1961
|
|
|
1954
1962
|
expect(method.postTipMessage).toHaveBeenCalledWith('SwitchFirmwareReconnectDevice');
|
|
1955
|
-
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(
|
|
1963
|
+
expect((method as any).protocolV2Reboot).toHaveBeenCalledWith(DeviceRebootType.Normal);
|
|
1956
1964
|
});
|
|
1957
1965
|
|
|
1958
1966
|
test('treats iOS BLE RxError 6 during Protocol V2 reboot as expected disconnect', async () => {
|
|
@@ -1972,7 +1980,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1972
1980
|
getCommands: () => ({ typedCall }),
|
|
1973
1981
|
});
|
|
1974
1982
|
|
|
1975
|
-
await expect((method as any).protocolV2Reboot(
|
|
1983
|
+
await expect((method as any).protocolV2Reboot(DeviceRebootType.Normal)).resolves.toEqual({
|
|
1976
1984
|
message: 'Device rebooted successfully',
|
|
1977
1985
|
});
|
|
1978
1986
|
});
|
|
@@ -1992,7 +2000,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
1992
2000
|
getCommands: () => ({ typedCall }),
|
|
1993
2001
|
});
|
|
1994
2002
|
|
|
1995
|
-
await expect((method as any).protocolV2Reboot(
|
|
2003
|
+
await expect((method as any).protocolV2Reboot(DeviceRebootType.Normal)).resolves.toEqual({
|
|
1996
2004
|
message: 'Device rebooted successfully',
|
|
1997
2005
|
});
|
|
1998
2006
|
});
|
|
@@ -2020,7 +2028,30 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2020
2028
|
|
|
2021
2029
|
await expect(
|
|
2022
2030
|
(method as any).protocolV2StartFirmwareUpdate({
|
|
2023
|
-
targets: [{ target_id:
|
|
2031
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2032
|
+
})
|
|
2033
|
+
).resolves.toBeUndefined();
|
|
2034
|
+
|
|
2035
|
+
expect(method.postTipMessage).toHaveBeenCalledWith('FirmwareUpdating');
|
|
2036
|
+
});
|
|
2037
|
+
|
|
2038
|
+
test('continues Protocol V2 install polling when update request transfer times out', async () => {
|
|
2039
|
+
const method = new FirmwareUpdateV4({
|
|
2040
|
+
id: 1,
|
|
2041
|
+
payload: {
|
|
2042
|
+
method: 'firmwareUpdateV4',
|
|
2043
|
+
},
|
|
2044
|
+
});
|
|
2045
|
+
const typedCall = jest.fn().mockRejectedValue(new Error('LIBUSB_TRANSFER_TIMED_OUT'));
|
|
2046
|
+
|
|
2047
|
+
(method as any).device = stubDevice({
|
|
2048
|
+
getCommands: () => ({ typedCall }),
|
|
2049
|
+
});
|
|
2050
|
+
method.postTipMessage = jest.fn();
|
|
2051
|
+
|
|
2052
|
+
await expect(
|
|
2053
|
+
(method as any).protocolV2StartFirmwareUpdate({
|
|
2054
|
+
targets: [{ target_id: 1, path: 'vol1:resource.bin' }],
|
|
2024
2055
|
})
|
|
2025
2056
|
).resolves.toBeUndefined();
|
|
2026
2057
|
|
|
@@ -2034,19 +2065,31 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2034
2065
|
method: 'firmwareUpdateV4',
|
|
2035
2066
|
},
|
|
2036
2067
|
});
|
|
2037
|
-
const typedCall = jest
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2068
|
+
const typedCall = jest.fn().mockImplementation((name: string) => {
|
|
2069
|
+
if (name === 'DeviceFirmwareUpdateStatusGet') {
|
|
2070
|
+
const callCount = typedCall.mock.calls.filter(call => call[0] === name).length;
|
|
2071
|
+
if (callCount === 1) {
|
|
2072
|
+
return Promise.reject(
|
|
2073
|
+
new Error(
|
|
2074
|
+
'Device protocol mismatch: expected V2, but device did not respond to expected protocol'
|
|
2075
|
+
)
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
return Promise.resolve({
|
|
2079
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
2080
|
+
message: {
|
|
2081
|
+
records: [{ target_id: 6, status: 2 }],
|
|
2082
|
+
},
|
|
2083
|
+
});
|
|
2084
|
+
}
|
|
2085
|
+
if (name === 'DeviceInfoGet') {
|
|
2086
|
+
return Promise.reject(new Error('DeviceInfo not ready'));
|
|
2087
|
+
}
|
|
2088
|
+
if (name === 'Ping') {
|
|
2089
|
+
return Promise.resolve({ type: 'Success', message: { message: 'ready' } });
|
|
2090
|
+
}
|
|
2091
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2092
|
+
});
|
|
2050
2093
|
const reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
2051
2094
|
|
|
2052
2095
|
(method as any).device = stubDevice({
|
|
@@ -2056,14 +2099,75 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2056
2099
|
method.postProgressMessage = jest.fn();
|
|
2057
2100
|
|
|
2058
2101
|
await (method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
2059
|
-
{ target_id:
|
|
2102
|
+
{ target_id: 6, path: 'vol1:ble-firmware.bin' },
|
|
2060
2103
|
]);
|
|
2061
2104
|
|
|
2062
2105
|
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
2063
|
-
expect(typedCall).
|
|
2106
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
2107
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2108
|
+
'DeviceInfoGet',
|
|
2109
|
+
'Ping',
|
|
2110
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2111
|
+
]);
|
|
2112
|
+
});
|
|
2113
|
+
|
|
2114
|
+
test('treats Protocol V2 normal mode after reconnect as firmware update complete', async () => {
|
|
2115
|
+
const method = new FirmwareUpdateV4({
|
|
2116
|
+
id: 1,
|
|
2117
|
+
payload: {
|
|
2118
|
+
method: 'firmwareUpdateV4',
|
|
2119
|
+
},
|
|
2120
|
+
});
|
|
2121
|
+
const typedCall = jest.fn().mockImplementation((name: string) => {
|
|
2122
|
+
if (name === 'DeviceFirmwareUpdateStatusGet') {
|
|
2123
|
+
return Promise.reject(new Error('Device disconnected'));
|
|
2124
|
+
}
|
|
2125
|
+
if (name === 'DeviceInfoGet') {
|
|
2126
|
+
return Promise.resolve({
|
|
2127
|
+
type: 'DeviceInfo',
|
|
2128
|
+
message: {
|
|
2129
|
+
hw: { serial_no: 'PR9999999999' },
|
|
2130
|
+
fw: {
|
|
2131
|
+
bootloader: { version: '9.9.9' },
|
|
2132
|
+
application: { version: '9.9.9' },
|
|
2133
|
+
},
|
|
2134
|
+
status: {
|
|
2135
|
+
device_id: 'PRO2-DEVICE-ID',
|
|
2136
|
+
init_states: true,
|
|
2137
|
+
},
|
|
2138
|
+
},
|
|
2139
|
+
});
|
|
2140
|
+
}
|
|
2141
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2142
|
+
});
|
|
2143
|
+
const reconnectProtocolV2Device = jest.fn().mockResolvedValue(undefined);
|
|
2144
|
+
const updateProtocolV2Features = jest.fn((deviceInfo: ProtocolV2DeviceInfo) =>
|
|
2145
|
+
normalizeProtocolV2Features({ ...descriptor, protocolType: 'V2' } as any, deviceInfo)
|
|
2146
|
+
);
|
|
2147
|
+
|
|
2148
|
+
(method as any).device = stubDevice({
|
|
2149
|
+
getCommands: () => ({ typedCall }),
|
|
2150
|
+
updateProtocolV2Features,
|
|
2151
|
+
});
|
|
2152
|
+
(method as any).reconnectProtocolV2Device = reconnectProtocolV2Device;
|
|
2153
|
+
method.postProgressMessage = jest.fn();
|
|
2154
|
+
|
|
2155
|
+
await expect(
|
|
2156
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete([
|
|
2157
|
+
{ target_id: 3, path: 'vol1:bootloader.bin' },
|
|
2158
|
+
{ target_id: 4, path: 'vol1:application_p1.bin' },
|
|
2159
|
+
])
|
|
2160
|
+
).resolves.toBeUndefined();
|
|
2161
|
+
|
|
2162
|
+
expect(reconnectProtocolV2Device).toHaveBeenCalledTimes(1);
|
|
2163
|
+
expect(updateProtocolV2Features).toHaveBeenCalledTimes(1);
|
|
2164
|
+
expect(typedCall.mock.calls.map(call => call[0])).toEqual([
|
|
2165
|
+
'DeviceFirmwareUpdateStatusGet',
|
|
2166
|
+
'DeviceInfoGet',
|
|
2167
|
+
]);
|
|
2064
2168
|
});
|
|
2065
2169
|
|
|
2066
|
-
test('uses
|
|
2170
|
+
test('uses SDK decoded enum names for Protocol V2 install polling', () => {
|
|
2067
2171
|
const method = new FirmwareUpdateV4({
|
|
2068
2172
|
id: 1,
|
|
2069
2173
|
payload: {
|
|
@@ -2072,18 +2176,36 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2072
2176
|
});
|
|
2073
2177
|
method.postProgressMessage = jest.fn();
|
|
2074
2178
|
|
|
2075
|
-
const expectedTargetIds = new Set([
|
|
2179
|
+
const expectedTargetIds = new Set([4]);
|
|
2076
2180
|
|
|
2181
|
+
expect(
|
|
2182
|
+
(method as any).assertProtocolV2TargetStatus([{ target_id: 4, status: 2 }], expectedTargetIds)
|
|
2183
|
+
).toBe(true);
|
|
2077
2184
|
expect(
|
|
2078
2185
|
(method as any).assertProtocolV2TargetStatus(
|
|
2079
|
-
[{ target_id:
|
|
2186
|
+
[{ target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED' }],
|
|
2080
2187
|
expectedTargetIds
|
|
2081
2188
|
)
|
|
2082
2189
|
).toBe(true);
|
|
2190
|
+
expect(
|
|
2191
|
+
(method as any).assertProtocolV2TargetStatus(
|
|
2192
|
+
[
|
|
2193
|
+
{
|
|
2194
|
+
target_id: 'FW_MGMT_TARGET_CRATE',
|
|
2195
|
+
status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED',
|
|
2196
|
+
},
|
|
2197
|
+
{ target_id: 'FW_MGMT_TARGET_SE04', status: 'FW_MGMT_UPDATER_TASK_STATUS_FINISHED' },
|
|
2198
|
+
],
|
|
2199
|
+
new Set([1, 10])
|
|
2200
|
+
)
|
|
2201
|
+
).toBe(true);
|
|
2083
2202
|
|
|
2203
|
+
expect(
|
|
2204
|
+
(method as any).assertProtocolV2TargetStatus([{ target_id: 4, status: 1 }], expectedTargetIds)
|
|
2205
|
+
).toBe(false);
|
|
2084
2206
|
expect(
|
|
2085
2207
|
(method as any).assertProtocolV2TargetStatus(
|
|
2086
|
-
[{ target_id:
|
|
2208
|
+
[{ target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_IN_PROGRESS' }],
|
|
2087
2209
|
expectedTargetIds
|
|
2088
2210
|
)
|
|
2089
2211
|
).toBe(false);
|
|
@@ -2091,7 +2213,17 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2091
2213
|
|
|
2092
2214
|
try {
|
|
2093
2215
|
(method as any).assertProtocolV2TargetStatus(
|
|
2094
|
-
[{ target_id:
|
|
2216
|
+
[{ target_id: 4, status: 3 }],
|
|
2217
|
+
expectedTargetIds
|
|
2218
|
+
);
|
|
2219
|
+
throw new Error('Expected Protocol V2 failed firmware status to throw');
|
|
2220
|
+
} catch (error: any) {
|
|
2221
|
+
expect(error.errorCode).toBe(HardwareErrorCode.FirmwareError);
|
|
2222
|
+
}
|
|
2223
|
+
|
|
2224
|
+
try {
|
|
2225
|
+
(method as any).assertProtocolV2TargetStatus(
|
|
2226
|
+
[{ target_id: 4, status: 'FW_MGMT_UPDATER_TASK_STATUS_FAILED_VERIFY' }],
|
|
2095
2227
|
expectedTargetIds
|
|
2096
2228
|
);
|
|
2097
2229
|
throw new Error('Expected Protocol V2 failed firmware status to throw');
|
|
@@ -2100,7 +2232,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2100
2232
|
}
|
|
2101
2233
|
});
|
|
2102
2234
|
|
|
2103
|
-
test('passes resource, bootloader,
|
|
2235
|
+
test('passes resource, bootloader, coprocessor, SE and app files to DeviceFirmwareUpdate targets', async () => {
|
|
2104
2236
|
const method = new FirmwareUpdateV4({
|
|
2105
2237
|
id: 1,
|
|
2106
2238
|
payload: {
|
|
@@ -2121,21 +2253,29 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2121
2253
|
.mockResolvedValue(undefined);
|
|
2122
2254
|
|
|
2123
2255
|
await (method as any).executeProtocolV2Update({
|
|
2124
|
-
|
|
2125
|
-
|
|
2256
|
+
resourceBinaryMap: [
|
|
2257
|
+
{
|
|
2258
|
+
fileName: 'resource.bin',
|
|
2259
|
+
binary: new Uint8Array([1, 2, 3]).buffer,
|
|
2260
|
+
targetId: 1,
|
|
2261
|
+
},
|
|
2262
|
+
],
|
|
2126
2263
|
bootloaderBinary: new Uint8Array([4, 5]).buffer,
|
|
2127
2264
|
fwBinaryMap: [
|
|
2128
2265
|
{
|
|
2129
|
-
fileName: '
|
|
2266
|
+
fileName: 'coprocessor.bin',
|
|
2130
2267
|
binary: new Uint8Array([6]).buffer,
|
|
2268
|
+
targetId: 6,
|
|
2131
2269
|
},
|
|
2132
2270
|
{
|
|
2133
|
-
fileName: '
|
|
2271
|
+
fileName: 'se01.bin',
|
|
2134
2272
|
binary: new Uint8Array([7]).buffer,
|
|
2273
|
+
targetId: 7,
|
|
2135
2274
|
},
|
|
2136
2275
|
{
|
|
2137
|
-
fileName: '
|
|
2276
|
+
fileName: 'application_p1.bin',
|
|
2138
2277
|
binary: new Uint8Array([8]).buffer,
|
|
2278
|
+
targetId: 4,
|
|
2139
2279
|
},
|
|
2140
2280
|
],
|
|
2141
2281
|
});
|
|
@@ -2143,18 +2283,19 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2143
2283
|
expect(writtenPaths).toEqual([
|
|
2144
2284
|
'vol1:resource.bin',
|
|
2145
2285
|
'vol1:bootloader.bin',
|
|
2146
|
-
'vol1:
|
|
2147
|
-
'vol1:
|
|
2148
|
-
'vol1:
|
|
2286
|
+
'vol1:coprocessor.bin',
|
|
2287
|
+
'vol1:se01.bin',
|
|
2288
|
+
'vol1:application_p1.bin',
|
|
2149
2289
|
]);
|
|
2150
|
-
|
|
2151
|
-
|
|
2290
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
|
|
2291
|
+
targets: [{ target_id: 1, path: 'vol1:resource.bin' }],
|
|
2292
|
+
});
|
|
2293
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
2152
2294
|
targets: [
|
|
2153
|
-
{ target_id:
|
|
2154
|
-
{ target_id:
|
|
2155
|
-
{ target_id:
|
|
2156
|
-
{ target_id:
|
|
2157
|
-
{ target_id: 0, path: 'vol1:firmware.bin' },
|
|
2295
|
+
{ target_id: 3, path: 'vol1:bootloader.bin' },
|
|
2296
|
+
{ target_id: 6, path: 'vol1:coprocessor.bin' },
|
|
2297
|
+
{ target_id: 7, path: 'vol1:se01.bin' },
|
|
2298
|
+
{ target_id: 4, path: 'vol1:application_p1.bin' },
|
|
2158
2299
|
],
|
|
2159
2300
|
});
|
|
2160
2301
|
expect(method.postProgressMessage).toHaveBeenCalledWith(100, 'transferData');
|
|
@@ -2167,7 +2308,6 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2167
2308
|
payload: {
|
|
2168
2309
|
method: 'firmwareUpdateV4',
|
|
2169
2310
|
platform: 'web',
|
|
2170
|
-
romloaderBinary: new Uint8Array([1]).buffer,
|
|
2171
2311
|
applicationP2Binary: new Uint8Array([2]).buffer,
|
|
2172
2312
|
se04Binary: new Uint8Array([3]).buffer,
|
|
2173
2313
|
},
|
|
@@ -2176,9 +2316,8 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2176
2316
|
|
|
2177
2317
|
const explicit = (method as any).collectExplicitTargetBinaries();
|
|
2178
2318
|
expect(explicit).toEqual([
|
|
2179
|
-
{ fileName: '
|
|
2180
|
-
{ fileName: '
|
|
2181
|
-
{ fileName: 'se04.bin', binary: expect.anything(), targetId: 6 },
|
|
2319
|
+
{ fileName: 'application_p2.bin', binary: expect.anything(), targetId: 5 },
|
|
2320
|
+
{ fileName: 'se04.bin', binary: expect.anything(), targetId: 10 },
|
|
2182
2321
|
]);
|
|
2183
2322
|
|
|
2184
2323
|
method.postTipMessage = jest.fn();
|
|
@@ -2194,43 +2333,732 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2194
2333
|
.mockResolvedValue(undefined);
|
|
2195
2334
|
|
|
2196
2335
|
await (method as any).executeProtocolV2Update({
|
|
2197
|
-
|
|
2336
|
+
resourceBinaryMap: [],
|
|
2198
2337
|
bootloaderBinary: null,
|
|
2199
2338
|
fwBinaryMap: explicit,
|
|
2200
2339
|
});
|
|
2201
2340
|
|
|
2202
2341
|
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenCalledWith({
|
|
2203
2342
|
targets: [
|
|
2204
|
-
{ target_id:
|
|
2205
|
-
{ target_id:
|
|
2206
|
-
{ target_id: 6, path: 'vol1:se04.bin' },
|
|
2343
|
+
{ target_id: 5, path: 'vol1:application_p2.bin' },
|
|
2344
|
+
{ target_id: 10, path: 'vol1:se04.bin' },
|
|
2207
2345
|
],
|
|
2208
2346
|
});
|
|
2209
2347
|
});
|
|
2210
2348
|
|
|
2211
|
-
test('
|
|
2349
|
+
test('maps explicit Pro2 resource crate binaries to separate CRATE targets', async () => {
|
|
2350
|
+
const firstResource = new Uint8Array([1]).buffer;
|
|
2351
|
+
const secondResource = new Uint8Array([2]).buffer;
|
|
2212
2352
|
const method = new FirmwareUpdateV4({
|
|
2213
2353
|
id: 1,
|
|
2214
2354
|
payload: {
|
|
2215
2355
|
method: 'firmwareUpdateV4',
|
|
2356
|
+
platform: 'web',
|
|
2357
|
+
resourceBinaries: [firstResource, secondResource],
|
|
2216
2358
|
},
|
|
2217
2359
|
});
|
|
2218
|
-
|
|
2219
|
-
(
|
|
2220
|
-
_name: string,
|
|
2221
|
-
_resType: string,
|
|
2222
|
-
params: { file: { offset: number; data: { byteLength: number } } }
|
|
2223
|
-
) =>
|
|
2224
|
-
Promise.resolve({
|
|
2225
|
-
type: 'FilesystemFile',
|
|
2226
|
-
message: {
|
|
2227
|
-
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
2228
|
-
},
|
|
2229
|
-
})
|
|
2230
|
-
);
|
|
2360
|
+
method.init();
|
|
2231
2361
|
|
|
2232
|
-
(method as any).
|
|
2233
|
-
|
|
2362
|
+
const resourceTargets = await (method as any).prepareResourceBinaries('universal', {
|
|
2363
|
+
deviceType: 'pro2',
|
|
2364
|
+
firmwareVersion: '1.0.0',
|
|
2365
|
+
capabilities: [],
|
|
2366
|
+
});
|
|
2367
|
+
|
|
2368
|
+
expect(resourceTargets).toEqual([
|
|
2369
|
+
{
|
|
2370
|
+
fileName: 'resource-1.bin',
|
|
2371
|
+
binary: firstResource,
|
|
2372
|
+
targetId: 1,
|
|
2373
|
+
},
|
|
2374
|
+
{
|
|
2375
|
+
fileName: 'resource-2.bin',
|
|
2376
|
+
binary: secondResource,
|
|
2377
|
+
targetId: 1,
|
|
2378
|
+
},
|
|
2379
|
+
]);
|
|
2380
|
+
expect((method as any).hasExplicitProtocolV2Payload([])).toBe(true);
|
|
2381
|
+
});
|
|
2382
|
+
|
|
2383
|
+
test('rejects romloaderBinary before sending a Protocol V2 update request', () => {
|
|
2384
|
+
const method = new FirmwareUpdateV4({
|
|
2385
|
+
id: 1,
|
|
2386
|
+
payload: {
|
|
2387
|
+
method: 'firmwareUpdateV4',
|
|
2388
|
+
platform: 'web',
|
|
2389
|
+
romloaderBinary: new Uint8Array([1]).buffer,
|
|
2390
|
+
},
|
|
2391
|
+
});
|
|
2392
|
+
method.init();
|
|
2393
|
+
|
|
2394
|
+
expect(() => (method as any).collectExplicitTargetBinaries()).toThrow(
|
|
2395
|
+
'FW_MGMT_TARGET_ROMLOADER is not accepted'
|
|
2396
|
+
);
|
|
2397
|
+
});
|
|
2398
|
+
|
|
2399
|
+
test('maps Pro2 remote firmware-v1 components to Protocol V2 targets in installOrder', async () => {
|
|
2400
|
+
const method = new FirmwareUpdateV4({
|
|
2401
|
+
id: 1,
|
|
2402
|
+
payload: {
|
|
2403
|
+
method: 'firmwareUpdateV4',
|
|
2404
|
+
platform: 'web',
|
|
2405
|
+
},
|
|
2406
|
+
});
|
|
2407
|
+
method.init();
|
|
2408
|
+
|
|
2409
|
+
const binaries = new Map(
|
|
2410
|
+
[
|
|
2411
|
+
'bootloader',
|
|
2412
|
+
'applicationP1',
|
|
2413
|
+
'applicationP2',
|
|
2414
|
+
'coprocessor',
|
|
2415
|
+
'se01',
|
|
2416
|
+
'se02',
|
|
2417
|
+
'se03',
|
|
2418
|
+
'se04',
|
|
2419
|
+
].map((key, index) => [
|
|
2420
|
+
`https://example.com/${key}.pp.bin`,
|
|
2421
|
+
new Uint8Array([index + 1]).buffer,
|
|
2422
|
+
])
|
|
2423
|
+
);
|
|
2424
|
+
const getSysResourceBinarySpy = jest
|
|
2425
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2426
|
+
.mockImplementation(url =>
|
|
2427
|
+
Promise.resolve({
|
|
2428
|
+
binary: binaries.get(url) ?? new Uint8Array([0]).buffer,
|
|
2429
|
+
})
|
|
2430
|
+
);
|
|
2431
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2432
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2433
|
+
.mockReturnValue({
|
|
2434
|
+
required: false,
|
|
2435
|
+
version: [1, 0, 0],
|
|
2436
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2437
|
+
bootloaderResource: 'https://example.com/bootloader.pp.bin',
|
|
2438
|
+
bootloaderVersion: [1, 0, 0],
|
|
2439
|
+
displayBootloaderVersion: [1, 0, 0],
|
|
2440
|
+
upgradeType: 'payload-package-set',
|
|
2441
|
+
installOrder: [
|
|
2442
|
+
'bootloader',
|
|
2443
|
+
'applicationP1',
|
|
2444
|
+
'applicationP2',
|
|
2445
|
+
'coprocessor',
|
|
2446
|
+
'se01',
|
|
2447
|
+
'se02',
|
|
2448
|
+
'se03',
|
|
2449
|
+
'se04',
|
|
2450
|
+
],
|
|
2451
|
+
components: {
|
|
2452
|
+
bootloader: {
|
|
2453
|
+
target: 'BOOTLOADER',
|
|
2454
|
+
url: 'https://example.com/bootloader.pp.bin',
|
|
2455
|
+
},
|
|
2456
|
+
applicationP1: {
|
|
2457
|
+
target: 'APPLICATION_P1',
|
|
2458
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2459
|
+
},
|
|
2460
|
+
applicationP2: {
|
|
2461
|
+
target: 'APPLICATION_P2',
|
|
2462
|
+
url: 'https://example.com/applicationP2.pp.bin',
|
|
2463
|
+
},
|
|
2464
|
+
coprocessor: {
|
|
2465
|
+
target: 'COPROCESSOR',
|
|
2466
|
+
url: 'https://example.com/coprocessor.pp.bin',
|
|
2467
|
+
},
|
|
2468
|
+
se01: { target: 'SE01', url: 'https://example.com/se01.pp.bin' },
|
|
2469
|
+
se02: { target: 'SE02', url: 'https://example.com/se02.pp.bin' },
|
|
2470
|
+
se03: { target: 'SE03', url: 'https://example.com/se03.pp.bin' },
|
|
2471
|
+
se04: { target: 'SE04', url: 'https://example.com/se04.pp.bin' },
|
|
2472
|
+
},
|
|
2473
|
+
fingerprint: '',
|
|
2474
|
+
changelog: {
|
|
2475
|
+
'zh-CN': '',
|
|
2476
|
+
'en-US': '',
|
|
2477
|
+
},
|
|
2478
|
+
});
|
|
2479
|
+
|
|
2480
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2481
|
+
deviceType: 'pro2',
|
|
2482
|
+
firmwareVersion: '0.0.0',
|
|
2483
|
+
});
|
|
2484
|
+
|
|
2485
|
+
expect(remoteBinaries.bootloaderBinary).toBe(
|
|
2486
|
+
binaries.get('https://example.com/bootloader.pp.bin')
|
|
2487
|
+
);
|
|
2488
|
+
expect(remoteBinaries.fwBinaryMap).toEqual([
|
|
2489
|
+
{ fileName: 'application_p1.bin', binary: expect.anything(), targetId: 4 },
|
|
2490
|
+
{ fileName: 'application_p2.bin', binary: expect.anything(), targetId: 5 },
|
|
2491
|
+
{ fileName: 'coprocessor.bin', binary: expect.anything(), targetId: 6 },
|
|
2492
|
+
{ fileName: 'se01.bin', binary: expect.anything(), targetId: 7 },
|
|
2493
|
+
{ fileName: 'se02.bin', binary: expect.anything(), targetId: 8 },
|
|
2494
|
+
{ fileName: 'se03.bin', binary: expect.anything(), targetId: 9 },
|
|
2495
|
+
{ fileName: 'se04.bin', binary: expect.anything(), targetId: 10 },
|
|
2496
|
+
]);
|
|
2497
|
+
expect(getSysResourceBinarySpy.mock.calls.map(call => call[0])).toEqual([
|
|
2498
|
+
'https://example.com/bootloader.pp.bin',
|
|
2499
|
+
'https://example.com/applicationP1.pp.bin',
|
|
2500
|
+
'https://example.com/applicationP2.pp.bin',
|
|
2501
|
+
'https://example.com/coprocessor.pp.bin',
|
|
2502
|
+
'https://example.com/se01.pp.bin',
|
|
2503
|
+
'https://example.com/se02.pp.bin',
|
|
2504
|
+
'https://example.com/se03.pp.bin',
|
|
2505
|
+
'https://example.com/se04.pp.bin',
|
|
2506
|
+
]);
|
|
2507
|
+
|
|
2508
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2509
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2510
|
+
});
|
|
2511
|
+
|
|
2512
|
+
test('skips Pro2 resource crate download when resourceManifest matches device OKPP headers', async () => {
|
|
2513
|
+
const method = new FirmwareUpdateV4({
|
|
2514
|
+
id: 1,
|
|
2515
|
+
payload: {
|
|
2516
|
+
method: 'firmwareUpdateV4',
|
|
2517
|
+
platform: 'web',
|
|
2518
|
+
},
|
|
2519
|
+
});
|
|
2520
|
+
method.init();
|
|
2521
|
+
|
|
2522
|
+
const payloadHash = 'ab'.repeat(64);
|
|
2523
|
+
const header = buildOkppHeader({ payloadHash });
|
|
2524
|
+
const typedCall = jest.fn(
|
|
2525
|
+
(name: string, _resType: string, params: { file: { offset: number }; chunk_len: number }) => {
|
|
2526
|
+
if (name === 'FilesystemPathInfoQuery') {
|
|
2527
|
+
return Promise.resolve({
|
|
2528
|
+
type: 'FilesystemPathInfo',
|
|
2529
|
+
message: { exist: true, directory: false, size: header.byteLength },
|
|
2530
|
+
});
|
|
2531
|
+
}
|
|
2532
|
+
if (name === 'FilesystemFileRead') {
|
|
2533
|
+
const { offset } = params.file;
|
|
2534
|
+
const readLen = params.chunk_len;
|
|
2535
|
+
return Promise.resolve({
|
|
2536
|
+
type: 'FilesystemFile',
|
|
2537
|
+
message: { data: header.slice(offset, offset + readLen) },
|
|
2538
|
+
});
|
|
2539
|
+
}
|
|
2540
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2541
|
+
}
|
|
2542
|
+
);
|
|
2543
|
+
(method as any).device = stubDevice({
|
|
2544
|
+
getCommands: () => ({ typedCall }),
|
|
2545
|
+
});
|
|
2546
|
+
|
|
2547
|
+
const getSysResourceBinarySpy = jest
|
|
2548
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2549
|
+
.mockResolvedValue({ binary: new Uint8Array([9]).buffer });
|
|
2550
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2551
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2552
|
+
.mockReturnValue({
|
|
2553
|
+
required: false,
|
|
2554
|
+
version: [1, 0, 0],
|
|
2555
|
+
url: '',
|
|
2556
|
+
upgradeType: 'payload-package-set',
|
|
2557
|
+
installOrder: ['resource'],
|
|
2558
|
+
components: {
|
|
2559
|
+
resource: {
|
|
2560
|
+
target: 'CRATE',
|
|
2561
|
+
url: 'https://example.com/pro2-resource.crate.okpkg',
|
|
2562
|
+
},
|
|
2563
|
+
},
|
|
2564
|
+
resourceManifest: {
|
|
2565
|
+
format: 'okpkg-crate',
|
|
2566
|
+
target: 'CRATE',
|
|
2567
|
+
version: [1, 0, 0],
|
|
2568
|
+
packages: [
|
|
2569
|
+
{
|
|
2570
|
+
path: 'resource/images/images.okpkg',
|
|
2571
|
+
type: 'RESC',
|
|
2572
|
+
payloadHash,
|
|
2573
|
+
},
|
|
2574
|
+
],
|
|
2575
|
+
},
|
|
2576
|
+
fingerprint: '',
|
|
2577
|
+
changelog: {
|
|
2578
|
+
'zh-CN': '',
|
|
2579
|
+
'en-US': '',
|
|
2580
|
+
},
|
|
2581
|
+
});
|
|
2582
|
+
|
|
2583
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2584
|
+
deviceType: 'pro2',
|
|
2585
|
+
firmwareVersion: '1.0.0',
|
|
2586
|
+
capabilities: [],
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2589
|
+
expect(remoteBinaries).toEqual({
|
|
2590
|
+
resourceBinaryMap: [],
|
|
2591
|
+
bootloaderBinary: null,
|
|
2592
|
+
fwBinaryMap: [],
|
|
2593
|
+
});
|
|
2594
|
+
expect(getSysResourceBinarySpy).not.toHaveBeenCalled();
|
|
2595
|
+
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
2596
|
+
path: 'vol0:/resource/images/images.okpkg',
|
|
2597
|
+
});
|
|
2598
|
+
|
|
2599
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2600
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2601
|
+
});
|
|
2602
|
+
|
|
2603
|
+
test('downloads Pro2 resource crate when resourceManifest package is missing', async () => {
|
|
2604
|
+
const method = new FirmwareUpdateV4({
|
|
2605
|
+
id: 1,
|
|
2606
|
+
payload: {
|
|
2607
|
+
method: 'firmwareUpdateV4',
|
|
2608
|
+
platform: 'web',
|
|
2609
|
+
},
|
|
2610
|
+
});
|
|
2611
|
+
method.init();
|
|
2612
|
+
|
|
2613
|
+
const resourceBinary = new Uint8Array([9]).buffer;
|
|
2614
|
+
const typedCall = jest.fn().mockResolvedValue({
|
|
2615
|
+
type: 'FilesystemPathInfo',
|
|
2616
|
+
message: { exist: false, directory: false, size: 0 },
|
|
2617
|
+
});
|
|
2618
|
+
(method as any).device = stubDevice({
|
|
2619
|
+
getCommands: () => ({ typedCall }),
|
|
2620
|
+
});
|
|
2621
|
+
|
|
2622
|
+
const getSysResourceBinarySpy = jest
|
|
2623
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2624
|
+
.mockResolvedValue({ binary: resourceBinary });
|
|
2625
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2626
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2627
|
+
.mockReturnValue({
|
|
2628
|
+
required: false,
|
|
2629
|
+
version: [1, 0, 0],
|
|
2630
|
+
url: '',
|
|
2631
|
+
upgradeType: 'payload-package-set',
|
|
2632
|
+
installOrder: ['resource'],
|
|
2633
|
+
components: {
|
|
2634
|
+
resource: {
|
|
2635
|
+
target: 'CRATE',
|
|
2636
|
+
url: 'https://example.com/pro2-resource.crate.okpkg',
|
|
2637
|
+
resourceManifest: {
|
|
2638
|
+
format: 'okpkg-crate',
|
|
2639
|
+
target: 'CRATE',
|
|
2640
|
+
version: [1, 0, 0],
|
|
2641
|
+
packages: [{ path: 'resource/images/images.okpkg', type: 'RESC' }],
|
|
2642
|
+
},
|
|
2643
|
+
},
|
|
2644
|
+
},
|
|
2645
|
+
fingerprint: '',
|
|
2646
|
+
changelog: {
|
|
2647
|
+
'zh-CN': '',
|
|
2648
|
+
'en-US': '',
|
|
2649
|
+
},
|
|
2650
|
+
});
|
|
2651
|
+
|
|
2652
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2653
|
+
deviceType: 'pro2',
|
|
2654
|
+
firmwareVersion: '1.0.0',
|
|
2655
|
+
capabilities: [],
|
|
2656
|
+
});
|
|
2657
|
+
|
|
2658
|
+
expect(remoteBinaries.resourceBinaryMap).toEqual([
|
|
2659
|
+
{
|
|
2660
|
+
fileName: 'resource-resource.bin',
|
|
2661
|
+
binary: resourceBinary,
|
|
2662
|
+
targetId: 1,
|
|
2663
|
+
},
|
|
2664
|
+
]);
|
|
2665
|
+
expect(getSysResourceBinarySpy).toHaveBeenCalledWith(
|
|
2666
|
+
'https://example.com/pro2-resource.crate.okpkg'
|
|
2667
|
+
);
|
|
2668
|
+
|
|
2669
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2670
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2671
|
+
});
|
|
2672
|
+
|
|
2673
|
+
test('maps Pro2 CRATE remote component to resource binary target', async () => {
|
|
2674
|
+
const method = new FirmwareUpdateV4({
|
|
2675
|
+
id: 1,
|
|
2676
|
+
payload: {
|
|
2677
|
+
method: 'firmwareUpdateV4',
|
|
2678
|
+
platform: 'web',
|
|
2679
|
+
},
|
|
2680
|
+
});
|
|
2681
|
+
method.init();
|
|
2682
|
+
|
|
2683
|
+
const resourceBinary = new Uint8Array([1, 2, 3]).buffer;
|
|
2684
|
+
const getSysResourceBinarySpy = jest
|
|
2685
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2686
|
+
.mockResolvedValue({ binary: resourceBinary });
|
|
2687
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2688
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2689
|
+
.mockReturnValue({
|
|
2690
|
+
required: false,
|
|
2691
|
+
version: [1, 0, 0],
|
|
2692
|
+
url: '',
|
|
2693
|
+
upgradeType: 'payload-package-set',
|
|
2694
|
+
installOrder: ['resource'],
|
|
2695
|
+
components: {
|
|
2696
|
+
resource: {
|
|
2697
|
+
target: 'CRATE',
|
|
2698
|
+
url: 'https://example.com/pro2-resource.crate.okpkg',
|
|
2699
|
+
},
|
|
2700
|
+
},
|
|
2701
|
+
fingerprint: '',
|
|
2702
|
+
changelog: {
|
|
2703
|
+
'zh-CN': '',
|
|
2704
|
+
'en-US': '',
|
|
2705
|
+
},
|
|
2706
|
+
});
|
|
2707
|
+
|
|
2708
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2709
|
+
deviceType: 'pro2',
|
|
2710
|
+
firmwareVersion: '0.0.0',
|
|
2711
|
+
capabilities: [],
|
|
2712
|
+
});
|
|
2713
|
+
|
|
2714
|
+
expect(remoteBinaries).toEqual({
|
|
2715
|
+
resourceBinaryMap: [
|
|
2716
|
+
{
|
|
2717
|
+
fileName: 'resource-resource.bin',
|
|
2718
|
+
binary: resourceBinary,
|
|
2719
|
+
targetId: 1,
|
|
2720
|
+
},
|
|
2721
|
+
],
|
|
2722
|
+
bootloaderBinary: null,
|
|
2723
|
+
fwBinaryMap: [],
|
|
2724
|
+
});
|
|
2725
|
+
|
|
2726
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2727
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2728
|
+
});
|
|
2729
|
+
|
|
2730
|
+
test('downloads only unmatched Pro2 resource crate components by component manifests', async () => {
|
|
2731
|
+
const method = new FirmwareUpdateV4({
|
|
2732
|
+
id: 1,
|
|
2733
|
+
payload: {
|
|
2734
|
+
method: 'firmwareUpdateV4',
|
|
2735
|
+
platform: 'web',
|
|
2736
|
+
},
|
|
2737
|
+
});
|
|
2738
|
+
method.init();
|
|
2739
|
+
|
|
2740
|
+
const imagesPayloadHash = 'ab'.repeat(64);
|
|
2741
|
+
const imagesHeader = buildOkppHeader({ payloadHash: imagesPayloadHash });
|
|
2742
|
+
const fontsBinary = new Uint8Array([7, 8, 9]).buffer;
|
|
2743
|
+
const typedCall = jest.fn(
|
|
2744
|
+
(
|
|
2745
|
+
name: string,
|
|
2746
|
+
_resType: string,
|
|
2747
|
+
params: { path?: string; file?: { offset: number }; chunk_len?: number }
|
|
2748
|
+
) => {
|
|
2749
|
+
if (name === 'FilesystemPathInfoQuery') {
|
|
2750
|
+
const exist = params.path === 'vol0:/resource/images/images.okpkg';
|
|
2751
|
+
return Promise.resolve({
|
|
2752
|
+
type: 'FilesystemPathInfo',
|
|
2753
|
+
message: { exist, directory: false, size: exist ? imagesHeader.byteLength : 0 },
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
if (name === 'FilesystemFileRead') {
|
|
2757
|
+
const offset = params.file?.offset ?? 0;
|
|
2758
|
+
const readLen = params.chunk_len ?? 0;
|
|
2759
|
+
return Promise.resolve({
|
|
2760
|
+
type: 'FilesystemFile',
|
|
2761
|
+
message: { data: imagesHeader.slice(offset, offset + readLen) },
|
|
2762
|
+
});
|
|
2763
|
+
}
|
|
2764
|
+
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2765
|
+
}
|
|
2766
|
+
);
|
|
2767
|
+
(method as any).device = stubDevice({
|
|
2768
|
+
getCommands: () => ({ typedCall }),
|
|
2769
|
+
});
|
|
2770
|
+
|
|
2771
|
+
const getSysResourceBinarySpy = jest
|
|
2772
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2773
|
+
.mockResolvedValue({ binary: fontsBinary });
|
|
2774
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2775
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2776
|
+
.mockReturnValue({
|
|
2777
|
+
required: false,
|
|
2778
|
+
version: [1, 0, 0],
|
|
2779
|
+
url: '',
|
|
2780
|
+
upgradeType: 'payload-package-set',
|
|
2781
|
+
installOrder: ['resourceImages', 'resourceFonts'],
|
|
2782
|
+
components: {
|
|
2783
|
+
resourceImages: {
|
|
2784
|
+
target: 'CRATE',
|
|
2785
|
+
url: 'https://example.com/pro2-resource-images.crate.okpkg',
|
|
2786
|
+
resourceManifest: {
|
|
2787
|
+
format: 'okpkg-crate',
|
|
2788
|
+
target: 'CRATE',
|
|
2789
|
+
version: [1, 0, 0],
|
|
2790
|
+
packages: [
|
|
2791
|
+
{
|
|
2792
|
+
path: 'resource/images/images.okpkg',
|
|
2793
|
+
type: 'RESC',
|
|
2794
|
+
payloadHash: imagesPayloadHash,
|
|
2795
|
+
},
|
|
2796
|
+
],
|
|
2797
|
+
},
|
|
2798
|
+
},
|
|
2799
|
+
resourceFonts: {
|
|
2800
|
+
target: 'CRATE',
|
|
2801
|
+
url: 'https://example.com/pro2-resource-fonts.crate.okpkg',
|
|
2802
|
+
resourceManifest: {
|
|
2803
|
+
format: 'okpkg-crate',
|
|
2804
|
+
target: 'CRATE',
|
|
2805
|
+
version: [1, 0, 0],
|
|
2806
|
+
packages: [{ path: 'resource/font/noto.okpkg', type: 'RESC' }],
|
|
2807
|
+
},
|
|
2808
|
+
},
|
|
2809
|
+
},
|
|
2810
|
+
fingerprint: '',
|
|
2811
|
+
changelog: {
|
|
2812
|
+
'zh-CN': '',
|
|
2813
|
+
'en-US': '',
|
|
2814
|
+
},
|
|
2815
|
+
});
|
|
2816
|
+
|
|
2817
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2818
|
+
deviceType: 'pro2',
|
|
2819
|
+
firmwareVersion: '1.0.0',
|
|
2820
|
+
capabilities: [],
|
|
2821
|
+
});
|
|
2822
|
+
|
|
2823
|
+
expect(remoteBinaries.resourceBinaryMap).toEqual([
|
|
2824
|
+
{
|
|
2825
|
+
fileName: 'resource-resourceFonts.bin',
|
|
2826
|
+
binary: fontsBinary,
|
|
2827
|
+
targetId: 1,
|
|
2828
|
+
},
|
|
2829
|
+
]);
|
|
2830
|
+
expect(getSysResourceBinarySpy).toHaveBeenCalledTimes(1);
|
|
2831
|
+
expect(getSysResourceBinarySpy).toHaveBeenCalledWith(
|
|
2832
|
+
'https://example.com/pro2-resource-fonts.crate.okpkg'
|
|
2833
|
+
);
|
|
2834
|
+
|
|
2835
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2836
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2837
|
+
});
|
|
2838
|
+
|
|
2839
|
+
test('installs multiple Pro2 resource crates as separate update requests', async () => {
|
|
2840
|
+
const method = new FirmwareUpdateV4({
|
|
2841
|
+
id: 1,
|
|
2842
|
+
payload: {
|
|
2843
|
+
method: 'firmwareUpdateV4',
|
|
2844
|
+
},
|
|
2845
|
+
});
|
|
2846
|
+
|
|
2847
|
+
method.postTipMessage = jest.fn();
|
|
2848
|
+
method.postProgressMessage = jest.fn();
|
|
2849
|
+
(method as any).protocolV2CommonUpdateProcess = jest
|
|
2850
|
+
.fn()
|
|
2851
|
+
.mockImplementation(params =>
|
|
2852
|
+
Promise.resolve(Number(params.processedSize ?? 0) + Number(params.payload.byteLength))
|
|
2853
|
+
);
|
|
2854
|
+
(method as any).protocolV2StartFirmwareUpdate = jest.fn().mockResolvedValue(undefined);
|
|
2855
|
+
(method as any).waitForProtocolV2FirmwareUpdateComplete = jest
|
|
2856
|
+
.fn()
|
|
2857
|
+
.mockResolvedValue(undefined);
|
|
2858
|
+
|
|
2859
|
+
await (method as any).executeProtocolV2Update({
|
|
2860
|
+
resourceBinaryMap: [
|
|
2861
|
+
{
|
|
2862
|
+
fileName: 'resource-images.bin',
|
|
2863
|
+
binary: new Uint8Array([1]).buffer,
|
|
2864
|
+
targetId: 1,
|
|
2865
|
+
},
|
|
2866
|
+
{
|
|
2867
|
+
fileName: 'resource-fonts.bin',
|
|
2868
|
+
binary: new Uint8Array([2]).buffer,
|
|
2869
|
+
targetId: 1,
|
|
2870
|
+
},
|
|
2871
|
+
],
|
|
2872
|
+
bootloaderBinary: null,
|
|
2873
|
+
fwBinaryMap: [
|
|
2874
|
+
{
|
|
2875
|
+
fileName: 'application_p1.bin',
|
|
2876
|
+
binary: new Uint8Array([3]).buffer,
|
|
2877
|
+
targetId: 4,
|
|
2878
|
+
},
|
|
2879
|
+
],
|
|
2880
|
+
});
|
|
2881
|
+
|
|
2882
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(1, {
|
|
2883
|
+
targets: [{ target_id: 1, path: 'vol1:resource-images.bin' }],
|
|
2884
|
+
});
|
|
2885
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(2, {
|
|
2886
|
+
targets: [{ target_id: 1, path: 'vol1:resource-fonts.bin' }],
|
|
2887
|
+
});
|
|
2888
|
+
expect((method as any).protocolV2StartFirmwareUpdate).toHaveBeenNthCalledWith(3, {
|
|
2889
|
+
targets: [{ target_id: 4, path: 'vol1:application_p1.bin' }],
|
|
2890
|
+
});
|
|
2891
|
+
});
|
|
2892
|
+
|
|
2893
|
+
test('skips Pro2 firmware components when configured versions are already installed', async () => {
|
|
2894
|
+
const method = new FirmwareUpdateV4({
|
|
2895
|
+
id: 1,
|
|
2896
|
+
payload: {
|
|
2897
|
+
method: 'firmwareUpdateV4',
|
|
2898
|
+
platform: 'web',
|
|
2899
|
+
},
|
|
2900
|
+
});
|
|
2901
|
+
method.init();
|
|
2902
|
+
|
|
2903
|
+
const getSysResourceBinarySpy = jest
|
|
2904
|
+
.spyOn(firmwareBinaryApi, 'getSysResourceBinary')
|
|
2905
|
+
.mockResolvedValue({ binary: new Uint8Array([1]).buffer });
|
|
2906
|
+
const getFirmwareLatestReleaseSpy = jest
|
|
2907
|
+
.spyOn(DataManager, 'getFirmwareLatestRelease')
|
|
2908
|
+
.mockReturnValue({
|
|
2909
|
+
required: false,
|
|
2910
|
+
version: [1, 0, 0],
|
|
2911
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2912
|
+
bootloaderVersion: [1, 0, 0],
|
|
2913
|
+
upgradeType: 'payload-package-set',
|
|
2914
|
+
installOrder: ['bootloader', 'applicationP1', 'applicationP2'],
|
|
2915
|
+
components: {
|
|
2916
|
+
bootloader: {
|
|
2917
|
+
target: 'BOOTLOADER',
|
|
2918
|
+
url: 'https://example.com/bootloader.pp.bin',
|
|
2919
|
+
},
|
|
2920
|
+
applicationP1: {
|
|
2921
|
+
target: 'APPLICATION_P1',
|
|
2922
|
+
url: 'https://example.com/applicationP1.pp.bin',
|
|
2923
|
+
},
|
|
2924
|
+
applicationP2: {
|
|
2925
|
+
target: 'APPLICATION_P2',
|
|
2926
|
+
url: 'https://example.com/applicationP2.pp.bin',
|
|
2927
|
+
},
|
|
2928
|
+
},
|
|
2929
|
+
fingerprint: '',
|
|
2930
|
+
changelog: {
|
|
2931
|
+
'zh-CN': '',
|
|
2932
|
+
'en-US': '',
|
|
2933
|
+
},
|
|
2934
|
+
});
|
|
2935
|
+
|
|
2936
|
+
const remoteBinaries = await (method as any).prepareRemoteProtocolV2Binaries('universal', {
|
|
2937
|
+
deviceType: 'pro2',
|
|
2938
|
+
firmwareVersion: '1.0.0',
|
|
2939
|
+
bootloaderVersion: '1.0.0',
|
|
2940
|
+
capabilities: [],
|
|
2941
|
+
});
|
|
2942
|
+
|
|
2943
|
+
expect(remoteBinaries).toEqual({
|
|
2944
|
+
resourceBinaryMap: [],
|
|
2945
|
+
bootloaderBinary: null,
|
|
2946
|
+
fwBinaryMap: [],
|
|
2947
|
+
});
|
|
2948
|
+
expect(getSysResourceBinarySpy).not.toHaveBeenCalled();
|
|
2949
|
+
|
|
2950
|
+
getSysResourceBinarySpy.mockRestore();
|
|
2951
|
+
getFirmwareLatestReleaseSpy.mockRestore();
|
|
2952
|
+
});
|
|
2953
|
+
|
|
2954
|
+
test('uses Pro2 remote components when firmwareUpdateV4 has no explicit binaries', async () => {
|
|
2955
|
+
const method = new FirmwareUpdateV4({
|
|
2956
|
+
id: 1,
|
|
2957
|
+
payload: {
|
|
2958
|
+
method: 'firmwareUpdateV4',
|
|
2959
|
+
platform: 'web',
|
|
2960
|
+
},
|
|
2961
|
+
});
|
|
2962
|
+
method.init();
|
|
2963
|
+
|
|
2964
|
+
const remoteBinaries = {
|
|
2965
|
+
resourceBinaryMap: [],
|
|
2966
|
+
bootloaderBinary: new Uint8Array([1]).buffer,
|
|
2967
|
+
fwBinaryMap: [
|
|
2968
|
+
{
|
|
2969
|
+
fileName: 'application_p1.bin',
|
|
2970
|
+
binary: new Uint8Array([2]).buffer,
|
|
2971
|
+
targetId: 4,
|
|
2972
|
+
},
|
|
2973
|
+
],
|
|
2974
|
+
};
|
|
2975
|
+
|
|
2976
|
+
(method as any).device = stubDevice({
|
|
2977
|
+
originalDescriptor: { protocolType: 'V2' },
|
|
2978
|
+
features: { deviceType: 'pro2', firmwareVersion: '0.0.0', capabilities: [] },
|
|
2979
|
+
});
|
|
2980
|
+
(method as any).prepareResourceBinaries = jest.fn().mockResolvedValue([]);
|
|
2981
|
+
(method as any).prepareRemoteProtocolV2Binaries = jest.fn().mockResolvedValue(remoteBinaries);
|
|
2982
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(true);
|
|
2983
|
+
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
2984
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
2985
|
+
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
2986
|
+
bootloaderVersion: '1.0.0',
|
|
2987
|
+
bleVersion: '0.0.0',
|
|
2988
|
+
firmwareVersion: '1.0.0',
|
|
2989
|
+
});
|
|
2990
|
+
method.postTipMessage = jest.fn();
|
|
2991
|
+
|
|
2992
|
+
await method.run();
|
|
2993
|
+
|
|
2994
|
+
expect((method as any).prepareRemoteProtocolV2Binaries).toHaveBeenCalledTimes(1);
|
|
2995
|
+
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
2996
|
+
resourceBinaryMap: remoteBinaries.resourceBinaryMap,
|
|
2997
|
+
bootloaderBinary: remoteBinaries.bootloaderBinary,
|
|
2998
|
+
fwBinaryMap: remoteBinaries.fwBinaryMap,
|
|
2999
|
+
});
|
|
3000
|
+
});
|
|
3001
|
+
|
|
3002
|
+
test('keeps explicit Protocol V2 binaries isolated from remote component auto-fill', async () => {
|
|
3003
|
+
const method = new FirmwareUpdateV4({
|
|
3004
|
+
id: 1,
|
|
3005
|
+
payload: {
|
|
3006
|
+
method: 'firmwareUpdateV4',
|
|
3007
|
+
platform: 'web',
|
|
3008
|
+
coprocessorBinary: new Uint8Array([1]).buffer,
|
|
3009
|
+
},
|
|
3010
|
+
});
|
|
3011
|
+
method.init();
|
|
3012
|
+
|
|
3013
|
+
(method as any).device = stubDevice({
|
|
3014
|
+
originalDescriptor: { protocolType: 'V2' },
|
|
3015
|
+
features: { deviceType: 'pro2', firmwareVersion: '0.0.0', capabilities: [] },
|
|
3016
|
+
});
|
|
3017
|
+
(method as any).prepareResourceBinaries = jest.fn().mockResolvedValue([]);
|
|
3018
|
+
(method as any).prepareRemoteProtocolV2Binaries = jest.fn();
|
|
3019
|
+
(method as any).enterProtocolV2BootloaderMode = jest.fn().mockResolvedValue(true);
|
|
3020
|
+
(method as any).executeProtocolV2Update = jest.fn().mockResolvedValue(undefined);
|
|
3021
|
+
(method as any).exitProtocolV2BootloaderToNormal = jest.fn().mockResolvedValue(undefined);
|
|
3022
|
+
(method as any).waitForProtocolV2FinalFeatures = jest.fn().mockResolvedValue({
|
|
3023
|
+
bootloaderVersion: '1.0.0',
|
|
3024
|
+
bleVersion: '0.0.0',
|
|
3025
|
+
firmwareVersion: '1.0.0',
|
|
3026
|
+
});
|
|
3027
|
+
method.postTipMessage = jest.fn();
|
|
3028
|
+
|
|
3029
|
+
await method.run();
|
|
3030
|
+
|
|
3031
|
+
expect((method as any).prepareRemoteProtocolV2Binaries).not.toHaveBeenCalled();
|
|
3032
|
+
expect((method as any).executeProtocolV2Update).toHaveBeenCalledWith({
|
|
3033
|
+
resourceBinaryMap: [],
|
|
3034
|
+
bootloaderBinary: null,
|
|
3035
|
+
fwBinaryMap: [{ fileName: 'coprocessor.bin', binary: expect.anything(), targetId: 6 }],
|
|
3036
|
+
});
|
|
3037
|
+
});
|
|
3038
|
+
|
|
3039
|
+
test('uses absolute processed_byte offsets and disables append for firmware file writes', async () => {
|
|
3040
|
+
const method = new FirmwareUpdateV4({
|
|
3041
|
+
id: 1,
|
|
3042
|
+
payload: {
|
|
3043
|
+
method: 'firmwareUpdateV4',
|
|
3044
|
+
},
|
|
3045
|
+
});
|
|
3046
|
+
const typedCall = jest.fn(
|
|
3047
|
+
(
|
|
3048
|
+
_name: string,
|
|
3049
|
+
_resType: string,
|
|
3050
|
+
params: { file: { offset: number; data: { byteLength: number } } }
|
|
3051
|
+
) =>
|
|
3052
|
+
Promise.resolve({
|
|
3053
|
+
type: 'FilesystemFile',
|
|
3054
|
+
message: {
|
|
3055
|
+
processed_byte: params.file.offset + params.file.data.byteLength,
|
|
3056
|
+
},
|
|
3057
|
+
})
|
|
3058
|
+
);
|
|
3059
|
+
|
|
3060
|
+
(method as any).device = stubDevice({
|
|
3061
|
+
getCommands: () => ({ typedCall }),
|
|
2234
3062
|
});
|
|
2235
3063
|
method.postProgressMessage = jest.fn();
|
|
2236
3064
|
|
|
@@ -2242,10 +3070,11 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2242
3070
|
});
|
|
2243
3071
|
|
|
2244
3072
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
2245
|
-
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0,
|
|
2246
|
-
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([
|
|
3073
|
+
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 4000]);
|
|
3074
|
+
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([4000, 97]);
|
|
2247
3075
|
expect(writePayloads.map(payload => payload.overwrite)).toEqual([true, false]);
|
|
2248
3076
|
expect(writePayloads.every(payload => payload.append === false)).toBe(true);
|
|
3077
|
+
expect(writePayloads.map(payload => payload.ui_percentage)).toEqual([0, 100]);
|
|
2249
3078
|
});
|
|
2250
3079
|
|
|
2251
3080
|
test('continues to DeviceFirmwareUpdate when FilesystemFileWrite returns processed chunk length', async () => {
|
|
@@ -2264,9 +3093,17 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2264
3093
|
},
|
|
2265
3094
|
});
|
|
2266
3095
|
}
|
|
2267
|
-
if (name === '
|
|
3096
|
+
if (name === 'DeviceFirmwareUpdateRequest') {
|
|
2268
3097
|
return Promise.resolve({ type: 'Success', message: { message: 'ok' } });
|
|
2269
3098
|
}
|
|
3099
|
+
if (name === 'DeviceFirmwareUpdateStatusGet') {
|
|
3100
|
+
return Promise.resolve({
|
|
3101
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3102
|
+
message: {
|
|
3103
|
+
records: [{ target_id: 4, status: 2 }],
|
|
3104
|
+
},
|
|
3105
|
+
});
|
|
3106
|
+
}
|
|
2270
3107
|
return Promise.reject(new Error(`unexpected call ${name}`));
|
|
2271
3108
|
});
|
|
2272
3109
|
|
|
@@ -2277,21 +3114,22 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2277
3114
|
method.postTipMessage = jest.fn();
|
|
2278
3115
|
|
|
2279
3116
|
await (method as any).executeProtocolV2Update({
|
|
2280
|
-
|
|
3117
|
+
resourceBinaryMap: [],
|
|
2281
3118
|
bootloaderBinary: null,
|
|
2282
3119
|
fwBinaryMap: [
|
|
2283
3120
|
{
|
|
2284
3121
|
fileName: 'firmware.bin',
|
|
2285
3122
|
binary: new Uint8Array(4097).buffer,
|
|
3123
|
+
targetId: 4,
|
|
2286
3124
|
},
|
|
2287
3125
|
],
|
|
2288
3126
|
});
|
|
2289
3127
|
|
|
2290
3128
|
expect(typedCall).toHaveBeenCalledWith(
|
|
2291
|
-
'
|
|
2292
|
-
['Success', '
|
|
3129
|
+
'DeviceFirmwareUpdateRequest',
|
|
3130
|
+
['Success', 'DeviceFirmwareUpdateStatus'],
|
|
2293
3131
|
{
|
|
2294
|
-
targets: [{ target_id:
|
|
3132
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2295
3133
|
},
|
|
2296
3134
|
expect.any(Object)
|
|
2297
3135
|
);
|
|
@@ -2337,6 +3175,7 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2337
3175
|
const writePayloads = typedCall.mock.calls.map(call => call[2]);
|
|
2338
3176
|
expect(writePayloads.map(payload => payload.file.offset)).toEqual([0, 1800]);
|
|
2339
3177
|
expect(writePayloads.map(payload => payload.file.data.byteLength)).toEqual([1800, 1]);
|
|
3178
|
+
expect(writePayloads.map(payload => payload.ui_percentage)).toEqual([0, 100]);
|
|
2340
3179
|
});
|
|
2341
3180
|
|
|
2342
3181
|
test('consumes Protocol V2 install progress before final update success', async () => {
|
|
@@ -2355,18 +3194,18 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2355
3194
|
method.postTipMessage = jest.fn();
|
|
2356
3195
|
|
|
2357
3196
|
await (method as any).protocolV2StartFirmwareUpdate({
|
|
2358
|
-
targets: [{ target_id:
|
|
3197
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2359
3198
|
});
|
|
2360
3199
|
|
|
2361
3200
|
const callOptions = typedCall.mock.calls[0][3];
|
|
2362
|
-
expect(typedCall.mock.calls[0][1]).toEqual(['Success', '
|
|
2363
|
-
expect(callOptions.intermediateTypes).toEqual(['
|
|
3201
|
+
expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
|
|
3202
|
+
expect(callOptions.intermediateTypes).toEqual(['DeviceFirmwareUpdateStatus']);
|
|
2364
3203
|
callOptions.onIntermediateResponse({
|
|
2365
|
-
type: '
|
|
2366
|
-
message: { target_id:
|
|
3204
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3205
|
+
message: { records: [{ target_id: 4, status: 1 }] },
|
|
2367
3206
|
});
|
|
2368
3207
|
|
|
2369
|
-
expect(method.postProgressMessage).toHaveBeenCalledWith(
|
|
3208
|
+
expect(method.postProgressMessage).toHaveBeenCalledWith(99, 'installingFirmware');
|
|
2370
3209
|
});
|
|
2371
3210
|
|
|
2372
3211
|
test('accepts Protocol V2 firmware update status as start response', async () => {
|
|
@@ -2377,8 +3216,8 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2377
3216
|
},
|
|
2378
3217
|
});
|
|
2379
3218
|
const typedCall = jest.fn().mockResolvedValue({
|
|
2380
|
-
type: '
|
|
2381
|
-
message: {
|
|
3219
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3220
|
+
message: { records: [{ target_id: 4, status: 1 }] },
|
|
2382
3221
|
});
|
|
2383
3222
|
|
|
2384
3223
|
(method as any).device = stubDevice({
|
|
@@ -2388,29 +3227,29 @@ describe('Protocol V2 firmware update targets', () => {
|
|
|
2388
3227
|
method.postTipMessage = jest.fn();
|
|
2389
3228
|
|
|
2390
3229
|
await (method as any).protocolV2StartFirmwareUpdate({
|
|
2391
|
-
targets: [{ target_id:
|
|
3230
|
+
targets: [{ target_id: 4, path: 'vol1:firmware.bin' }],
|
|
2392
3231
|
});
|
|
2393
3232
|
|
|
2394
|
-
expect(typedCall.mock.calls[0][1]).toEqual(['Success', '
|
|
3233
|
+
expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
|
|
2395
3234
|
expect(method.postTipMessage).toHaveBeenCalledWith('FirmwareUpdating');
|
|
2396
3235
|
});
|
|
2397
3236
|
});
|
|
2398
3237
|
|
|
2399
3238
|
describe('Protocol V2 firmware update method', () => {
|
|
2400
|
-
test('returns
|
|
3239
|
+
test('returns DeviceFirmwareUpdateStatus from low-level update trigger', async () => {
|
|
2401
3240
|
const method = new DeviceFirmwareUpdate({
|
|
2402
3241
|
id: 1,
|
|
2403
3242
|
payload: {
|
|
2404
3243
|
method: 'deviceFirmwareUpdate',
|
|
2405
|
-
targetId:
|
|
3244
|
+
targetId: 4,
|
|
2406
3245
|
path: 'vol0:firmware.bin',
|
|
2407
3246
|
},
|
|
2408
3247
|
});
|
|
2409
3248
|
method.init();
|
|
2410
3249
|
|
|
2411
3250
|
const typedCall = jest.fn().mockResolvedValue({
|
|
2412
|
-
type: '
|
|
2413
|
-
message: {
|
|
3251
|
+
type: 'DeviceFirmwareUpdateStatus',
|
|
3252
|
+
message: { records: [{ target_id: 4, status: 1 }] },
|
|
2414
3253
|
});
|
|
2415
3254
|
|
|
2416
3255
|
(method as any).device = stubDevice({
|
|
@@ -2418,11 +3257,11 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2418
3257
|
});
|
|
2419
3258
|
|
|
2420
3259
|
await expect(method.run()).resolves.toEqual({
|
|
2421
|
-
|
|
3260
|
+
records: [{ target_id: 4, status: 1 }],
|
|
2422
3261
|
});
|
|
2423
|
-
expect(typedCall.mock.calls[0][1]).toEqual(['Success', '
|
|
3262
|
+
expect(typedCall.mock.calls[0][1]).toEqual(['Success', 'DeviceFirmwareUpdateStatus']);
|
|
2424
3263
|
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
2425
|
-
targets: [{ target_id:
|
|
3264
|
+
targets: [{ target_id: 4, path: 'vol0:firmware.bin' }],
|
|
2426
3265
|
});
|
|
2427
3266
|
});
|
|
2428
3267
|
|
|
@@ -2431,7 +3270,7 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2431
3270
|
id: 1,
|
|
2432
3271
|
payload: {
|
|
2433
3272
|
method: 'deviceFirmwareUpdate',
|
|
2434
|
-
targetId:
|
|
3273
|
+
targetId: 4,
|
|
2435
3274
|
path: 'vol0:firmware.bin',
|
|
2436
3275
|
},
|
|
2437
3276
|
});
|
|
@@ -2485,7 +3324,7 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2485
3324
|
targets: [
|
|
2486
3325
|
{
|
|
2487
3326
|
target_id: undefined,
|
|
2488
|
-
targetId:
|
|
3327
|
+
targetId: 4,
|
|
2489
3328
|
path: 'vol0:firmware.bin',
|
|
2490
3329
|
},
|
|
2491
3330
|
],
|
|
@@ -2498,18 +3337,39 @@ describe('Protocol V2 firmware update method', () => {
|
|
|
2498
3337
|
|
|
2499
3338
|
await method.run();
|
|
2500
3339
|
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
2501
|
-
targets: [{ target_id:
|
|
3340
|
+
targets: [{ target_id: 4, path: 'vol0:firmware.bin' }],
|
|
3341
|
+
});
|
|
3342
|
+
});
|
|
3343
|
+
|
|
3344
|
+
test('accepts firmware target enum names from low-level update params', async () => {
|
|
3345
|
+
const typedCall = jest.fn().mockResolvedValue({ message: {} });
|
|
3346
|
+
const method = new DeviceFirmwareUpdate({
|
|
3347
|
+
id: 1,
|
|
3348
|
+
payload: {
|
|
3349
|
+
method: 'deviceFirmwareUpdate',
|
|
3350
|
+
targetId: 'FW_MGMT_TARGET_CRATE',
|
|
3351
|
+
path: 'vol0:resource.bin',
|
|
3352
|
+
},
|
|
3353
|
+
});
|
|
3354
|
+
method.init();
|
|
3355
|
+
(method as any).device = stubDevice({
|
|
3356
|
+
commands: { typedCall },
|
|
3357
|
+
});
|
|
3358
|
+
|
|
3359
|
+
await method.run();
|
|
3360
|
+
expect(typedCall.mock.calls[0][2]).toEqual({
|
|
3361
|
+
targets: [{ target_id: 1, path: 'vol0:resource.bin' }],
|
|
2502
3362
|
});
|
|
2503
3363
|
});
|
|
2504
3364
|
});
|
|
2505
3365
|
|
|
2506
3366
|
describe('Protocol V2 reboot methods', () => {
|
|
2507
|
-
test('sends
|
|
3367
|
+
test('sends DeviceReboot from deviceReboot', async () => {
|
|
2508
3368
|
const typedCall = jest.fn().mockResolvedValue({ message: { message: 'ok' } });
|
|
2509
|
-
const method = new
|
|
3369
|
+
const method = new DeviceReboot({
|
|
2510
3370
|
id: 1,
|
|
2511
3371
|
payload: {
|
|
2512
|
-
method: '
|
|
3372
|
+
method: 'deviceReboot',
|
|
2513
3373
|
rebootType: 2,
|
|
2514
3374
|
},
|
|
2515
3375
|
});
|
|
@@ -2518,37 +3378,86 @@ describe('Protocol V2 reboot methods', () => {
|
|
|
2518
3378
|
|
|
2519
3379
|
await method.run();
|
|
2520
3380
|
|
|
2521
|
-
expect(typedCall).toHaveBeenCalledWith('
|
|
3381
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceReboot', 'Success', {
|
|
2522
3382
|
reboot_type: 2,
|
|
2523
3383
|
});
|
|
2524
3384
|
});
|
|
3385
|
+
});
|
|
2525
3386
|
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
const
|
|
3387
|
+
describe('Protocol V2 current low-level methods', () => {
|
|
3388
|
+
test('sends ProtocolInfoRequest from protocolInfoRequest', async () => {
|
|
3389
|
+
const typedCall = jest.fn().mockResolvedValue({ message: { version: 1 } });
|
|
3390
|
+
const method = new ProtocolInfoRequest({
|
|
3391
|
+
id: 1,
|
|
3392
|
+
payload: { method: 'protocolInfoRequest' },
|
|
3393
|
+
});
|
|
3394
|
+
method.init();
|
|
3395
|
+
(method as any).device = stubDevice({ commands: { typedCall } });
|
|
3396
|
+
|
|
3397
|
+
await method.run();
|
|
3398
|
+
|
|
3399
|
+
expect(typedCall).toHaveBeenCalledWith('ProtocolInfoRequest', 'ProtocolInfo', {});
|
|
3400
|
+
});
|
|
3401
|
+
|
|
3402
|
+
test('sends DeviceFactoryInfoSet and DeviceFactoryInfoGet', async () => {
|
|
3403
|
+
const typedCall = jest.fn().mockResolvedValue({ message: {} });
|
|
3404
|
+
const setMethod = new DeviceFactoryInfoSet({
|
|
2529
3405
|
id: 1,
|
|
2530
3406
|
payload: {
|
|
2531
|
-
method: '
|
|
2532
|
-
|
|
3407
|
+
method: 'deviceFactoryInfoSet',
|
|
3408
|
+
serial_number: 'PR2SERIAL',
|
|
3409
|
+
burn_in_completed: true,
|
|
3410
|
+
},
|
|
3411
|
+
});
|
|
3412
|
+
setMethod.init();
|
|
3413
|
+
(setMethod as any).device = stubDevice({ commands: { typedCall } });
|
|
3414
|
+
|
|
3415
|
+
await setMethod.run();
|
|
3416
|
+
|
|
3417
|
+
expect(typedCall).toHaveBeenCalledWith('DeviceFactoryInfoSet', 'Success', {
|
|
3418
|
+
info: {
|
|
3419
|
+
version: undefined,
|
|
3420
|
+
serial_number: 'PR2SERIAL',
|
|
3421
|
+
burn_in_completed: true,
|
|
3422
|
+
factory_test_completed: undefined,
|
|
3423
|
+
manufacture_time: undefined,
|
|
2533
3424
|
},
|
|
2534
3425
|
});
|
|
3426
|
+
|
|
3427
|
+
typedCall.mockResolvedValueOnce({ message: { serial_number: 'PR2SERIAL' } });
|
|
3428
|
+
const getMethod = new DeviceFactoryInfoGet({
|
|
3429
|
+
id: 2,
|
|
3430
|
+
payload: { method: 'deviceFactoryInfoGet' },
|
|
3431
|
+
});
|
|
3432
|
+
getMethod.init();
|
|
3433
|
+
(getMethod as any).device = stubDevice({ commands: { typedCall } });
|
|
3434
|
+
|
|
3435
|
+
await getMethod.run();
|
|
3436
|
+
|
|
3437
|
+
expect(typedCall).toHaveBeenLastCalledWith('DeviceFactoryInfoGet', 'DeviceFactoryInfo', {});
|
|
3438
|
+
});
|
|
3439
|
+
|
|
3440
|
+
test('sends FilesystemPermissionFix from filesystemPermissionFix', async () => {
|
|
3441
|
+
const typedCall = jest.fn().mockResolvedValue({ message: {} });
|
|
3442
|
+
const method = new FilesystemPermissionFix({
|
|
3443
|
+
id: 1,
|
|
3444
|
+
payload: { method: 'filesystemPermissionFix' },
|
|
3445
|
+
});
|
|
2535
3446
|
method.init();
|
|
2536
3447
|
(method as any).device = stubDevice({ commands: { typedCall } });
|
|
2537
3448
|
|
|
2538
3449
|
await method.run();
|
|
2539
3450
|
|
|
2540
|
-
expect(typedCall).toHaveBeenCalledWith('
|
|
2541
|
-
reboot_type: 2,
|
|
2542
|
-
});
|
|
3451
|
+
expect(typedCall).toHaveBeenCalledWith('FilesystemPermissionFix', 'Success', {});
|
|
2543
3452
|
});
|
|
2544
3453
|
});
|
|
2545
3454
|
|
|
2546
3455
|
describe('Protocol V2 raw device info method', () => {
|
|
2547
3456
|
const buildMethod = (payload: Record<string, unknown> = {}) => {
|
|
2548
|
-
const method = new
|
|
3457
|
+
const method = new DeviceInfoGet({
|
|
2549
3458
|
id: 1,
|
|
2550
3459
|
payload: {
|
|
2551
|
-
method: '
|
|
3460
|
+
method: 'deviceInfoGet',
|
|
2552
3461
|
...payload,
|
|
2553
3462
|
},
|
|
2554
3463
|
});
|
|
@@ -2558,7 +3467,7 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2558
3467
|
|
|
2559
3468
|
test('passes requested targets/types through and returns the raw DeviceInfo message', async () => {
|
|
2560
3469
|
const method = buildMethod({
|
|
2561
|
-
targets: { hw: true, se1: true
|
|
3470
|
+
targets: { hw: true, se1: true },
|
|
2562
3471
|
types: { version: true, hash: true },
|
|
2563
3472
|
});
|
|
2564
3473
|
const typedCall = jest.fn().mockResolvedValue({
|
|
@@ -2574,9 +3483,8 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2574
3483
|
protocol_version: 1,
|
|
2575
3484
|
hw: { serial_no: 'PR2SERIAL' },
|
|
2576
3485
|
});
|
|
2577
|
-
// 未知 key(junk)被过滤,已选 key 原样透传,不构建 DeviceProfile
|
|
2578
3486
|
expect(typedCall).toHaveBeenCalledWith(
|
|
2579
|
-
'
|
|
3487
|
+
'DeviceInfoGet',
|
|
2580
3488
|
'DeviceInfo',
|
|
2581
3489
|
{
|
|
2582
3490
|
targets: { hw: true, se1: true },
|
|
@@ -2586,6 +3494,20 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2586
3494
|
);
|
|
2587
3495
|
});
|
|
2588
3496
|
|
|
3497
|
+
test('rejects removed target/type names before transport call', () => {
|
|
3498
|
+
expect(() =>
|
|
3499
|
+
buildMethod({
|
|
3500
|
+
targets: { hw: true, bt: true },
|
|
3501
|
+
})
|
|
3502
|
+
).toThrow('targets');
|
|
3503
|
+
|
|
3504
|
+
expect(() =>
|
|
3505
|
+
buildMethod({
|
|
3506
|
+
types: { version: true, digest: true },
|
|
3507
|
+
})
|
|
3508
|
+
).toThrow('types');
|
|
3509
|
+
});
|
|
3510
|
+
|
|
2589
3511
|
test('defaults to the basic targets/types when none are given', async () => {
|
|
2590
3512
|
const method = buildMethod();
|
|
2591
3513
|
const typedCall = jest.fn().mockResolvedValue({ type: 'DeviceInfo', message: {} });
|
|
@@ -2597,10 +3519,10 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2597
3519
|
await method.run();
|
|
2598
3520
|
|
|
2599
3521
|
expect(typedCall).toHaveBeenCalledWith(
|
|
2600
|
-
'
|
|
3522
|
+
'DeviceInfoGet',
|
|
2601
3523
|
'DeviceInfo',
|
|
2602
3524
|
{
|
|
2603
|
-
targets: { hw: true, fw: true,
|
|
3525
|
+
targets: { hw: true, fw: true, coprocessor: true, status: true },
|
|
2604
3526
|
types: { version: true, specific: true },
|
|
2605
3527
|
},
|
|
2606
3528
|
{ timeoutMs: PROTOCOL_V2_DEVICE_INFO_TIMEOUT_MS }
|
|
@@ -2620,48 +3542,6 @@ describe('Protocol V2 raw device info method', () => {
|
|
|
2620
3542
|
});
|
|
2621
3543
|
});
|
|
2622
3544
|
|
|
2623
|
-
describe('Protocol V2 onboarding status method', () => {
|
|
2624
|
-
test('returns OnboardingStatus from low-level status query', async () => {
|
|
2625
|
-
const method = new DeviceGetOnboardingStatus({
|
|
2626
|
-
id: 1,
|
|
2627
|
-
payload: {
|
|
2628
|
-
method: 'deviceGetOnboardingStatus',
|
|
2629
|
-
},
|
|
2630
|
-
});
|
|
2631
|
-
method.init();
|
|
2632
|
-
|
|
2633
|
-
const typedCall = jest.fn().mockResolvedValue({
|
|
2634
|
-
type: 'OnboardingStatus',
|
|
2635
|
-
message: {
|
|
2636
|
-
step: 3,
|
|
2637
|
-
setup: {
|
|
2638
|
-
restore: {
|
|
2639
|
-
mnemonic: true,
|
|
2640
|
-
},
|
|
2641
|
-
},
|
|
2642
|
-
detail_code: 7,
|
|
2643
|
-
detail_str: 'Recovery Phrase',
|
|
2644
|
-
},
|
|
2645
|
-
});
|
|
2646
|
-
|
|
2647
|
-
(method as any).device = stubDevice({
|
|
2648
|
-
commands: { typedCall },
|
|
2649
|
-
});
|
|
2650
|
-
|
|
2651
|
-
await expect(method.run()).resolves.toEqual({
|
|
2652
|
-
step: 3,
|
|
2653
|
-
setup: {
|
|
2654
|
-
restore: {
|
|
2655
|
-
mnemonic: true,
|
|
2656
|
-
},
|
|
2657
|
-
},
|
|
2658
|
-
detail_code: 7,
|
|
2659
|
-
detail_str: 'Recovery Phrase',
|
|
2660
|
-
});
|
|
2661
|
-
expect(typedCall).toHaveBeenCalledWith('GetOnboardingStatus', 'OnboardingStatus', {});
|
|
2662
|
-
});
|
|
2663
|
-
});
|
|
2664
|
-
|
|
2665
3545
|
describe('Protocol V2 file write method', () => {
|
|
2666
3546
|
test('rejects invalid write parameters before transport call', () => {
|
|
2667
3547
|
expect(() => {
|
|
@@ -2711,17 +3591,22 @@ describe('Protocol V2 file write method', () => {
|
|
|
2711
3591
|
method.init();
|
|
2712
3592
|
await method.run();
|
|
2713
3593
|
|
|
2714
|
-
expect(typedCall).toHaveBeenCalledWith(
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
3594
|
+
expect(typedCall).toHaveBeenCalledWith(
|
|
3595
|
+
'FilesystemFileWrite',
|
|
3596
|
+
'FilesystemFile',
|
|
3597
|
+
{
|
|
3598
|
+
file: {
|
|
3599
|
+
path: 'vol1:test.bin',
|
|
3600
|
+
offset: 1,
|
|
3601
|
+
total_size: 2,
|
|
3602
|
+
data: new Uint8Array([1]),
|
|
3603
|
+
},
|
|
3604
|
+
overwrite: false,
|
|
3605
|
+
append: false,
|
|
3606
|
+
ui_percentage: 100,
|
|
2720
3607
|
},
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
ui_percentage: 99,
|
|
2724
|
-
}, { timeoutMs: undefined });
|
|
3608
|
+
{ timeoutMs: undefined }
|
|
3609
|
+
);
|
|
2725
3610
|
expect(method.postMessage).toHaveBeenCalledWith({
|
|
2726
3611
|
event: 'UI_EVENT',
|
|
2727
3612
|
type: UI_REQUEST.DEVICE_PROGRESS,
|
|
@@ -2754,28 +3639,40 @@ describe('Protocol V2 file write method', () => {
|
|
|
2754
3639
|
const result = await method.run();
|
|
2755
3640
|
|
|
2756
3641
|
expect(typedCall).toHaveBeenCalledTimes(2);
|
|
2757
|
-
expect(typedCall).toHaveBeenNthCalledWith(
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
3642
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
3643
|
+
1,
|
|
3644
|
+
'FilesystemFileWrite',
|
|
3645
|
+
'FilesystemFile',
|
|
3646
|
+
{
|
|
3647
|
+
file: {
|
|
3648
|
+
path: 'vol1:test.bin',
|
|
3649
|
+
offset: 0,
|
|
3650
|
+
total_size: 4097,
|
|
3651
|
+
data: data.slice(0, 4000),
|
|
3652
|
+
},
|
|
3653
|
+
overwrite: true,
|
|
3654
|
+
append: false,
|
|
3655
|
+
ui_percentage: 0,
|
|
3656
|
+
},
|
|
3657
|
+
{ timeoutMs: undefined }
|
|
3658
|
+
);
|
|
3659
|
+
expect(typedCall).toHaveBeenNthCalledWith(
|
|
3660
|
+
2,
|
|
3661
|
+
'FilesystemFileWrite',
|
|
3662
|
+
'FilesystemFile',
|
|
3663
|
+
{
|
|
3664
|
+
file: {
|
|
3665
|
+
path: 'vol1:test.bin',
|
|
3666
|
+
offset: 4000,
|
|
3667
|
+
total_size: 4097,
|
|
3668
|
+
data: data.slice(4000),
|
|
3669
|
+
},
|
|
3670
|
+
overwrite: false,
|
|
3671
|
+
append: false,
|
|
3672
|
+
ui_percentage: 100,
|
|
3673
|
+
},
|
|
3674
|
+
{ timeoutMs: undefined }
|
|
3675
|
+
);
|
|
2779
3676
|
expect(result).toMatchObject({
|
|
2780
3677
|
path: 'vol1:test.bin',
|
|
2781
3678
|
processed_byte: 4097,
|
|
@@ -2785,8 +3682,8 @@ describe('Protocol V2 file write method', () => {
|
|
|
2785
3682
|
event: 'UI_EVENT',
|
|
2786
3683
|
type: UI_REQUEST.DEVICE_PROGRESS,
|
|
2787
3684
|
payload: expect.objectContaining({
|
|
2788
|
-
progress:
|
|
2789
|
-
transferredBytes:
|
|
3685
|
+
progress: 97,
|
|
3686
|
+
transferredBytes: 4000,
|
|
2790
3687
|
totalBytes: 4097,
|
|
2791
3688
|
elapsedMs: expect.any(Number),
|
|
2792
3689
|
}),
|
|
@@ -2911,7 +3808,7 @@ describe('Protocol V2 file read method', () => {
|
|
|
2911
3808
|
total_size: 0,
|
|
2912
3809
|
},
|
|
2913
3810
|
chunk_len: 64,
|
|
2914
|
-
ui_percentage:
|
|
3811
|
+
ui_percentage: 0,
|
|
2915
3812
|
});
|
|
2916
3813
|
expect(typedCall).toHaveBeenNthCalledWith(3, 'FilesystemFileRead', 'FilesystemFile', {
|
|
2917
3814
|
file: {
|
|
@@ -2920,7 +3817,7 @@ describe('Protocol V2 file read method', () => {
|
|
|
2920
3817
|
total_size: 0,
|
|
2921
3818
|
},
|
|
2922
3819
|
chunk_len: 1,
|
|
2923
|
-
ui_percentage:
|
|
3820
|
+
ui_percentage: 100,
|
|
2924
3821
|
});
|
|
2925
3822
|
expect(result.data.byteLength).toBe(65);
|
|
2926
3823
|
expect(result.data[0]).toBe(1);
|