balena-supervisor/test/integration/config/extlinux.spec.ts

333 lines
8.6 KiB
TypeScript
Raw Normal View History

import { promises as fs } from 'fs';
import { stripIndent } from 'common-tags';
import { expect } from 'chai';
import { testfs } from 'mocha-pod';
import * as hostUtils from '~/lib/host-utils';
import { Extlinux } from '~/src/config/backends/extlinux';
describe('config/extlinux', () => {
it('should correctly parse an extlinux.conf file', async () => {
const tfs = await testfs({
[hostUtils.pathOnBoot('extlinux/extlinux.conf')]: stripIndent`
DEFAULT primary
# CommentExtlinux files
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
MENU LABEL primary Image
LINUX /Image
FDT /boot/mycustomdtb.dtb
APPEND \${cbootargs} \${resin_kernel_root} ro rootwait isolcpus=3\
`,
}).enable();
const extLinux = new Extlinux();
await expect(extLinux.getBootConfig()).to.eventually.deep.equal({
fdt: '/boot/mycustomdtb.dtb',
isolcpus: '3',
});
await tfs.restore();
});
it('should parse multiple service entries', async () => {
const tfs = await testfs({
[hostUtils.pathOnBoot('extlinux/extlinux.conf')]: stripIndent`
DEFAULT primary
# Comment
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
LINUX test1
FDT /boot/mycustomdtb.dtb
APPEND test2
LABEL secondary
LINUX test3
FDT /boot/mycustomdtb.dtb
APPEND test4\
`,
}).enable();
const extLinux = new Extlinux();
await expect(extLinux.getBootConfig()).to.eventually.deep.equal({
fdt: '/boot/mycustomdtb.dtb',
});
await tfs.restore();
});
it('only matches supported devices', async () => {
const extLinux = new Extlinux();
for (const { deviceType, metaRelease, supported } of MATCH_TESTS) {
await expect(
extLinux.matches(deviceType, metaRelease),
).to.eventually.equal(supported);
}
});
it('errors when cannot find extlinux.conf', async () => {
// The file does not exist before the test
await expect(fs.access(hostUtils.pathOnBoot('extlinux/extlinux.conf'))).to
.be.rejected;
const extLinux = new Extlinux();
// Stub readFile to reject much like if the file didn't exist
await expect(extLinux.getBootConfig()).to.eventually.be.rejectedWith(
'Could not find extlinux file. Device is possibly bricked',
);
});
it('throws error for malformed extlinux.conf', async () => {
for (const badConfig of MALFORMED_CONFIGS) {
const tfs = await testfs({
[hostUtils.pathOnBoot('extlinux/extlinux.conf')]: badConfig,
}).enable();
const extLinux = new Extlinux();
// Expect correct rejection from the given bad config
await expect(extLinux.getBootConfig()).to.be.rejectedWith(
badConfig.reason,
);
await tfs.restore();
}
});
it('sets new config values', async () => {
const tfs = await testfs({
[hostUtils.pathOnBoot('extlinux/extlinux.conf')]: stripIndent`
DEFAULT primary
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
MENU LABEL primary Image
LINUX /Image
APPEND \${cbootargs} \${resin_kernel_root} ro rootwait\
`,
}).enable();
const extLinux = new Extlinux();
await extLinux.setBootConfig({
fdt: '/boot/mycustomdtb.dtb',
isolcpus: '2',
});
await expect(
fs.readFile(hostUtils.pathOnBoot('extlinux/extlinux.conf'), 'utf8'),
).to.eventually.equal(
stripIndent`
DEFAULT primary
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
MENU LABEL primary Image
LINUX /Image
APPEND \${cbootargs} \${resin_kernel_root} ro rootwait isolcpus=2
FDT /boot/mycustomdtb.dtb
` + '\n', // add newline because stripIndent trims last newline
);
await tfs.restore();
});
it('only allows supported configuration options', () => {
const extLinux = new Extlinux();
[
{ configName: 'isolcpus', supported: true },
{ configName: 'fdt', supported: true },
{ configName: '', supported: false },
{ configName: 'ro', supported: false }, // not allowed to configure
{ configName: 'rootwait', supported: false }, // not allowed to configure
].forEach(({ configName, supported }) =>
expect(extLinux.isSupportedConfig(configName)).to.equal(supported),
);
});
it('correctly detects boot config variables', () => {
const extLinux = new Extlinux();
[
{ config: 'HOST_EXTLINUX_isolcpus', valid: true },
{ config: 'HOST_EXTLINUX_fdt', valid: true },
{ config: 'HOST_EXTLINUX_rootwait', valid: true },
{ config: 'HOST_EXTLINUX_5', valid: true },
// TODO: { config: 'HOST_EXTLINUX', valid: false },
// TODO: { config: 'HOST_EXTLINUX_', valid: false },
{ config: 'DEVICE_EXTLINUX_isolcpus', valid: false },
{ config: 'isolcpus', valid: false },
].forEach(({ config, valid }) =>
expect(extLinux.isBootConfigVar(config)).to.equal(valid),
);
});
it('converts variable to backend formatted name', () => {
const extLinux = new Extlinux();
[
{ input: 'HOST_EXTLINUX_isolcpus', output: 'isolcpus' },
{ input: 'HOST_EXTLINUX_fdt', output: 'fdt' },
{ input: 'HOST_EXTLINUX_rootwait', output: 'rootwait' },
{ input: 'HOST_EXTLINUX_something_else', output: 'something_else' },
{ input: 'HOST_EXTLINUX_', output: 'HOST_EXTLINUX_' },
{ input: 'HOST_EXTLINUX_ ', output: ' ' },
{ input: 'ROOT_EXTLINUX_isolcpus', output: 'ROOT_EXTLINUX_isolcpus' },
].forEach(({ input, output }) =>
expect(extLinux.processConfigVarName(input)).to.equal(output),
);
});
it('normalizes variable value', () => {
const extLinux = new Extlinux();
[{ input: { key: 'key', value: 'value' }, output: 'value' }].forEach(
({ input, output }) =>
expect(extLinux.processConfigVarValue(input.key, input.value)).to.equal(
output,
),
);
});
it('returns the environment name for config variable', () => {
const extLinux = new Extlinux();
[
{ input: 'isolcpus', output: 'HOST_EXTLINUX_isolcpus' },
{ input: 'fdt', output: 'HOST_EXTLINUX_fdt' },
{ input: 'rootwait', output: 'HOST_EXTLINUX_rootwait' },
{ input: '', output: 'HOST_EXTLINUX_' },
{ input: '5', output: 'HOST_EXTLINUX_5' },
].forEach(({ input, output }) =>
expect(extLinux.createConfigVarName(input)).to.equal(output),
);
});
});
const MALFORMED_CONFIGS = [
{
contents: stripIndent`
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
MENU LABEL primary Image
LINUX /Image
APPEND ro rootwait isolcpus=0,4
`,
reason: 'Could not find default entry for extlinux.conf file',
},
{
contents: stripIndent`
DEFAULT typo_oops
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
MENU LABEL primary Image
LINUX /Image
APPEND ro rootwait isolcpus=0,4
`,
reason: 'Cannot find label entry (label: typo_oops) for extlinux.conf file',
},
{
contents: stripIndent`
DEFAULT primary
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
MENU LABEL primary Image
LINUX /Image
`,
reason:
'Could not find APPEND directive in default extlinux.conf boot entry',
},
{
contents: stripIndent`
DEFAULT primary
TIMEOUT 30
MENU TITLE Boot Options
LABEL primary
MENU LABEL primary Image
LINUX /Image
APPEND ro rootwait isolcpus=0,4=woops
`,
reason: 'Unable to parse invalid value: isolcpus=0,4=woops',
},
];
const SUPPORTED_VERSION = '2.45.0'; // or less
const UNSUPPORTED_VERSION = '2.47.0'; // or greater
const MATCH_TESTS = [
{
deviceType: 'jetson-tx1',
metaRelease: SUPPORTED_VERSION,
supported: true,
},
{
deviceType: 'jetson-tx2',
metaRelease: SUPPORTED_VERSION,
supported: true,
},
{
deviceType: 'jetson-tx2',
metaRelease: UNSUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'jetson-nano',
metaRelease: SUPPORTED_VERSION,
supported: true,
},
{
deviceType: 'jetson-nano',
metaRelease: UNSUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'jetson-xavier',
metaRelease: SUPPORTED_VERSION,
supported: true,
},
{
deviceType: 'jetson-xavier',
metaRelease: UNSUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'intel-nuc',
metaRelease: SUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'intel-nuc',
metaRelease: UNSUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'raspberry',
metaRelease: SUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'raspberry',
metaRelease: UNSUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'fincm3',
metaRelease: SUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'fincm3',
metaRelease: UNSUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'up-board',
metaRelease: SUPPORTED_VERSION,
supported: false,
},
{
deviceType: 'up-board',
metaRelease: UNSUPPORTED_VERSION,
supported: false,
},
];