Add a few unit tests for MspHelper class (#3425)
* Add a few unit tests for MSPHelper * Simplify encode_message_v1 * Simplify MSP_BOARD_INFO handlingmaster
parent
61e31e4250
commit
1657351598
|
@ -262,41 +262,26 @@ const MSP = {
|
||||||
return crc;
|
return crc;
|
||||||
},
|
},
|
||||||
encode_message_v1: function(code, data) {
|
encode_message_v1: function(code, data) {
|
||||||
let bufferOut;
|
const dataLength = data ? data.length : 0;
|
||||||
// always reserve 6 bytes for protocol overhead !
|
// always reserve 6 bytes for protocol overhead !
|
||||||
if (data) {
|
const bufferSize = dataLength + 6;
|
||||||
const size = data.length + 6;
|
let bufferOut = new ArrayBuffer(bufferSize);
|
||||||
let checksum = 0;
|
|
||||||
|
|
||||||
bufferOut = new ArrayBuffer(size);
|
|
||||||
let bufView = new Uint8Array(bufferOut);
|
let bufView = new Uint8Array(bufferOut);
|
||||||
|
|
||||||
bufView[0] = 36; // $
|
bufView[0] = 36; // $
|
||||||
bufView[1] = 77; // M
|
bufView[1] = 77; // M
|
||||||
bufView[2] = 60; // <
|
bufView[2] = 60; // <
|
||||||
bufView[3] = data.length;
|
bufView[3] = dataLength;
|
||||||
bufView[4] = code;
|
bufView[4] = code;
|
||||||
|
|
||||||
checksum = bufView[3] ^ bufView[4];
|
let checksum = bufView[3] ^ bufView[4];
|
||||||
|
|
||||||
for (let i = 0; i < data.length; i++) {
|
for (let i = 0; i < dataLength; i++) {
|
||||||
bufView[i + 5] = data[i];
|
bufView[i + 5] = data[i];
|
||||||
|
|
||||||
checksum ^= bufView[i + 5];
|
checksum ^= bufView[i + 5];
|
||||||
}
|
}
|
||||||
|
|
||||||
bufView[5 + data.length] = checksum;
|
bufView[5 + dataLength] = checksum;
|
||||||
} else {
|
|
||||||
bufferOut = new ArrayBuffer(6);
|
|
||||||
let bufView = new Uint8Array(bufferOut);
|
|
||||||
|
|
||||||
bufView[0] = 36; // $
|
|
||||||
bufView[1] = 77; // M
|
|
||||||
bufView[2] = 60; // <
|
|
||||||
bufView[3] = 0; // data length
|
|
||||||
bufView[4] = code; // code
|
|
||||||
bufView[5] = bufView[3] ^ bufView[4]; // checksum
|
|
||||||
}
|
|
||||||
return bufferOut;
|
return bufferOut;
|
||||||
},
|
},
|
||||||
encode_message_v2: function (code, data) {
|
encode_message_v2: function (code, data) {
|
||||||
|
|
|
@ -787,35 +787,15 @@ MspHelper.prototype.process_data = function(dataHandler) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FC.CONFIG.boardVersion = data.readU16();
|
FC.CONFIG.boardVersion = data.readU16();
|
||||||
FC.CONFIG.boardType = 0;
|
|
||||||
|
|
||||||
FC.CONFIG.boardType = data.readU8();
|
FC.CONFIG.boardType = data.readU8();
|
||||||
|
|
||||||
FC.CONFIG.targetCapabilities = 0;
|
|
||||||
FC.CONFIG.targetName = '';
|
|
||||||
|
|
||||||
FC.CONFIG.targetCapabilities = data.readU8();
|
FC.CONFIG.targetCapabilities = data.readU8();
|
||||||
let length = data.readU8();
|
FC.CONFIG.targetName = this.getText(data);
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
FC.CONFIG.targetName += String.fromCharCode(data.readU8());
|
|
||||||
}
|
|
||||||
|
|
||||||
FC.CONFIG.boardName = '';
|
FC.CONFIG.boardName = this.getText(data);
|
||||||
FC.CONFIG.manufacturerId = '';
|
FC.CONFIG.manufacturerId = this.getText(data);
|
||||||
FC.CONFIG.signature = [];
|
FC.CONFIG.signature = [];
|
||||||
|
|
||||||
length = data.readU8();
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
FC.CONFIG.boardName += String.fromCharCode(data.readU8());
|
|
||||||
}
|
|
||||||
|
|
||||||
length = data.readU8();
|
|
||||||
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
FC.CONFIG.manufacturerId += String.fromCharCode(data.readU8());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < self.SIGNATURE_LENGTH; i++) {
|
for (let i = 0; i < self.SIGNATURE_LENGTH; i++) {
|
||||||
FC.CONFIG.signature.push(data.readU8());
|
FC.CONFIG.signature.push(data.readU8());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,165 @@
|
||||||
|
import {beforeEach, describe, expect, it} from "vitest";
|
||||||
|
import MspHelper from "../../../src/js/msp/MSPHelper";
|
||||||
|
import MSPCodes from "../../../src/js/msp/MSPCodes";
|
||||||
|
import '../../../src/js/injected_methods';
|
||||||
|
import FC from "../../../src/js/fc";
|
||||||
|
import {API_VERSION_1_46} from "../../../src/js/data_storage";
|
||||||
|
|
||||||
|
describe("MspHelper", () => {
|
||||||
|
const mspHelper = new MspHelper();
|
||||||
|
beforeEach(() => {
|
||||||
|
FC.resetState();
|
||||||
|
});
|
||||||
|
describe("process_data", () => {
|
||||||
|
it("refuses to process data with crc-error", () => {
|
||||||
|
let callbackCalled = false;
|
||||||
|
|
||||||
|
let callbackFunction = (item) => {
|
||||||
|
callbackCalled = true;
|
||||||
|
expect(item['crcError']).toEqual(true);
|
||||||
|
expect(item['command']).toEqual(MSPCodes.MSP_BOARD_INFO);
|
||||||
|
expect(item['length']).toEqual(0);
|
||||||
|
};
|
||||||
|
|
||||||
|
mspHelper.process_data({
|
||||||
|
code: MSPCodes.MSP_BOARD_INFO,
|
||||||
|
dataView: new DataView(new Uint8Array([]).buffer),
|
||||||
|
crcError: true,
|
||||||
|
callbacks: [{
|
||||||
|
callback: callbackFunction,
|
||||||
|
callbackOnError: true,
|
||||||
|
code: MSPCodes.MSP_BOARD_INFO,
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(callbackCalled).toEqual(true);
|
||||||
|
});
|
||||||
|
it("handles MSP_API_VERSION correctly", () => {
|
||||||
|
let randomValues = crypto.getRandomValues(new Uint8Array(3));
|
||||||
|
const [mspProtocolVersion, apiVersionMajor, apiVersionMinor] = randomValues;
|
||||||
|
mspHelper.process_data({
|
||||||
|
code: MSPCodes.MSP_API_VERSION,
|
||||||
|
dataView: new DataView(randomValues.buffer),
|
||||||
|
crcError: false,
|
||||||
|
callbacks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(FC.CONFIG.mspProtocolVersion).toEqual(mspProtocolVersion);
|
||||||
|
expect(FC.CONFIG.apiVersion).toEqual(`${apiVersionMajor}.${apiVersionMinor}.0`);
|
||||||
|
});
|
||||||
|
it("handles MSP_PIDNAMES correctly", () => {
|
||||||
|
let pidNamesCount = 1 + crypto.getRandomValues(new Uint8Array(1))[0];
|
||||||
|
let expectedNames = Array.from({length: pidNamesCount}).map(_ => generateRandomString());
|
||||||
|
|
||||||
|
let lowLevelData = [];
|
||||||
|
appendStringToArray(lowLevelData, `${expectedNames.join(';')};`);
|
||||||
|
|
||||||
|
mspHelper.process_data({
|
||||||
|
code: MSPCodes.MSP_PIDNAMES,
|
||||||
|
dataView: new DataView(new Uint8Array(lowLevelData).buffer),
|
||||||
|
crcError: false,
|
||||||
|
callbacks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(FC.PID_NAMES).toEqual(expectedNames);
|
||||||
|
});
|
||||||
|
it("handles MSP_MOTOR correctly", () => {
|
||||||
|
let motorCount = crypto.getRandomValues(new Uint8Array(1))[0] % 8;
|
||||||
|
let motorBytes = crypto.getRandomValues(new Uint16Array(motorCount));
|
||||||
|
|
||||||
|
mspHelper.process_data({
|
||||||
|
code: MSPCodes.MSP_MOTOR,
|
||||||
|
dataView: new DataView(new Uint16Array(motorBytes).buffer),
|
||||||
|
crcError: false,
|
||||||
|
callbacks: [],
|
||||||
|
});
|
||||||
|
expect(new Uint16Array(FC.MOTOR_DATA).slice(0, motorCount)).toEqual(motorBytes);
|
||||||
|
expect(FC.MOTOR_DATA.slice(motorCount, 8)).toContain(undefined);
|
||||||
|
});
|
||||||
|
it("handles MSP_BOARD_INFO correctly for API version", () => {
|
||||||
|
FC.CONFIG.apiVersion = API_VERSION_1_46;
|
||||||
|
let infoBuffer = [];
|
||||||
|
|
||||||
|
const boardIdentifier = appendStringToArray(infoBuffer, generateRandomString(4)); // set board-identifier
|
||||||
|
|
||||||
|
infoBuffer.push16(0xDEAD); // set board version
|
||||||
|
infoBuffer.push8(0x12); // set board type
|
||||||
|
infoBuffer.push8(0x32); // set target capabilities
|
||||||
|
|
||||||
|
const targetName = appendStringToArray(infoBuffer, generateRandomString(), true); // set target name
|
||||||
|
const boardName = appendStringToArray(infoBuffer, generateRandomString(), true); // set board name
|
||||||
|
const manufacturerId = appendStringToArray(infoBuffer, generateRandomString(), true); // set board name
|
||||||
|
const signature = crypto.getRandomValues(new Uint8Array(32));
|
||||||
|
|
||||||
|
signature.forEach(element => infoBuffer.push8(element));
|
||||||
|
infoBuffer.push8(0xFA); // mcu type id
|
||||||
|
infoBuffer.push8(0xBB); // configuration state
|
||||||
|
infoBuffer.push16(0xBAAB); // sample rate
|
||||||
|
infoBuffer.push32(0xDEADBEEF); // configuration problems
|
||||||
|
|
||||||
|
mspHelper.process_data({
|
||||||
|
code: MSPCodes.MSP_BOARD_INFO,
|
||||||
|
dataView: new DataView(new Uint8Array(infoBuffer).buffer),
|
||||||
|
crcError: false,
|
||||||
|
callbacks: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(FC.CONFIG.boardIdentifier).toEqual(boardIdentifier);
|
||||||
|
expect(FC.CONFIG.boardVersion).toEqual(0xDEAD);
|
||||||
|
expect(FC.CONFIG.boardType).toEqual(0x12);
|
||||||
|
expect(FC.CONFIG.targetCapabilities).toEqual(0x32);
|
||||||
|
expect(FC.CONFIG.targetName).toEqual(targetName);
|
||||||
|
expect(FC.CONFIG.boardName).toEqual(boardName);
|
||||||
|
expect(FC.CONFIG.manufacturerId).toEqual(manufacturerId);
|
||||||
|
expect(new Uint8Array(FC.CONFIG.signature)).toEqual(signature);
|
||||||
|
expect(FC.CONFIG.mcuTypeId).toEqual(0xFA);
|
||||||
|
|
||||||
|
expect(FC.CONFIG.configurationState).toEqual(0xBB);
|
||||||
|
expect(FC.CONFIG.sampleRateHz).toEqual(0xBAAB);
|
||||||
|
expect(FC.CONFIG.configurationProblems).toEqual(0xDEADBEEF);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends given string to an array. If required, it will append length of the string by length.
|
||||||
|
* @param destination array to which we append given string (and length if required)
|
||||||
|
* @param source string to append to an array
|
||||||
|
* @param prefixWithLength should we prefix the string by its length in the array
|
||||||
|
* @returns {*} string that was requested to be inserted to the array
|
||||||
|
*/
|
||||||
|
function appendStringToArray(destination, source, prefixWithLength = false) {
|
||||||
|
const size = source.length;
|
||||||
|
|
||||||
|
if (prefixWithLength) {
|
||||||
|
destination.push8(source.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
destination.push8(source.charCodeAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random string of required length. If required length is -1, it will generate a random string of a random length.
|
||||||
|
* @param length required random string length. If lower than 0, it will generate a string of random length.
|
||||||
|
* @returns {string} random string (composed of letters [A-Za-z0-9])
|
||||||
|
*/
|
||||||
|
function generateRandomString(length = -1) {
|
||||||
|
let result = '';
|
||||||
|
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
length = crypto.getRandomValues(new Uint8Array(1))[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
const signature = crypto.getRandomValues(new Uint8Array(length));
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
result += characters.charAt(signature[i] % charactersLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
Loading…
Reference in New Issue