Files
iptvnator/tools/packaging/verify-linux-frame-copy-runtime.test.mjs
T

1017 lines
32 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import {
createRuntimeProbeEnvironment,
detectLinuxArtifactFormat,
extractLinuxArtifact,
findAppImageSquashfsOffsets,
findExtractedResourceDir,
parseVerifierArguments,
readLinuxArtifactMetadata,
readElfArchitecture,
validateSystemPackageDependencies,
validateExtractedSnapMetadata,
verifyExtractedLinuxFrameCopyRuntime,
verifyLinuxFrameCopyArtifact,
} from './verify-linux-frame-copy-runtime.mjs';
const SYSTEM_TARGETS = ['deb', 'pacman', 'rpm'];
const SYSTEM_MANIFEST = {
schemaVersion: 1,
origin: 'system-libmpv-frame-copy',
generatedAt: '2026-07-17T00:00:00.000Z',
platform: 'linux',
arch: 'x64',
profile: 'system',
runtimeMode: 'system',
targets: SYSTEM_TARGETS,
artifacts: {
addon: {
name: 'embedded_mpv.node',
regularFile: true,
readable: true,
},
frameReader: {
name: 'embedded_mpv_frame_reader.node',
regularFile: true,
readable: true,
},
helper: {
name: 'iptvnator_mpv_helper',
regularFile: true,
readable: true,
executable: true,
},
},
processIsolation: {
addonLoadsLibmpv: false,
readerLoadsLibmpv: false,
electronLoadsLibmpv: false,
helperLinksLibmpv: true,
helperRunpath: ['$ORIGIN/lib'],
},
nativeViewFallback: 'process-isolated mpv --wid',
libmpvSoname: 'libmpv.so.2',
packageDependencies: {
deb: 'libmpv2',
rpm: 'mpv-libs',
pacman: 'mpv',
},
runtimeFiles: [],
runtimeTotalBytes: 0,
};
function elfHeader(architecture) {
const machines = {
x64: 62,
arm64: 183,
armv7l: 40,
};
const image = Buffer.alloc(64);
image.set([0x7f, 0x45, 0x4c, 0x46, 2, 1], 0);
image.writeUInt16LE(machines[architecture], 18);
return image;
}
function createSystemPayload({ architecture = 'x64' } = {}) {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-layout-')
);
const appDir = path.join(root, 'opt', 'IPTVnator');
const resourceDir = path.join(appDir, 'resources');
const nativeDir = path.join(
resourceDir,
'app.asar.unpacked',
'electron-backend',
'native'
);
fs.mkdirSync(nativeDir, { recursive: true });
fs.writeFileSync(
path.join(appDir, 'iptvnator.bin'),
elfHeader(architecture)
);
if (architecture === 'x64') {
fs.writeFileSync(path.join(nativeDir, 'embedded_mpv.node'), 'addon', {
mode: 0o644,
});
fs.writeFileSync(
path.join(nativeDir, 'embedded_mpv_frame_reader.node'),
'reader',
{ mode: 0o644 }
);
fs.writeFileSync(
path.join(nativeDir, 'iptvnator_mpv_helper'),
'helper',
{ mode: 0o755 }
);
fs.writeFileSync(
path.join(nativeDir, 'embedded-mpv-runtime.json'),
`${JSON.stringify(SYSTEM_MANIFEST, null, 2)}\n`,
{ mode: 0o644 }
);
} else {
fs.writeFileSync(
path.join(nativeDir, 'embedded-mpv-unavailable.txt'),
`Unavailable for ${architecture}\n`
);
}
return { root, appDir, resourceDir, nativeDir };
}
function validElfInspector(binaryPath) {
const name = path.basename(binaryPath);
if (name === 'iptvnator_mpv_helper') {
return {
soname: null,
needed: ['libc.so.6', 'libmpv.so.2'],
rpath: [],
runpath: ['$ORIGIN/lib'],
};
}
return {
soname: null,
needed: ['libc.so.6'],
rpath: [],
runpath: [],
};
}
function successfulProbeRunner() {
return {
status: 0,
signal: null,
stdout: '{"protocol":1,"usable":true,"libmpv":"0.41.0","renderApi":"egl"}\n',
stderr: '',
};
}
test('detects every supported Linux package payload format', () => {
assert.equal(detectLinuxArtifactFormat('IPTVnator.AppImage'), 'appimage');
assert.equal(detectLinuxArtifactFormat('IPTVnator.deb'), 'deb');
assert.equal(detectLinuxArtifactFormat('IPTVnator.rpm'), 'rpm');
assert.equal(detectLinuxArtifactFormat('IPTVnator.pacman'), 'pacman');
assert.equal(detectLinuxArtifactFormat('IPTVnator.pkg.tar.zst'), 'pacman');
assert.equal(detectLinuxArtifactFormat('IPTVnator.snap'), 'snap');
assert.equal(detectLinuxArtifactFormat('IPTVnator.flatpak'), 'flatpak');
assert.throws(
() => detectLinuxArtifactFormat('IPTVnator.tar.gz'),
/Unsupported Linux package artifact/
);
});
test('parses the required artifact and profile arguments without evaluating paths', () => {
const directory = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-args-')
);
const artifactPath = path.join(directory, 'package $(touch owned).deb');
fs.writeFileSync(artifactPath, 'fixture');
try {
assert.deepEqual(
parseVerifierArguments([
'--artifact',
artifactPath,
'--profile',
'system',
]),
{
artifactPath: path.resolve(artifactPath),
profileName: 'system',
}
);
assert.throws(
() => parseVerifierArguments(['--artifact', artifactPath]),
/--profile/
);
assert.throws(
() =>
parseVerifierArguments([
'--artifact',
artifactPath,
'--profile',
'standard',
]),
/Unsupported Linux frame-copy profile/
);
assert.throws(
() =>
parseVerifierArguments([
'--artifact',
artifactPath,
'--artifact',
artifactPath,
'--profile',
'system',
]),
/duplicate --artifact/
);
assert.throws(
() =>
parseVerifierArguments([
'--artifact',
artifactPath,
'--profile',
'system',
'--profile',
'system',
]),
/duplicate --profile/
);
} finally {
fs.rmSync(directory, { recursive: true, force: true });
}
});
test('extracts every payload format with argument arrays and no shell', () => {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-extract-')
);
const invocations = [];
const runCommand = (command, args, options = {}) => {
invocations.push({ command, args: [...args], cwd: options.cwd });
if (command === 'ostree' && args[0] === 'refs') {
return {
status: 0,
stdout: 'app/com.fourgray.iptvnator/x86_64/stable\n',
stderr: '',
};
}
return { status: 0, stdout: '', stderr: '' };
};
try {
for (const [format, fileName] of [
['appimage', 'IPTVnator.AppImage'],
['deb', 'IPTVnator.deb'],
['rpm', 'IPTVnator.rpm'],
['pacman', 'IPTVnator.pacman'],
['snap', 'IPTVnator.snap'],
['flatpak', 'IPTVnator.flatpak'],
]) {
const artifactPath = path.join(root, fileName);
const destination = path.join(root, `${format}-payload`);
fs.writeFileSync(
artifactPath,
format === 'appimage'
? Buffer.concat([Buffer.alloc(4096), Buffer.from('hsqs')])
: 'fixture'
);
fs.mkdirSync(destination);
extractLinuxArtifact({
artifactPath,
format,
destination,
runCommand,
});
}
assert.deepEqual(
invocations.map(({ command }) => command),
[
'unsquashfs',
'dpkg-deb',
'bsdtar',
'bsdtar',
'unsquashfs',
'flatpak',
'ostree',
'ostree',
]
);
assert.equal(
invocations.some(
({ command, args }) =>
['sh', 'bash'].includes(command) || args.includes('-c')
),
false
);
assert.deepEqual(invocations[1].args.slice(0, 2), [
'--extract',
path.join(root, 'IPTVnator.deb'),
]);
assert.deepEqual(invocations[2].args.slice(0, 2), [
'--extract',
'--file',
]);
assert.deepEqual(invocations[4].args.slice(0, 2), [
'-no-progress',
'-dest',
]);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('locates AppImage SquashFS payloads without executing a foreign-arch runtime', () => {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-appimage-')
);
const artifactPath = path.join(root, 'arm64.AppImage');
fs.writeFileSync(
artifactPath,
Buffer.concat([
Buffer.alloc(128, 0x41),
Buffer.from('hsqs'),
Buffer.alloc(64),
Buffer.from('hsqs'),
])
);
try {
assert.deepEqual(findAppImageSquashfsOffsets(artifactPath), [128, 196]);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('finds one packaged native payload under arbitrary format roots', () => {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-payload-')
);
const resourceDir = path.join(root, 'opt', 'IPTVnator', 'resources');
const nativeDir = path.join(
resourceDir,
'app.asar.unpacked',
'electron-backend',
'native'
);
fs.mkdirSync(nativeDir, { recursive: true });
try {
assert.equal(findExtractedResourceDir(root), resourceDir);
const duplicateNativeDir = path.join(
root,
'duplicate',
'resources',
'app.asar.unpacked',
'electron-backend',
'native'
);
fs.mkdirSync(duplicateNativeDir, { recursive: true });
assert.throws(
() => findExtractedResourceDir(root),
/exactly one embedded MPV native payload/
);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('reads x64, arm64, and armv7 ELF architectures without host execution', () => {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-elf-')
);
try {
for (const architecture of ['x64', 'arm64', 'armv7l']) {
const binaryPath = path.join(root, architecture);
fs.writeFileSync(binaryPath, elfHeader(architecture));
assert.equal(readElfArchitecture(binaryPath), architecture);
}
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('requires the exact system package dependency for DEB, RPM, and Pacman', () => {
assert.deepEqual(
validateSystemPackageDependencies('deb', [
'libmpv2 (>= 0.35)',
'libc6',
]),
[]
);
assert.deepEqual(
validateSystemPackageDependencies('rpm', ['mpv-libs', 'glibc']),
[]
);
assert.deepEqual(
validateSystemPackageDependencies('pacman', ['mpv>=0.35', 'glibc']),
[]
);
assert.match(
validateSystemPackageDependencies('deb', ['libmpv1'])[0],
/libmpv2/
);
});
test('reads DEB and RPM metadata without shell pipelines', () => {
const invocations = [];
const runCommand = (command, args) => {
invocations.push({ command, args: [...args] });
if (command === 'dpkg-deb' && args.at(-1) === 'Architecture') {
return { status: 0, stdout: 'amd64\n', stderr: '' };
}
if (command === 'dpkg-deb') {
return {
status: 0,
stdout: 'libmpv2 (>= 0.35), libc6\n',
stderr: '',
};
}
if (command === 'rpm' && args.includes('%{ARCH}\\n')) {
return { status: 0, stdout: 'x86_64\n', stderr: '' };
}
return {
status: 0,
stdout: 'mpv-libs\nglibc\n',
stderr: '',
};
};
assert.deepEqual(
readLinuxArtifactMetadata({
artifactPath: '/tmp/package name.deb',
format: 'deb',
extractionRoot: '/tmp/payload',
runCommand,
}),
{
declaredArch: 'x64',
dependencies: ['libmpv2 (>= 0.35)', 'libc6'],
}
);
assert.deepEqual(
readLinuxArtifactMetadata({
artifactPath: '/tmp/package name.rpm',
format: 'rpm',
extractionRoot: '/tmp/payload',
runCommand,
}),
{
declaredArch: 'x64',
dependencies: ['mpv-libs', 'glibc'],
}
);
assert.equal(
invocations.some(
({ command, args }) =>
['sh', 'bash'].includes(command) || args.includes('-c')
),
false
);
});
test('reads Pacman architecture and dependencies from the extracted .PKGINFO', () => {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-pacman-')
);
fs.writeFileSync(
path.join(root, '.PKGINFO'),
[
'pkgname = iptvnator',
'arch = x86_64',
'depend = mpv>=0.35',
'depend = glibc',
'',
].join('\n')
);
try {
assert.deepEqual(
readLinuxArtifactMetadata({
artifactPath: '/tmp/package.pacman',
format: 'pacman',
extractionRoot: root,
}),
{
declaredArch: 'x64',
dependencies: ['mpv>=0.35', 'glibc'],
}
);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('validates an x64 system payload and executes one bounded helper probe', () => {
const fixture = createSystemPayload();
const probeCalls = [];
try {
const errors = verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'deb',
profileName: 'system',
packageDependencies: ['libmpv2 (>= 0.35)'],
elfInspector: validElfInspector,
probeRunner(command, args, options) {
probeCalls.push({ command, args, options });
return successfulProbeRunner();
},
environment: {
PATH: '/usr/bin',
LD_LIBRARY_PATH: '/host/can-mask-missing-dependencies',
LD_PRELOAD: '/host/can-inject.so',
},
});
assert.deepEqual(errors, []);
assert.equal(probeCalls.length, 1);
assert.equal(
probeCalls[0].command,
path.join(fixture.nativeDir, 'iptvnator_mpv_helper')
);
assert.deepEqual(probeCalls[0].args, ['--runtime-probe']);
assert.deepEqual(probeCalls[0].options, {
encoding: 'utf8',
env: { PATH: '/usr/bin' },
killSignal: 'SIGKILL',
timeout: 3000,
windowsHide: true,
});
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
test('rejects helper probes terminated by a signal or hard timeout', () => {
for (const probeResult of [
{
status: null,
signal: 'SIGKILL',
stdout: '',
stderr: '',
},
{
error: Object.assign(new Error('spawnSync helper ETIMEDOUT'), {
code: 'ETIMEDOUT',
}),
status: null,
signal: 'SIGKILL',
stdout: '',
stderr: '',
},
]) {
const fixture = createSystemPayload();
try {
const errors = verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'deb',
profileName: 'system',
packageDependencies: ['libmpv2'],
elfInspector: validElfInspector,
probeRunner() {
return probeResult;
},
});
assert.match(
errors.join('\n'),
/(?:runtime probe terminated by signal SIGKILL|Unable to execute .*ETIMEDOUT)/
);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
}
});
test('requires a private top-level shared-memory plug used by the Snap app', () => {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-snap-metadata-')
);
const snapYamlPath = path.join(root, 'meta', 'snap.yaml');
fs.mkdirSync(path.dirname(snapYamlPath), { recursive: true });
const validSnapYaml = [
'name: iptvnator',
'apps:',
' iptvnator:',
' command: iptvnator',
' plugs:',
' - desktop',
' - shared-memory',
'plugs:',
' shared-memory:',
' interface: shared-memory',
' private: true',
'',
].join('\n');
try {
fs.writeFileSync(snapYamlPath, validSnapYaml);
assert.deepEqual(validateExtractedSnapMetadata(root), []);
for (const [mutate, expected] of [
[
(contents) =>
contents.replace(' private: true', ' private: false'),
/private: true/,
],
[
(contents) =>
contents.replace(' - shared-memory\n', ''),
/app.*shared-memory plug/i,
],
[
(contents) =>
contents.replace(
' shared-memory:\n interface: shared-memory\n private: true\n',
''
),
/top-level shared-memory plug/i,
],
[
(contents) =>
contents.replace(
' private: true\n',
' private: true\n shared-memory: named-area\n'
),
/private shared-memory plug.*exactly interface and private/i,
],
[
(contents) =>
contents.replace(
'\nplugs:\n',
[
'',
'plugs:',
' second-shared-memory:',
' interface: shared-memory',
' private: true',
'',
].join('\n')
),
/exactly one plug.*shared-memory interface/i,
],
[
(contents) =>
`${contents}slots:\n shared-memory:\n interface: shared-memory\n`,
/must not declare.*shared-memory slot/i,
],
[
(contents) =>
contents.replace(
'\nplugs:\n',
'\nplugs:\n other: { interface: shared-memory }\n'
),
/plug declarations.*block mappings/i,
],
[
(contents) =>
`${contents}slots: { leak: { interface: shared-memory } }\n`,
/slots.*block mappings/i,
],
[
(contents) =>
contents.replace(
'\nplugs:\n',
'\nplugs:\n network:\n interface: network\n network:\n interface: network\n'
),
/plug keys.*unique/i,
],
[
(contents) =>
contents.replace(
[
' plugs:',
' - desktop',
' - shared-memory',
].join('\n'),
[
' plugs:',
' nested:',
' - shared-memory',
].join('\n')
),
/scalar sequence.*shared-memory/i,
],
]) {
fs.writeFileSync(snapYamlPath, mutate(validSnapYaml));
assert.match(
validateExtractedSnapMetadata(root).join('\n'),
expected
);
}
fs.rmSync(snapYamlPath);
assert.match(
validateExtractedSnapMetadata(root).join('\n'),
/Missing extracted Snap metadata/
);
fs.writeFileSync(path.join(root, 'outside.yaml'), validSnapYaml);
fs.symlinkSync(
path.join(root, 'outside.yaml'),
snapYamlPath,
process.platform === 'win32' ? 'file' : undefined
);
assert.match(
validateExtractedSnapMetadata(root).join('\n'),
/regular file/
);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});
test('artifact verification rejects Snap metadata before accepting its payload', () => {
const fixture = createSystemPayload();
const artifactPath = path.join(fixture.root, 'package.snap');
const snapYamlPath = path.join(fixture.root, 'meta', 'snap.yaml');
fs.writeFileSync(artifactPath, 'fixture');
fs.mkdirSync(path.dirname(snapYamlPath), { recursive: true });
fs.writeFileSync(
snapYamlPath,
[
'name: iptvnator',
'apps:',
' iptvnator:',
' command: iptvnator',
' plugs:',
' - desktop',
'plugs:',
' shared-memory:',
' interface: shared-memory',
' private: true',
'',
].join('\n')
);
let payloadVerifierCalls = 0;
const verify = () =>
verifyLinuxFrameCopyArtifact({
artifactPath,
profileName: 'portable',
extractArtifact() {
return fixture.root;
},
metadataReader() {
return { declaredArch: 'x64', dependencies: [] };
},
payloadVerifier() {
payloadVerifierCalls += 1;
return [];
},
});
try {
assert.throws(verify, /app.*shared-memory plug/i);
assert.equal(payloadVerifierCalls, 0);
fs.writeFileSync(
snapYamlPath,
fs
.readFileSync(snapYamlPath, 'utf8')
.replace(' - desktop\n', ' - shared-memory\n')
);
assert.deepEqual(verify(), {
artifactPath,
format: 'snap',
profileName: 'portable',
architecture: 'x64',
});
assert.equal(payloadVerifierCalls, 1);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
test('bundled probes use only the packaged library directory', () => {
assert.deepEqual(
createRuntimeProbeEnvironment({
environment: {
PATH: '/usr/bin',
LD_LIBRARY_PATH: '/host/can-mask-missing-dependencies',
LD_PRELOAD: '/host/can-inject.so',
},
nativeDir: '/package/resources/native',
runtimeMode: 'bundled',
}),
{
PATH: '/usr/bin',
LD_LIBRARY_PATH: '/package/resources/native/lib',
}
);
});
test('rejects helper probe framing and fields that runtime capability rejects', () => {
const validPayload =
'{"protocol":1,"usable":true,"libmpv":"0.41.0","renderApi":"egl"}';
for (const stdout of [
validPayload,
`${validPayload}\n\n`,
`${validPayload.slice(0, -1)},"extra":true}\n`,
]) {
const fixture = createSystemPayload();
try {
const errors = verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'deb',
profileName: 'system',
packageDependencies: ['libmpv2'],
elfInspector: validElfInspector,
probeRunner() {
return {
status: 0,
signal: null,
stdout,
stderr: '',
};
},
});
assert.match(
errors.join('\n'),
/runtime probe (?:must emit|did not return)/
);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
}
});
test('reports missing helper, wrong mode, wrong profile, isolation, and loader failures', () => {
const cases = [
{
mutate({ nativeDir }) {
fs.rmSync(path.join(nativeDir, 'iptvnator_mpv_helper'));
},
expected: /Missing embedded MPV frame-copy helper/,
},
{
mutate({ nativeDir }) {
fs.chmodSync(
path.join(nativeDir, 'iptvnator_mpv_helper'),
0o644
);
},
expected: /must have mode 0755/,
},
{
profileName: 'portable',
expected: /does not include target "deb"/,
},
{
elfInspector(binaryPath) {
if (path.basename(binaryPath) === 'embedded_mpv.node') {
return {
soname: null,
needed: ['libmpv.so.2'],
rpath: [],
runpath: [],
};
}
return validElfInspector(binaryPath);
},
expected: /addon must not link libmpv/,
},
{
probeRunner() {
return {
status: 127,
signal: null,
stdout: '',
stderr: 'error while loading shared libraries: libmpv.so.2',
};
},
expected: /runtime probe failed with status 127.*libmpv\.so\.2/s,
},
];
for (const testCase of cases) {
const fixture = createSystemPayload();
try {
testCase.mutate?.(fixture);
const errors = verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'deb',
profileName: testCase.profileName ?? 'system',
packageDependencies: ['libmpv2'],
elfInspector: testCase.elfInspector ?? validElfInspector,
probeRunner: testCase.probeRunner ?? successfulProbeRunner,
});
assert.match(errors.join('\n'), testCase.expected);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
}
});
test('rejects an x64 package manifest produced from a target subset', () => {
const fixture = createSystemPayload();
const manifestPath = path.join(
fixture.nativeDir,
'embedded-mpv-runtime.json'
);
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
manifest.targets = ['deb'];
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
try {
const errors = verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'deb',
profileName: 'system',
packageDependencies: ['libmpv2'],
elfInspector: validElfInspector,
probeRunner: successfulProbeRunner,
});
assert.match(
errors.join('\n'),
/manifest targets.*must equal \["deb","pacman","rpm"\]/
);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
test('requires marker-only foreign packages, scans Electron, and never probes', () => {
const fixture = createSystemPayload({ architecture: 'arm64' });
let probeCalls = 0;
try {
assert.deepEqual(
verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'appimage',
profileName: 'portable',
packageDependencies: [],
elfInspector: validElfInspector,
probeRunner() {
probeCalls += 1;
return successfulProbeRunner();
},
}),
[]
);
assert.equal(probeCalls, 0);
const isolationErrors = verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'appimage',
profileName: 'portable',
packageDependencies: [],
elfInspector(binaryPath) {
if (path.basename(binaryPath) === 'iptvnator.bin') {
return {
soname: null,
needed: ['/tmp/libmpv.so.2'],
rpath: [],
runpath: [],
};
}
return validElfInspector(binaryPath);
},
probeRunner() {
probeCalls += 1;
return successfulProbeRunner();
},
});
assert.match(
isolationErrors.join('\n'),
/Electron binary must not link libmpv/
);
assert.equal(probeCalls, 0);
const dependencyErrors = verifyExtractedLinuxFrameCopyRuntime({
resourceDir: fixture.resourceDir,
artifactFormat: 'deb',
profileName: 'system',
packageDependencies: ['libmpv2', 'libc6'],
declaredArch: 'arm64',
elfInspector: validElfInspector,
probeRunner: successfulProbeRunner,
});
assert.match(
dependencyErrors.join('\n'),
/must not declare frame-copy dependency libmpv2/
);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
test('always removes its temporary extraction root after a verifier failure', () => {
const root = fs.mkdtempSync(
path.join(os.tmpdir(), 'iptvnator-verifier-cleanup-parent-')
);
const artifactPath = path.join(root, 'package.deb');
fs.writeFileSync(artifactPath, 'fixture');
let extractionDestination;
try {
assert.throws(
() =>
verifyLinuxFrameCopyArtifact({
artifactPath,
profileName: 'system',
extractArtifact({ destination }) {
extractionDestination = destination;
fs.writeFileSync(
path.join(path.dirname(destination), 'sentinel'),
'temporary'
);
throw new Error('intentional extraction failure');
},
}),
/intentional extraction failure/
);
assert.equal(
fs.existsSync(path.dirname(extractionDestination)),
false,
'temporary verifier root must be removed'
);
} finally {
fs.rmSync(root, { recursive: true, force: true });
}
});