Add a few unit tests for MspHelper class (#3425)

* Add a few unit tests for MSPHelper

* Simplify encode_message_v1

* Simplify MSP_BOARD_INFO handling
master
Richard Benkovsky 2023-04-19 12:44:05 -07:00 committed by GitHub
parent 61e31e4250
commit 1657351598
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 183 additions and 53 deletions

View File

@ -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; let bufView = new Uint8Array(bufferOut);
bufferOut = new ArrayBuffer(size); bufView[0] = 36; // $
let bufView = new Uint8Array(bufferOut); bufView[1] = 77; // M
bufView[2] = 60; // <
bufView[3] = dataLength;
bufView[4] = code;
bufView[0] = 36; // $ let checksum = bufView[3] ^ bufView[4];
bufView[1] = 77; // M
bufView[2] = 60; // <
bufView[3] = data.length;
bufView[4] = code;
checksum = bufView[3] ^ bufView[4]; for (let i = 0; i < dataLength; i++) {
bufView[i + 5] = data[i];
for (let i = 0; i < data.length; i++) { checksum ^= bufView[i + 5];
bufView[i + 5] = data[i];
checksum ^= bufView[i + 5];
}
bufView[5 + data.length] = 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
} }
bufView[5 + dataLength] = checksum;
return bufferOut; return bufferOut;
}, },
encode_message_v2: function (code, data) { encode_message_v2: function (code, data) {

View File

@ -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());
} }

View File

@ -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;
}