mirror of
https://github.com/pybricks/pybricks-code.git
synced 2026-09-11 17:14:15 +00:00
add tests for bootloader protocol errors
This commit is contained in:
committed by
David Lechner
parent
3e4ea382e5
commit
7a7eba407f
@@ -77,6 +77,20 @@ export enum ProtectionLevel {
|
||||
Level2 = 0x02,
|
||||
}
|
||||
|
||||
/**
|
||||
* Protocol error. Thrown e.g. when there is a malformed message.
|
||||
*/
|
||||
export class ProtocolError extends Error {
|
||||
/**
|
||||
* Creates a new ProtocolError.
|
||||
* @param message The error message
|
||||
* @param data The bytecodes that caused the error
|
||||
*/
|
||||
constructor(message: string, public data: DataView) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new message to erase the flash memory.
|
||||
*/
|
||||
@@ -192,11 +206,20 @@ export function parseErrorResponse(msg: DataView): Command {
|
||||
assert(msg.getUint8(0) === 5, 'unexpected length');
|
||||
// Error responses are ordered differently compared to command responses.
|
||||
if (msg.getUint8(2) !== ErrorBytecode) {
|
||||
throw Error('expecting error');
|
||||
throw new ProtocolError(
|
||||
`expecting error bytecode 0x05 but got 0x${msg
|
||||
.getUint8(2)
|
||||
.toString(16)
|
||||
.padStart(2, '0')}`,
|
||||
msg,
|
||||
);
|
||||
}
|
||||
if (msg.getUint8(4) !== ErrorCode.UnknownCommand) {
|
||||
// "command not recognized" is only possible error code
|
||||
throw Error('unexpected error code');
|
||||
throw new ProtocolError(
|
||||
`unknown error code: 0x${msg.getUint8(4).toString(16).padStart(2, '0')}`,
|
||||
msg,
|
||||
);
|
||||
}
|
||||
const command = msg.getUint8(3);
|
||||
return command;
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
BootloaderRequestActionType,
|
||||
checksumRequest,
|
||||
checksumResponse,
|
||||
didError,
|
||||
didReceive,
|
||||
didRequest,
|
||||
didSend,
|
||||
@@ -25,7 +26,13 @@ import {
|
||||
stateRequest,
|
||||
stateResponse,
|
||||
} from '../actions/bootloader';
|
||||
import { Command, HubType, ProtectionLevel, Result } from '../protocols/bootloader';
|
||||
import {
|
||||
Command,
|
||||
HubType,
|
||||
ProtectionLevel,
|
||||
ProtocolError,
|
||||
Result,
|
||||
} from '../protocols/bootloader';
|
||||
import { createCountFunc } from '../utils/iter';
|
||||
import bootloader from './bootloader';
|
||||
|
||||
@@ -271,4 +278,70 @@ describe('message decoder', () => {
|
||||
await task.toPromise();
|
||||
expect(dispatched[0]).toEqual(expected);
|
||||
});
|
||||
|
||||
test.each([
|
||||
[
|
||||
'bad error bytecode',
|
||||
[
|
||||
0x05, // length
|
||||
0x00, // unused (hub id)
|
||||
0x04, // **invalid message type**
|
||||
0x77, // get flash state command
|
||||
0x05, // command not recognized
|
||||
],
|
||||
didError(
|
||||
new ProtocolError(
|
||||
'expecting error bytecode 0x05 but got 0x04',
|
||||
new DataView(new Uint8Array().buffer),
|
||||
),
|
||||
),
|
||||
],
|
||||
[
|
||||
'unknown error code',
|
||||
[
|
||||
0x05, // length
|
||||
0x00, // unused (hub id)
|
||||
0x05, // flash loader error message
|
||||
0x77, // get flash state command
|
||||
0x04, // **invalid error code**
|
||||
],
|
||||
didError(
|
||||
new ProtocolError(
|
||||
'unknown error code: 0x04',
|
||||
new DataView(new Uint8Array().buffer),
|
||||
),
|
||||
),
|
||||
],
|
||||
[
|
||||
'unknown response',
|
||||
[
|
||||
0x00, // **bad command**
|
||||
0x01, // **junk**
|
||||
0x02, // **junk**
|
||||
0x03, // **junk**
|
||||
0x04, // **junk**
|
||||
],
|
||||
didError(
|
||||
new ProtocolError(
|
||||
'unknown bootloader response type: 0x00',
|
||||
new DataView(new Uint8Array().buffer),
|
||||
),
|
||||
),
|
||||
],
|
||||
])('protocol error', async (_n, message, expected) => {
|
||||
const response = new Uint8Array(message);
|
||||
const channel = stdChannel();
|
||||
const dispatched = new Array<Action>();
|
||||
const task = runSaga(
|
||||
{
|
||||
channel,
|
||||
dispatch: (action: Action) => dispatched.push(action),
|
||||
},
|
||||
bootloader,
|
||||
);
|
||||
channel.put(didReceive(new DataView(response.buffer)));
|
||||
task.cancel();
|
||||
await task.toPromise();
|
||||
expect(dispatched[0]).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
checksumRequest,
|
||||
checksumResponse,
|
||||
connect,
|
||||
didError,
|
||||
didRequest,
|
||||
disconnectRequest,
|
||||
eraseRequest,
|
||||
@@ -65,6 +66,7 @@ import {
|
||||
ErrorBytecode,
|
||||
HubType,
|
||||
MaxProgramFlashSize,
|
||||
ProtocolError,
|
||||
createDisconnectRequest,
|
||||
createEraseFlashRequest,
|
||||
createGetChecksumRequest,
|
||||
@@ -184,11 +186,15 @@ function* decodeResponse(action: BootloaderConnectionDidReceiveAction): Generato
|
||||
yield put(errorResponse(parseErrorResponse(action.data)));
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown bootloader response action ${action}`);
|
||||
throw new ProtocolError(
|
||||
`unknown bootloader response type: 0x${responseType
|
||||
.toString(16)
|
||||
.padStart(2, '0')}`,
|
||||
action.data,
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
// TODO: dispatch an error action
|
||||
console.error(`Error decoding message: ${err}`);
|
||||
yield put(didError(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"target": "es2018",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
|
||||
Reference in New Issue
Block a user