proper error handling for failure to connect

This commit is contained in:
David Lechner
2021-01-22 15:58:10 -06:00
parent d38ae70ab2
commit 2d44705112
2 changed files with 168 additions and 114 deletions
+165 -108
View File
@@ -18,11 +18,13 @@ import {
flashFirmware as flashFirmwareAction,
} from '../actions/flash-firmware';
import {
BootloaderConnectionFailureReason,
BootloaderProgramRequestAction,
checksumRequest,
checksumResponse,
connect,
didConnect,
didFailToConnect,
didRequest,
eraseRequest,
eraseResponse,
@@ -44,143 +46,198 @@ afterEach(() => {
});
describe('flashFirmware', () => {
test('normal flow', async () => {
const metadata: FirmwareMetadata = {
'metadata-version': '1.0.0',
'device-id': HubType.MoveHub,
'checksum-type': 'sum',
'firmware-version': '1.2.3',
'max-firmware-size': 1024,
'mpy-abi-version': 5,
'mpy-cross-options': ['-mno-unicode'],
'user-mpy-offset': 100,
};
describe('normal flow', () => {
test('success', async () => {
const metadata: FirmwareMetadata = {
'metadata-version': '1.0.0',
'device-id': HubType.MoveHub,
'checksum-type': 'sum',
'firmware-version': '1.2.3',
'max-firmware-size': 1024,
'mpy-abi-version': 5,
'mpy-cross-options': ['-mno-unicode'],
'user-mpy-offset': 100,
};
const zip = new JSZip();
zip.file('firmware-base.bin', new Uint8Array(64));
zip.file('firmware.metadata.json', JSON.stringify(metadata));
zip.file('main.py', 'print("test")');
zip.file('ReadMe_OSS.txt', 'test');
const zip = new JSZip();
zip.file('firmware-base.bin', new Uint8Array(64));
zip.file('firmware.metadata.json', JSON.stringify(metadata));
zip.file('main.py', 'print("test")');
zip.file('ReadMe_OSS.txt', 'test');
jest.spyOn(window, 'fetch').mockResolvedValueOnce(
new Response(await zip.generateAsync({ type: 'blob' })),
);
jest.spyOn(window, 'fetch').mockResolvedValueOnce(
new Response(await zip.generateAsync({ type: 'blob' })),
);
const saga = new AsyncSaga(flashFirmware, { nextMessageId: createCountFunc() });
const saga = new AsyncSaga(flashFirmware, {
nextMessageId: createCountFunc(),
});
saga.setState({ settings: { flashCurrentProgram: false } });
saga.setState({ settings: { flashCurrentProgram: false } });
// saga is triggered by this action
// saga is triggered by this action
saga.put(flashFirmwareAction());
saga.put(flashFirmwareAction());
// first step is to connect to the hub bootloader
// first step is to connect to the hub bootloader
let action = await saga.take();
expect(action).toEqual(connect());
let action = await saga.take();
expect(action).toEqual(connect());
saga.put(didConnect());
saga.put(didConnect());
// then find out what kind of hub it is
// then find out what kind of hub it is
action = await saga.take();
expect(action).toEqual(infoRequest(0));
saga.put(didRequest(0));
saga.put(infoResponse(0x01000000, 0x08005000, 0x081f800, HubType.MoveHub));
// then compile main.py to .mpy
action = await saga.take();
expect(action).toMatchInlineSnapshot(`
Object {
"options": Array [
"-mno-unicode",
],
"script": "print(\\"test\\")",
"type": "mpy.action.compile",
}
`);
const mpySize = 20;
const mpyBinaryData = new Uint8Array(mpySize);
saga.put(didCompile(mpyBinaryData));
// then start flashing the firmware
// should get didStart action just before starting to erase
action = await saga.take();
expect(action).toEqual(didStart());
// erase first
action = await saga.take();
expect(action).toEqual(eraseRequest(1));
saga.put(didRequest(1));
saga.put(eraseResponse(Result.OK));
// then write the new firmware
const totalFirmwareSize = metadata['user-mpy-offset'] + mpySize + 8;
action = await saga.take();
expect(action).toEqual(initRequest(2, totalFirmwareSize));
saga.put(didRequest(2));
saga.put(initResponse(Result.OK));
const dummyPayload = new ArrayBuffer(0);
let id = 2;
for (let count = 1, offset = 0; ; count++, offset += 14) {
action = await saga.take();
expect(action).toEqual(
programRequest(++id, 0x08005000 + offset, dummyPayload),
);
expect((action as BootloaderProgramRequestAction).payload.byteLength).toBe(
Math.min(14, totalFirmwareSize - offset),
);
expect(action).toEqual(infoRequest(0));
saga.put(didRequest(0));
saga.put(infoResponse(0x01000000, 0x08005000, 0x081f800, HubType.MoveHub));
// then compile main.py to .mpy
action = await saga.take();
expect(action).toMatchInlineSnapshot(`
Object {
"options": Array [
"-mno-unicode",
],
"script": "print(\\"test\\")",
"type": "mpy.action.compile",
}
`);
const mpySize = 20;
const mpyBinaryData = new Uint8Array(mpySize);
saga.put(didCompile(mpyBinaryData));
// then start flashing the firmware
// should get didStart action just before starting to erase
action = await saga.take();
expect(action).toEqual(didStart());
// erase first
action = await saga.take();
expect(action).toEqual(eraseRequest(1));
saga.put(didRequest(1));
saga.put(eraseResponse(Result.OK));
// then write the new firmware
const totalFirmwareSize = metadata['user-mpy-offset'] + mpySize + 8;
action = await saga.take();
expect(action).toEqual(initRequest(2, totalFirmwareSize));
saga.put(didRequest(2));
saga.put(initResponse(Result.OK));
const dummyPayload = new ArrayBuffer(0);
let id = 2;
for (let count = 1, offset = 0; ; count++, offset += 14) {
action = await saga.take();
expect(action).toEqual(
programRequest(++id, 0x08005000 + offset, dummyPayload),
);
expect(
(action as BootloaderProgramRequestAction).payload.byteLength,
).toBe(Math.min(14, totalFirmwareSize - offset));
saga.put(didRequest(id));
action = await saga.take();
expect(action).toEqual(didProgress(offset / totalFirmwareSize));
// Have to be careful that a checksum request is not sent after
// last payload is sent, otherwise the hub gets confused.
if (offset + 14 >= totalFirmwareSize) {
break;
}
if (count % 10 === 0) {
action = await saga.take();
expect(action).toEqual(checksumRequest(++id));
saga.put(didRequest(id));
saga.put(checksumResponse(0));
}
}
// hub indicates success
saga.put(programResponse(0, totalFirmwareSize));
action = await saga.take();
expect(action).toEqual(didProgress(1));
// and finally reboot the hub
action = await saga.take();
expect(action).toEqual(rebootRequest(++id));
saga.put(didRequest(id));
// then we are done
action = await saga.take();
expect(action).toEqual(didProgress(offset / totalFirmwareSize));
expect(action).toEqual(didFinish());
// Have to be careful that a checksum request is not sent after
// last payload is sent, otherwise the hub gets confused.
await saga.end();
});
if (offset + 14 >= totalFirmwareSize) {
break;
}
test('fail to connect', async () => {
const metadata: FirmwareMetadata = {
'metadata-version': '1.0.0',
'device-id': HubType.MoveHub,
'checksum-type': 'sum',
'firmware-version': '1.2.3',
'max-firmware-size': 1024,
'mpy-abi-version': 5,
'mpy-cross-options': ['-mno-unicode'],
'user-mpy-offset': 100,
};
if (count % 10 === 0) {
action = await saga.take();
expect(action).toEqual(checksumRequest(++id));
const zip = new JSZip();
zip.file('firmware-base.bin', new Uint8Array(64));
zip.file('firmware.metadata.json', JSON.stringify(metadata));
zip.file('main.py', 'print("test")');
zip.file('ReadMe_OSS.txt', 'test');
saga.put(didRequest(id));
saga.put(checksumResponse(0));
}
}
jest.spyOn(window, 'fetch').mockResolvedValueOnce(
new Response(await zip.generateAsync({ type: 'blob' })),
);
// hub indicates success
const saga = new AsyncSaga(flashFirmware, {
nextMessageId: createCountFunc(),
});
saga.put(programResponse(0, totalFirmwareSize));
saga.setState({ settings: { flashCurrentProgram: false } });
action = await saga.take();
expect(action).toEqual(didProgress(1));
// saga is triggered by this action
// and finally reboot the hub
saga.put(flashFirmwareAction());
action = await saga.take();
expect(action).toEqual(rebootRequest(++id));
// first step is to connect to the hub bootloader
saga.put(didRequest(id));
let action = await saga.take();
expect(action).toEqual(connect());
// then we are done
saga.put(
didFailToConnect(BootloaderConnectionFailureReason.GattServiceNotFound),
);
action = await saga.take();
expect(action).toEqual(didFinish());
// it should fail here because of failure to connect
await saga.end();
action = await saga.take();
expect(action).toEqual(
didFailToStart(FailToStartReasonType.FailedToConnect),
);
await saga.end();
});
});
describe('user supplied firmware.zip', () => {
+3 -6
View File
@@ -31,9 +31,8 @@ import {
} from '../actions/flash-firmware';
import {
BootloaderChecksumResponseAction,
BootloaderConnectionAction,
BootloaderConnectionActionType,
BootloaderConnectionDidConnectAction,
BootloaderConnectionDidFailToConnectAction,
BootloaderDidRequestAction,
BootloaderDidRequestType,
BootloaderEraseResponseAction,
@@ -233,15 +232,13 @@ function* flashFirmware(action: FlashFirmwareFlashAction): Generator {
}
yield* put(connect());
const connectResult = yield* take<
| BootloaderConnectionDidConnectAction
| BootloaderConnectionDidFailToConnectAction
>([
const connectResult = yield* take<BootloaderConnectionAction>([
BootloaderConnectionActionType.DidConnect,
BootloaderConnectionActionType.DidFailToConnect,
]);
if (connectResult.type === BootloaderConnectionActionType.DidFailToConnect) {
yield* put(didFailToStart(FailToStartReasonType.FailedToConnect));
return;
}