Files
iptvnator/tools/packaging/linux-frame-copy-profile.test.mjs
T
4gray 8fdac824fd feat(packaging): ship Linux embedded MPV frame-copy runtime (#1200)
* docs: design Linux frame-copy packaging

* docs: plan Linux frame-copy packaging

* feat(packaging): define Linux frame-copy profiles

* fix(packaging): reject inherited profile names

* feat(embedded-mpv): validate staged Linux runtime

* fix(embedded-mpv): require Linux source packages

* fix(embedded-mpv): harden Linux runtime staging

* feat(embedded-mpv): build LGPL Linux runtime

* fix(embedded-mpv): pin Linux runtime inputs

* feat(embedded-mpv): build relocatable Linux helper

* fix(embedded-mpv): require bundled Linux runtime

* fix(embedded-mpv): make Linux runtime portable

* feat(packaging): ship Linux frame-copy artifacts

* fix(embedded-mpv): verify Linux helper linkage

* fix(packaging): enforce Linux frame-copy isolation

* fix(embedded-mpv): pin Linux display data

* docs(embedded-mpv): document Linux frame-copy packaging

* feat(embedded-mpv): probe Linux frame-copy runtime

* test(embedded-mpv): smoke packaged Linux frame-copy

* docs(embedded-mpv): clarify Linux system runtime baseline

* fix(embedded-mpv): harden Linux runtime capability gate

* ci: verify Linux frame-copy packages

* test(embedded-mpv): harden packaged Linux smoke

* test(embedded-mpv): preserve packaged GL mode

* test(packaging): harden Linux package probes

* fix(embedded-mpv): enable private Snap shared memory

* fix(embedded-mpv): sanitize Linux helper environment

* fix(packaging): enforce private Snap memory semantics

* fix(packaging): reject ambiguous Snap memory metadata

* fix(embedded-mpv): prioritize trusted Snap GL

* fix(packaging): reject advanced Snap YAML semantics

* fix(packaging): reject arbitrary Snap YAML aliases

* feat(packaging): ship Linux runtime license notices

* docs(embedded-mpv): document Linux runtime distribution

* fix(packaging): parse Snap trailing comments safely

* fix(release): gate Snap publish on public source release

* fix(packaging): strip VCS metadata from source bundle

* docs(packaging): clarify Linux source release gate

* test(embedded-mpv): smoke missing bundled libmpv

* style(embedded-mpv): format final validation inputs

* fix(e2e): satisfy fixture index signature typing

* fix(ci): declare fontconfig gperf generator

* fix(embedded-mpv): hash runtime cache identities

* fix(packaging): harden Linux frame-copy delivery

* fix(packaging): tighten runtime delivery gates

* fix(ci): decouple Linux runtime matrix

* fix(packaging): harden Linux frame-copy delivery

* fix(packaging): validate Linux frame-copy runtimes

* fix(packaging): scope Snap Electron library checks

* feat(packaging): ship Linux frame-copy runtimes

* fix(packaging): improve Linux runtime smoke diagnostics

* fix(packaging): expose bounded helper probe details

* test(packaging): trace Snap EGL probe failures

* fix(packaging): prefer core22 ABI in Snap helper

* fix(packaging): bound helper probe capture

* fix(packaging): harden Linux frame-copy releases

* fix(packaging): canonicalize libplacebo submodule identity

* fix(packaging): make source archive inspection portable

* fix(packaging): harden Snap release verification
2026-07-18 17:28:22 +02:00

213 lines
7.1 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import { createRequire } from 'node:module';
import { dirname, join } from 'node:path';
import test from 'node:test';
import { fileURLToPath } from 'node:url';
import { configureLinuxFrameCopyBuild } from './configure-linux-frame-copy-build.mjs';
const currentDir = dirname(fileURLToPath(import.meta.url));
const require = createRequire(import.meta.url);
const {
LINUX_FRAME_COPY_PROFILES,
LINUX_SYSTEM_PACKAGE_DEPENDENCIES,
resolveLinuxFrameCopyProfile,
validateLinuxProfileTargets,
} = require('./linux-frame-copy-profile.cjs');
const electronBuilderConfig = JSON.parse(
fs.readFileSync(
join(currentDir, '..', '..', 'electron-builder.json'),
'utf8'
)
);
function hasSystemFrameCopyDependency(config, format, dependency) {
return (config[format]?.fpm ?? []).some((option) =>
new RegExp(`^--depends(?:=|\\s+)${dependency}(?:$|\\s|[<>=])`).test(
option
)
);
}
test('defines the exact immutable Linux frame-copy profile matrix', () => {
assert.deepEqual(LINUX_FRAME_COPY_PROFILES, {
system: {
name: 'system',
runtimeMode: 'system',
targets: ['deb', 'rpm', 'pacman'],
manifestOrigin: 'system-libmpv-frame-copy',
},
portable: {
name: 'portable',
runtimeMode: 'bundled',
targets: ['appimage', 'snap'],
manifestOrigin: 'bundled-lgpl-frame-copy',
},
flatpak: {
name: 'flatpak',
runtimeMode: 'bundled',
targets: ['flatpak'],
manifestOrigin: 'bundled-lgpl-frame-copy',
},
});
assert.equal(Object.isFrozen(LINUX_FRAME_COPY_PROFILES), true);
for (const profile of Object.values(LINUX_FRAME_COPY_PROFILES)) {
assert.equal(Object.isFrozen(profile), true);
assert.equal(Object.isFrozen(profile.targets), true);
}
});
test('defines immutable system-package helper runtime dependencies', () => {
assert.deepEqual(LINUX_SYSTEM_PACKAGE_DEPENDENCIES, {
deb: ['libmpv2', 'libegl1', 'libgl1', 'libgbm1'],
rpm: ['mpv-libs', 'libglvnd-egl', 'libglvnd-glx', 'mesa-libgbm'],
pacman: ['mpv', 'libglvnd', 'mesa'],
});
assert.equal(Object.isFrozen(LINUX_SYSTEM_PACKAGE_DEPENDENCIES), true);
for (const dependencies of Object.values(
LINUX_SYSTEM_PACKAGE_DEPENDENCIES
)) {
assert.equal(Object.isFrozen(dependencies), true);
}
});
test('resolves each supported profile as an immutable defensive value', () => {
const firstSystemProfile = resolveLinuxFrameCopyProfile('system');
const secondSystemProfile = resolveLinuxFrameCopyProfile('system');
assert.deepEqual(firstSystemProfile, {
name: 'system',
runtimeMode: 'system',
targets: ['deb', 'rpm', 'pacman'],
manifestOrigin: 'system-libmpv-frame-copy',
});
assert.deepEqual(resolveLinuxFrameCopyProfile('portable'), {
name: 'portable',
runtimeMode: 'bundled',
targets: ['appimage', 'snap'],
manifestOrigin: 'bundled-lgpl-frame-copy',
});
assert.deepEqual(resolveLinuxFrameCopyProfile('flatpak'), {
name: 'flatpak',
runtimeMode: 'bundled',
targets: ['flatpak'],
manifestOrigin: 'bundled-lgpl-frame-copy',
});
assert.notEqual(firstSystemProfile, LINUX_FRAME_COPY_PROFILES.system);
assert.notEqual(firstSystemProfile, secondSystemProfile);
assert.notEqual(
firstSystemProfile.targets,
LINUX_FRAME_COPY_PROFILES.system.targets
);
assert.notEqual(firstSystemProfile.targets, secondSystemProfile.targets);
assert.equal(Object.isFrozen(firstSystemProfile), true);
assert.equal(Object.isFrozen(firstSystemProfile.targets), true);
assert.throws(() => firstSystemProfile.targets.push('appimage'), TypeError);
assert.deepEqual(resolveLinuxFrameCopyProfile('system').targets, [
'deb',
'rpm',
'pacman',
]);
});
test('rejects missing and unsupported profile names with clear errors', () => {
for (const value of [undefined, null, '', ' ']) {
assert.throws(
() => resolveLinuxFrameCopyProfile(value),
/Linux frame-copy profile is required/
);
}
assert.throws(
() => resolveLinuxFrameCopyProfile('standard'),
/Unsupported Linux frame-copy profile "standard"/
);
});
test('rejects inherited object property names as unsupported profiles', () => {
for (const value of ['constructor', 'toString', '__proto__']) {
const unsupportedProfileError = new RegExp(
`Unsupported Linux frame-copy profile "${value}"`
);
assert.throws(
() => resolveLinuxFrameCopyProfile(value),
unsupportedProfileError
);
assert.throws(
() => validateLinuxProfileTargets(value, ['deb']),
unsupportedProfileError
);
}
});
test('validates profile targets case-insensitively with deterministic errors', () => {
const targets = ['DEB', 'AppImage', 'RPM'];
assert.deepEqual(validateLinuxProfileTargets('system', targets), [
'Linux frame-copy profile "system" cannot build target "appimage".',
]);
assert.deepEqual(targets, ['DEB', 'AppImage', 'RPM']);
assert.deepEqual(
validateLinuxProfileTargets('portable', ['APPIMAGE', 'sNaP']),
[]
);
assert.deepEqual(validateLinuxProfileTargets('flatpak', ['FlatPak']), []);
});
test('rejects a non-array profile target list', () => {
assert.throws(
() => validateLinuxProfileTargets('system', 'deb'),
/Linux frame-copy targets must be an array/
);
});
test('keeps frame-copy package dependencies out of the base Electron Builder config', () => {
for (const [target, dependencies] of Object.entries(
LINUX_SYSTEM_PACKAGE_DEPENDENCIES
)) {
assert.equal(
electronBuilderConfig[target]?.depends,
undefined,
`${target}.depends must remain unset so electron-builder keeps its defaults`
);
for (const dependency of dependencies) {
assert.equal(
hasSystemFrameCopyDependency(
electronBuilderConfig,
target,
dependency
),
false
);
}
}
});
test('keeps frame-copy package dependencies out of non-system passes', () => {
const configs = [
configureLinuxFrameCopyBuild(electronBuilderConfig, {
profileName: 'portable',
}),
configureLinuxFrameCopyBuild(electronBuilderConfig, {
profileName: 'flatpak',
}),
configureLinuxFrameCopyBuild(electronBuilderConfig, {
foreignDeb: true,
}),
];
for (const config of configs) {
for (const [format, dependencies] of Object.entries(
LINUX_SYSTEM_PACKAGE_DEPENDENCIES
)) {
for (const dependency of dependencies) {
assert.equal(
hasSystemFrameCopyDependency(config, format, dependency),
false
);
}
}
}
});