particle-cli
Version:
Simple Node commandline application for working with your Particle devices and using the Particle Cloud
297 lines (230 loc) • 8.41 kB
JavaScript
const BinaryCommand = require('../cmd/binary');
const { expect } = require('../../test/setup');
const path = require('path');
const fs = require('fs-extra');
const { PATH_FIXTURES_THIRDPARTY_OTA_DIR, PATH_FIXTURES_BINARIES_DIR } = require('../../test/lib/env');
describe('Binary Inspect', () => {
let binaryCommand;
beforeEach(async () => {
binaryCommand = new BinaryCommand();
});
describe('createProtectedBinary', () => {
it('creates a protected binary', async () => {
const filename = path.join(PATH_FIXTURES_BINARIES_DIR, 'argon-bootloader-610.bin');
const saveTo = 'argon_stroby-protected.bin';
let error;
let resPath;
try {
resPath = await binaryCommand.createProtectedBinary({ saveTo, file: filename, verbose: false });
} catch (err) {
error = err;
}
expect(error).to.equal(undefined);
expect(resPath).to.equal(path.join(path.dirname(filename), saveTo));
await fs.remove(resPath);
});
});
describe('__checkFile', () => {
it('errors if file does not exist', async () => {
let error;
try {
await binaryCommand._checkFile('does-not-exist.bin');
} catch (_error) {
error = _error;
}
expect(error).to.be.an.instanceof(Error);
expect(error.message).to.equal('File does not exist: does-not-exist.bin');
});
it('returns nothing if file exists', async () => {
let res = false;
try {
res = await binaryCommand._checkFile(path.join(PATH_FIXTURES_BINARIES_DIR, 'argon_stroby.bin'));
} catch (err) {
// ignore error
}
expect(res).to.equal(true);
});
});
describe('_extractApplicationFiles', () => {
it('errors if file is not .zip or .bin', async () => {
let error;
try {
await binaryCommand._extractApplicationFiles('not-a-zip-or-bin-file');
} catch (_error) {
error = _error;
}
expect(error).to.be.an.instanceof(Error);
expect(error.message).to.equal('File must be a .bin or .zip file: not-a-zip-or-bin-file');
});
it('extracts a .zip file', async () => {
const zipPath = path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'bundle.zip');
const binaryInfo = await binaryCommand._extractApplicationFiles(zipPath);
expect(binaryInfo).to.have.property('application').with.property('name', 'app.bin');
expect(binaryInfo).to.have.property('assets').with.lengthOf(3);
expect(binaryInfo.assets.map(a => a.name)).to.eql(['cat.txt', 'house.txt', 'water.txt']);
});
xit('errors out if the .zip file does not contain a .bin', async () => {
// TODO
});
it('extracts a .bin file', async () => {
const binPath = path.join(PATH_FIXTURES_BINARIES_DIR, 'argon_stroby.bin');
const binaryInfo = await binaryCommand._extractApplicationFiles(binPath);
expect(binaryInfo).to.have.property('application').with.property('name', 'argon_stroby.bin');
expect(binaryInfo).to.have.property('assets').with.lengthOf(0);
});
it('handles if zip file does not have a binary or assets', async () => {
const zipPath = path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'invalid-bundle.zip');
const binaryInfo = await binaryCommand._extractApplicationFiles(zipPath);
expect(binaryInfo).to.have.property('application').with.property('name', 'app.txt');
expect(binaryInfo).to.have.property('assets').with.lengthOf(0);
});
});
describe('_parseBinary', () => {
it('parses a .bin file', async () => {
const name = 'argon_stroby.bin';
const data = await fs.readFile(path.join(PATH_FIXTURES_BINARIES_DIR, name));
const applicationBinary = { name, data };
const res = await binaryCommand._parseBinary(applicationBinary);
expect(path.basename(res.filename)).to.equal('argon_stroby.bin');
expect(res.crc.ok).to.equal(true);
expect(res).to.have.property('prefixInfo');
expect(res).to.have.property('suffixInfo');
});
it('errors if the binary is not valid', async () => {
const binary = { name: 'junk', data: Buffer.from('junk') };
let error;
try {
await binaryCommand._parseBinary(binary);
} catch (_error) {
error = _error;
}
expect(error).to.be.an.instanceof(Error);
expect(error.message).to.match(/Could not parse junk/);
});
});
describe('_verifyBundle', () => {
it('verifies bundle with asset info', async () => {
const zipPath = path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'bundle.zip');
const res = await binaryCommand._extractApplicationFiles(zipPath);
const parsedBinaryInfo = await binaryCommand._parseBinary(res.application);
const verify = await binaryCommand._verifyBundle(parsedBinaryInfo, res.assets);
expect(verify).to.equal(true);
});
});
describe('_validateProtectedBinary', () => {
it('validates a protected binary', async () => {
const module = {
prefixInfo: {
moduleIndex: 0,
moduleFunction: 2,
moduleVersion: 3000
}
};
let error;
try {
binaryCommand._validateProtectedBinary(module);
} catch (e) {
error = e;
}
expect(error).to.equal(undefined);
});
it('errors if binary is of the wrong module index', async () => {
const module = {
prefixInfo: {
moduleIndex: 1,
moduleFunction: 2,
moduleVersion: 3000
}
};
let error;
try {
binaryCommand._validateProtectedBinary(module);
} catch (e) {
error = e;
}
expect(error.message).to.equal('Device protection feature is not supported for this binary.');
});
it('errors if binary is not a bootloader', async () => {
const module = {
prefixInfo: {
moduleIndex: 1,
moduleFunction: 0,
moduleVersion: 3000
}
};
let error;
try {
binaryCommand._validateProtectedBinary(module);
} catch (e) {
error = e;
}
expect(error.message).to.equal('Device protection feature is not supported for this binary.');
});
it('errors if binary is of an older bootloader version', async () => {
const module = {
prefixInfo: {
moduleIndex: 1,
moduleFunction: 0,
moduleVersion: 2000
}
};
let error;
try {
binaryCommand._validateProtectedBinary(module);
} catch (e) {
error = e;
}
expect(error.message).to.equal('Device protection feature is not supported for this binary.');
});
});
describe('listAssetsFromApplication', () => {
it('lists assets from a bundle', async () => {
const zipPath = path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'bundle.zip');
const assets = await binaryCommand.listAssetsFromApplication(zipPath);
expect(assets).to.have.lengthOf(3);
expect(assets.map(a => a.name)).to.eql(['cat.txt', 'house.txt', 'water.txt']);
});
it('lists assets from an application binary', async () => {
const binPath = path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'app-with-assets.bin');
const assets = await binaryCommand.listAssetsFromApplication(binPath);
expect(assets).to.have.lengthOf(3);
expect(assets.map(a => a.name)).to.eql(['cat.txt', 'house.txt', 'water.txt']);
});
it('lists assets from a binary which does not have assets', async () => {
const binPath = path.join(PATH_FIXTURES_BINARIES_DIR, 'argon_stroby.bin');
let error;
try {
await binaryCommand.listAssetsFromApplication(binPath);
} catch (e) {
error = e;
}
expect(error).to.be.an.instanceof(Error);
expect(error.message).to.equal('No assets found');
});
});
describe('stripAssetsFromApplication', () => {
it('strips assets from a binary', async () => {
const binPath = path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'app-with-assets.bin');
const res = await binaryCommand.stripAssetsFromApplication(binPath);
expect(res).to.equal(path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'app-with-assets-no-assets.bin'));
await fs.remove(res);
});
it('strips assets from a bundle', async () => {
const zipPath = path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'bundle.zip');
const res = await binaryCommand.stripAssetsFromApplication(zipPath);
expect(res).to.equal(path.join(PATH_FIXTURES_THIRDPARTY_OTA_DIR, 'bundle-no-assets.bin'));
await fs.remove(res);
});
it('errors if binary has no assets', async () => {
const binPath = path.join(PATH_FIXTURES_BINARIES_DIR, 'argon_stroby.bin');
let error;
try {
await binaryCommand.stripAssetsFromApplication(binPath);
} catch (e) {
error = e;
}
expect(error).to.be.an.instanceof(Error);
expect(error.message).to.equal('No assets found');
});
});
});