Skip to content

Commit

Permalink
Add more device tests
Browse files Browse the repository at this point in the history
  • Loading branch information
robmosca committed Dec 13, 2023
1 parent 40f2484 commit 1ba5449
Show file tree
Hide file tree
Showing 3 changed files with 211 additions and 36 deletions.
16 changes: 9 additions & 7 deletions src/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ class Device extends EventEmitter {
transferid,
});
}
this.refreshStorageStatus();
return this.refreshStorageStatus();
}

async refreshStorageStatus() {
Expand Down Expand Up @@ -239,10 +239,12 @@ export async function disconnectDevice() {
device = undefined;
}

export function execMethodOnDeviceSlot(method: string, slotId: number) {
if (device) {
(device as { [method: string]: any })[method](slotId);
}
export async function runProgramOnDevice(slotId: number) {
return device?.runProgram(slotId);
}

export async function removeProgramFromDevice(slotId: number) {
return device?.removeProgram(slotId);
}

export function stopProgramOnDevice() {
Expand All @@ -253,12 +255,12 @@ export function moveProgramOnDevice(fromSlotId: number, toSlotId: number) {
device?.moveProgram(fromSlotId, toSlotId);
}

export function uploadProgramOnDevice(
export function uploadProgramToDevice(
prgName: string,
prgText: string,
slotId: number,
) {
device?.uploadProgram(prgName, prgText, slotId);
return device?.uploadProgram(prgName, prgText, slotId);
}

export function getDeviceInfo() {
Expand Down
16 changes: 12 additions & 4 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ import {
SlotType,
connectDevice,
disconnectDevice,
execMethodOnDeviceSlot,
getDeviceSlots,
isDeviceConnected,
moveProgramOnDevice,
runProgramOnDevice,
removeProgramFromDevice,
stopProgramOnDevice,
uploadProgramOnDevice,
uploadProgramToDevice,
} from './device';
import { existsSync } from 'fs';
import { SerialPort } from 'serialport';
Expand Down Expand Up @@ -183,7 +184,14 @@ async function askSlotAndExecuteMethod(
`Executing ${functionName} ${slot.tooltip} (Slot ${slot.index})...`,
);

execMethodOnDeviceSlot(functionName, slot.index);
switch (functionName) {
case 'runProgram':
return runProgramOnDevice(slot.index);
case 'removeProgram':
return removeProgramFromDevice(slot.index);
default:
return;
}
}

async function runProgram(slot: ProgramSlotTreeItem) {
Expand Down Expand Up @@ -282,5 +290,5 @@ async function uploadProgram(slot?: ProgramSlotTreeItem) {

const prgName = basename(filename, '.py');
const prgText = editor!.document.getText();
return uploadProgramOnDevice(prgName, prgText, slot.index);
return uploadProgramToDevice(prgName, prgText, slot.index);
}
215 changes: 190 additions & 25 deletions src/test/device.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import {
_testing,
isDeviceConnected,
disconnectDevice,
runProgramOnDevice,
removeProgramFromDevice,
uploadProgramToDevice,
getDeviceSlots,
} from '../device';
import { SerialPortMock } from 'serialport';
import * as chai from 'chai';
Expand All @@ -16,6 +20,26 @@ const expect = chai.expect;
describe('Device', () => {
const deviceName = '/dev/mock';
const APIRequestStub = sinon.stub(api, 'APIRequest');
const defaultSlots = {
1: {
name: 'Program 1',
id: 1,
project_id: 'asi3',
modified: new Date(),
type: 'python',
created: new Date(),
size: 123,
},
2: {
name: 'Program 2',
id: 2,
project_id: 'erhw',
modified: new Date(),
type: 'python',
created: new Date(),
size: 321,
},
};

beforeEach(() => {
_testing.SerialPortType = SerialPortMock;
Expand All @@ -40,37 +64,20 @@ describe('Device', () => {
});
};

const stubConnect = () => {
APIRequestStub.resolves({
const getStorageInfo = (additionalSlots?: any) => {
return {
storage: { total: 100, used: 50, available: 50 },
slots: {
1: {
name: 'Program 1',
id: 1,
project_id: 'asi3',
modified: new Date(),
type: 'python',
created: new Date(),
size: 123,
},
2: {
name: 'Program 2',
id: 2,
project_id: 'erhw',
modified: new Date(),
type: 'python',
created: new Date(),
size: 321,
},
...defaultSlots,
...additionalSlots,
},
});
};
};

describe('connectDevice', function () {
it('should connect to the device and refresh storage status', async function () {
// Arrange
createPortMock('');
stubConnect();
APIRequestStub.resolves(getStorageInfo());

await connectDevice(deviceName);

Expand All @@ -85,7 +92,6 @@ describe('Device', () => {
});

it('should throw an error if the device is already connected', async function () {
// Arrange
createPortMock('');

await connectDevice(deviceName);
Expand Down Expand Up @@ -133,7 +139,7 @@ describe('Device', () => {
describe('disconnectDevice', function () {
it('should disconnect the device', async function () {
createPortMock('');
stubConnect();
APIRequestStub.resolves(getStorageInfo());

await connectDevice(deviceName);

Expand All @@ -150,4 +156,163 @@ describe('Device', () => {
expect(isDeviceConnected()).to.be.false;
});
});

describe('runProgramOnDevice', function () {
it('should run a program on a device slot', async function () {
createPortMock('');
APIRequestStub.resolves(getStorageInfo());

await connectDevice(deviceName);

APIRequestStub.reset();
APIRequestStub.resolves({});

runProgramOnDevice(12);

expect(APIRequestStub).to.have.been.calledOnce;
expect(APIRequestStub).to.have.been.calledWith(
sinon.match.any,
'program_execute',
{ slotid: 12 },
);
});

it('should throw an error if the slot is not valid', async function () {
createPortMock('');
APIRequestStub.resolves(getStorageInfo());

await connectDevice(deviceName);

APIRequestStub.reset();
APIRequestStub.resolves({});

let error: Error | undefined = undefined;
try {
await runProgramOnDevice(24);
} catch (err) {
expect(err).instanceOf(Error);
error = err as Error;
}
expect(error?.message).to.be.equal(
'Invalid program slot index 24, valid slots: 0-19',
);
});
});

describe('removeProgramFromDevice', function () {
it('should remove a program from a device slot', async function () {
createPortMock('');
APIRequestStub.resolves(getStorageInfo());

await connectDevice(deviceName);

APIRequestStub.reset();
APIRequestStub.resolves({});

removeProgramFromDevice(12);

expect(APIRequestStub).to.have.been.calledOnce;
expect(APIRequestStub).to.have.been.calledWith(
sinon.match.any,
'remove_project',
{ slotid: 12 },
);
});

it('should throw an error if the slot is not valid', async function () {
createPortMock('');
APIRequestStub.resolves(getStorageInfo());

await connectDevice(deviceName);

APIRequestStub.reset();
APIRequestStub.resolves({});

let error: Error | undefined = undefined;
try {
await removeProgramFromDevice(24);
} catch (err) {
expect(err).instanceOf(Error);
error = err as Error;
}
expect(error?.message).to.be.equal(
'Invalid program slot index 24, valid slots: 0-19',
);
});
});

describe('uploadProgramToDevice', function () {
it('should upload a program to a device slot', async function () {
const additionalSlots = {
3: {
name: 'Program 3',
id: 3,
project_id: '2g3e',
modified: new Date(),
type: 'python',
created: new Date(),
size: 321,
},
};
createPortMock('');
APIRequestStub.resolves(getStorageInfo());

await connectDevice(deviceName);

APIRequestStub.callsFake((port, method) => {
if (method === 'start_write_program') {
return Promise.resolve({ blocksize: 16, transferid: 'a123' });
} else if (method === 'get_storage_status') {
return Promise.resolve(getStorageInfo(additionalSlots));
} else {
return Promise.resolve({});
}
});
const prgText = `
print("Hello world")
print("This is a sample program")
print("It only prints stuff...")
print("...but it's a good start!")
`;
const size = prgText.length;

await uploadProgramToDevice('Program 3', prgText, 12);

expect(APIRequestStub.callCount).to.equal(14);
expect(APIRequestStub).to.have.been.calledWith(
sinon.match.any,
'start_write_program',
{
slotid: 12,
size,
meta: {
created: sinon.match.any,
modified: sinon.match.any,
name: 'UHJvZ3JhbSAz',
project_id: 'ScctlpwQVu64', // This seems to be mandatory for python files
type: 'python',
},
},
);
expect(APIRequestStub).to.have.been.calledWith(
sinon.match.any,
'write_package',
{
data: sinon.match.any,
transferid: 'a123',
},
);
expect(APIRequestStub).to.have.been.calledWith(
sinon.match.any,
'get_storage_status',
{},
);

const slots = await getDeviceSlots();
expect(slots).to.deep.equal({
...defaultSlots,
...additionalSlots,
});
});
});
});

0 comments on commit 1ba5449

Please sign in to comment.