Skip to content

Commit

Permalink
Add a few unit tests for MspHelper class (#3425)
Browse files Browse the repository at this point in the history
* Add a few unit tests for MSPHelper

* Simplify encode_message_v1

* Simplify MSP_BOARD_INFO handling
  • Loading branch information
Benky authored Apr 19, 2023
1 parent 61e31e4 commit 1657351
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 53 deletions.
45 changes: 15 additions & 30 deletions src/js/msp.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,41 +262,26 @@ const MSP = {
return crc;
},
encode_message_v1: function(code, data) {
let bufferOut;
const dataLength = data ? data.length : 0;
// always reserve 6 bytes for protocol overhead !
if (data) {
const size = data.length + 6;
let checksum = 0;

bufferOut = new ArrayBuffer(size);
let bufView = new Uint8Array(bufferOut);

bufView[0] = 36; // $
bufView[1] = 77; // M
bufView[2] = 60; // <
bufView[3] = data.length;
bufView[4] = code;

checksum = bufView[3] ^ bufView[4];

for (let i = 0; i < data.length; i++) {
bufView[i + 5] = data[i];
const bufferSize = dataLength + 6;
let bufferOut = new ArrayBuffer(bufferSize);
let bufView = new Uint8Array(bufferOut);

checksum ^= bufView[i + 5];
}
bufView[0] = 36; // $
bufView[1] = 77; // M
bufView[2] = 60; // <
bufView[3] = dataLength;
bufView[4] = code;

bufView[5 + data.length] = checksum;
} else {
bufferOut = new ArrayBuffer(6);
let bufView = new Uint8Array(bufferOut);
let checksum = bufView[3] ^ bufView[4];

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
for (let i = 0; i < dataLength; i++) {
bufView[i + 5] = data[i];
checksum ^= bufView[i + 5];
}

bufView[5 + dataLength] = checksum;
return bufferOut;
},
encode_message_v2: function (code, data) {
Expand Down
26 changes: 3 additions & 23 deletions src/js/msp/MSPHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -787,35 +787,15 @@ MspHelper.prototype.process_data = function(dataHandler) {
}

FC.CONFIG.boardVersion = data.readU16();
FC.CONFIG.boardType = 0;

FC.CONFIG.boardType = data.readU8();

FC.CONFIG.targetCapabilities = 0;
FC.CONFIG.targetName = '';

FC.CONFIG.targetCapabilities = data.readU8();
let length = data.readU8();
for (let i = 0; i < length; i++) {
FC.CONFIG.targetName += String.fromCharCode(data.readU8());
}
FC.CONFIG.targetName = this.getText(data);

FC.CONFIG.boardName = '';
FC.CONFIG.manufacturerId = '';
FC.CONFIG.boardName = this.getText(data);
FC.CONFIG.manufacturerId = this.getText(data);
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++) {
FC.CONFIG.signature.push(data.readU8());
}
Expand Down
165 changes: 165 additions & 0 deletions test/js/msp/MSPHelper.test.js
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit 1657351

Please sign in to comment.