balena-supervisor/test/integration/config/extra-uenv.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

242 lines
7.6 KiB
TypeScript

import { stripIndent } from 'common-tags';
import { expect } from 'chai';
import { testfs } from 'mocha-pod';
import { promises as fs } from 'fs';
import * as hostUtils from '~/lib/host-utils';
import log from '~/lib/supervisor-console';
import { ExtraUEnv } from '~/src/config/backends/extra-uEnv';
describe('config/extra-uEnv', () => {
const backend = new ExtraUEnv();
it('should parse extra_uEnv string', () => {
const fileContents = stripIndent`\
custom_fdt_file=mycustom.dtb
extra_os_cmdline=isolcpus=3,4 splash console=tty0
`;
// @ts-expect-error accessing private method
const parsed = ExtraUEnv.parseOptions(fileContents);
expect(parsed).to.deep.equal({
fdt: 'mycustom.dtb',
isolcpus: '3,4',
splash: '',
console: 'tty0',
});
});
it('should only parse supported configuration options from bootConfigPath', async () => {
let tfs = await testfs({
[hostUtils.pathOnBoot(`extra_uEnv.txt`)]: stripIndent`
custom_fdt_file=mycustom.dtb
extra_os_cmdline=isolcpus=3,4
`,
}).enable();
await expect(backend.getBootConfig()).to.eventually.deep.equal({
fdt: 'mycustom.dtb',
isolcpus: '3,4',
});
await tfs.restore();
// Add other options that will get filtered out because they aren't supported
tfs = await testfs({
[hostUtils.pathOnBoot(`extra_uEnv.txt`)]: stripIndent`
custom_fdt_file=mycustom.dtb
extra_os_cmdline=isolcpus=3,4 console=tty0 splash
`,
}).enable();
await expect(backend.getBootConfig()).to.eventually.deep.equal({
fdt: 'mycustom.dtb',
isolcpus: '3,4',
});
await tfs.restore();
// Configuration with no supported values
tfs = await testfs({
[hostUtils.pathOnBoot(`extra_uEnv.txt`)]: stripIndent`
fdt=something_else
isolcpus
123.12=5
`,
}).enable();
await expect(backend.getBootConfig()).to.eventually.deep.equal({});
await tfs.restore();
});
it('only matches supported devices', async () => {
// The file exists before
const tfs = await testfs({
[hostUtils.pathOnBoot(`extra_uEnv.txt`)]: stripIndent`
custom_fdt_file=mycustom.dtb
extra_os_cmdline=isolcpus=3,4
`,
}).enable();
for (const device of MATCH_TESTS) {
// Test device that has extra_uEnv.txt
await expect(backend.matches(device.type)).to.eventually.equal(
device.supported,
);
}
await tfs.restore();
// The file no longer exists
await expect(
fs.access(hostUtils.pathOnBoot(`extra_uEnv.txt`)),
'extra_uEnv.txt does not exist before the test',
).to.be.rejected;
for (const device of MATCH_TESTS) {
// Test same device but without extra_uEnv.txt
await expect(backend.matches(device.type)).to.eventually.be.false;
}
});
it('errors when cannot find extra_uEnv.txt', async () => {
// The file no longer exists
await expect(
fs.access(hostUtils.pathOnBoot(`extra_uEnv.txt`)),
'extra_uEnv.txt does not exist before the test',
).to.be.rejected;
await expect(backend.getBootConfig()).to.eventually.be.rejectedWith(
'Could not find extra_uEnv file. Device is possibly bricked',
);
});
it('logs warning for malformed extra_uEnv.txt', async () => {
for (const badConfig of MALFORMED_CONFIGS) {
// Setup the environment with a bad config
const tfs = await testfs({
[hostUtils.pathOnBoot(`extra_uEnv.txt`)]: badConfig.contents,
}).enable();
// Expect warning log from the given bad config
await backend.getBootConfig();
expect(log.warn).to.have.been.calledWith(badConfig.reason);
await tfs.restore();
}
});
it('sets new config values', async () => {
const tfs = await testfs({
// This config contains a value set from something else
// We to make sure the Supervisor is enforcing the source of truth (the cloud)
// So after setting new values this unsupported/not set value should be gone
[hostUtils.pathOnBoot(`extra_uEnv.txt`)]: stripIndent`
extra_os_cmdline=rootwait isolcpus=3,4
other_service=set_this_value
`,
}).enable();
// Sets config with mix of supported and not supported values
await backend.setBootConfig({
fdt: '/boot/mycustomdtb.dtb',
isolcpus: '2',
console: 'tty0', // not supported so won't be set
});
// Confirm that the file was written correctly
await expect(
fs.readFile(hostUtils.pathOnBoot(`extra_uEnv.txt`), 'utf8'),
).to.eventually.equal(
'custom_fdt_file=/boot/mycustomdtb.dtb\nextra_os_cmdline=isolcpus=2\n',
);
expect(log.warn).to.have.been.calledWith(
'Not setting unsupported value: { console: tty0 }',
);
await tfs.restore();
});
it('sets new config values containing collections', async () => {
// @ts-expect-error accessing private value
const previousSupportedConfigs = ExtraUEnv.supportedConfigs;
// Stub isSupportedConfig so we can confirm collections work
// @ts-expect-error accessing private value
ExtraUEnv.supportedConfigs = {
fdt: { key: 'custom_fdt_file', collection: false },
isolcpus: { key: 'extra_os_cmdline', collection: true },
console: { key: 'extra_os_cmdline', collection: true },
splash: { key: 'extra_os_cmdline', collection: true },
};
const tfs = await testfs({
[hostUtils.pathOnBoot(`extra_uEnv.txt`)]: stripIndent`
other_service=set_this_value
`,
}).enable();
// Set config again
await backend.setBootConfig({
fdt: '/boot/mycustomdtb.dtb',
isolcpus: '2', // collection entry so should be concatted to other collections of this entry
console: 'tty0', // collection entry so should be concatted to other collections of this entry
splash: '', // collection entry so should be concatted to other collections of this entry
});
await expect(
fs.readFile(hostUtils.pathOnBoot(`extra_uEnv.txt`), 'utf8'),
).to.eventually.equal(
'custom_fdt_file=/boot/mycustomdtb.dtb\nextra_os_cmdline=isolcpus=2 console=tty0 splash\n',
);
// @ts-expect-error accessing private value
ExtraUEnv.supportedConfigs = previousSupportedConfigs;
await tfs.restore();
});
});
const MALFORMED_CONFIGS = [
{
contents: stripIndent`
custom_fdt_file=mycustom.dtb
extra_os_cmdline=isolcpus=3,4
another_value
`,
reason: 'Could not read extra_uEnv entry: another_value',
},
];
const MATCH_TESTS = [
{ type: 'astro-tx2', supported: true },
{ type: 'blackboard-tx2', supported: true },
{ type: 'jetson-tx2', supported: true },
{ type: 'n310-tx2', supported: true },
{ type: 'n510-tx2', supported: true },
{ type: 'orbitty-tx2', supported: true },
{ type: 'spacely-tx2', supported: true },
{ type: 'srd3-tx2', supported: true },
{ type: 'jetson-nano', supported: true },
{ type: 'jetson-nano-emmc', supported: true },
{ type: 'jn30b-nano', supported: true },
{ type: 'photon-nano', supported: true },
{ type: 'intel-nuc', supported: false },
{ type: 'raspberry', supported: false },
{ type: 'fincm3', supported: false },
{ type: 'asus-tinker-board', supported: false },
{ type: 'nano-board', supported: false },
{ type: 'jetson-nano-2gb-devkit', supported: true },
{ type: 'jetson-nano-2gb-devkit-emmc', supported: false },
{ type: 'tx2-tx2-device', supported: false },
{ type: 'jetson-tx2-nx-devkit', supported: true },
{ type: 'photon-tx2-nx', supported: true },
{ type: 'jetson-xavier-nx-devkit', supported: false },
{ type: 'jetson-agx-orin-devkit', supported: true },
{ type: 'jetson-agx-orin', supported: false },
{ type: 'photon-xavier-nx', supported: false },
{ type: 'imx8m-var-dart', supported: true },
{ type: 'imx8mm-var-dart', supported: true },
{ type: 'imx8mm-var-dart-nrt', supported: true },
{ type: 'imx8mm-var-dart-plt', supported: true },
{ type: 'imx8mm-var-som', supported: true },
{ type: 'imx8m-var-som', supported: false },
{ type: 'imx6ul-var-dart', supported: false },
];