balena-supervisor/test/integration/config/extlinux.spec.ts
pipex 827f892c13 Migrate all device config tests to integration.
This means that configuration backend tests no longer use stubs and
(mostly) avoid internal dependencies in the tests. Instead of stubs and
mock-fs, the tests use [testfs](https://github.com/balena-io-modules/mocha-pod#working-with-the-filesystem)
which allows working with a real filesystem and ensuring everything is
re-set between tests.

This is the last change needed in order to be able to merge #1971. Here is the list of changes

- [x] Migrate splash image backend tests
- [x] Migrate extlinux backend tests
- [x] Migrate config.txt backend tests
- [x] Migrate extra-uenv config tests
- [x] Migrate odmdata config tests
- [x] Migrate config utils tests
- [x] Migrate device-config tests

Change-type: patch
2022-11-14 11:12:52 -03:00

333 lines
8.6 KiB
TypeScript

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,
},
];