Files
InstanceBrick--nightcap/tests/anylinux_packaging_test.sh
T

291 lines
13 KiB
Bash

#!/usr/bin/env bash
# Modified by vii from komaruworld/mocktail. See README "About this fork".
# Copyright 2026 Mocktail Project Authors
# SPDX-License-Identifier: Apache-2.0
set -Eeuo pipefail
readonly ROOT="${1:?source root is required}"
readonly CXX="${2:-c++}"
readonly TEMP_DIR="$(mktemp -d "${TMPDIR:-/tmp}/mocktail-anylinux-test.XXXXXX")"
trap 'rm -rf -- "${TEMP_DIR}"' EXIT
source "${ROOT}/scripts/package_anylinux.sh"
export UPINFO=""
mkdir -p -- "${TEMP_DIR}/fixtures"
# Compile the real adapter loader and updater canary. The fake runtime
# deliberately has no Android adapters next to its relocated executable.
"${CXX}" -std=c++17 -I"${ROOT}/include" -x c++ - \
"${ROOT}/src/graphics/bionic_egl_bridge.cc" \
"${ROOT}/src/runtime/supported_launch_policy.cc" \
"${ROOT}/src/update/readiness_canary.cc" -ldl -pthread \
-o "${TEMP_DIR}/fixtures/mocktail" <<'EOF'
#include <cstdlib>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <string>
#include "mocktail/graphics/bionic_egl_bridge.h"
#include "runtime/supported_launch_policy.h"
#include "update/readiness_canary.h"
int main(int argc, char** argv) {
std::string error;
if (!mocktail::runtime::ApplySupportedLaunchPolicy(false, &error)) return 4;
const char* manifest = std::getenv("MOCKTAIL_COMPATIBILITY_MANIFEST");
if (!manifest || std::string(manifest).empty()) return 5;
std::cout << "manifest=" << manifest << std::endl;
mocktail::graphics::BionicEglBridge bridge;
if (!bridge.Load()) {
std::cerr << bridge.error() << '\n';
return 1;
}
const char* directory = std::getenv("MOCKTAIL_RUNTIME_LIBRARY_DIR");
if (!directory || bridge.library_path() !=
std::filesystem::path(directory) / "libEGL.so") return 2;
std::cout << "adapter=" << bridge.library_path() << std::endl;
if (argc == 2 && std::string(argv[1]) == "--help") {
mocktail::update::CanaryOptions options;
options.runtime_binary = argv[0];
options.payload_directory = directory;
options.compatibility_manifest = std::filesystem::path(directory) / "candidate.json";
options.cache_root = std::getenv("MOCKTAIL_TEST_CANARY_ROOT");
options.state_root = options.cache_root / "state";
options.timeout_seconds = 5;
const auto result = mocktail::update::RunReadinessCanary(options);
std::ifstream log(result.log_path);
const std::string contents((std::istreambuf_iterator<char>(log)), {});
std::cout << contents;
// Only test loading through the real sanitized child environment;
// synthetic output is not proof of rendered-frame readiness.
return result.exit_code == 0 &&
contents.find("manifest=" + options.compatibility_manifest.string()) != std::string::npos &&
contents.find("canary adapter loaded") != std::string::npos ? 0 : 3;
}
std::cout << "canary adapter loaded\n";
}
EOF
"${CXX}" -std=c++17 -shared -fPIC "${ROOT}/stubs/libegl_stub.cc" \
-ldl -o "${TEMP_DIR}/fixtures/libEGL.so"
# The packaging contract uses a recording quick-sharun, without changing
# the host /usr or downloading toolchain dependencies during a unit test.
cat >"${TEMP_DIR}/quick-sharun" <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
if [[ "$1" == --make-appimage ]]; then
[[ "$#" == 1 && -f "${APPDIR}/AppRun.sh" ]]
[[ "${APPIMAGETOOL}" == /* && -x "${APPIMAGETOOL}" ]]
printf 'make-appimage\n' >>"${MOCKTAIL_PACKAGING_FIXTURES}/calls"
mkdir -p -- "${OUTPATH}"
cp -- "$(type -P true)" "${OUTPATH}/${OUTNAME}"
if [[ -n "${UPINFO:-}" ]]; then
printf '%s\n' "${UPINFO}" >"${OUTPATH}/update-info"
case "${MOCKTAIL_TEST_ZSYNC:-present}" in
present)
printf 'Filename: %s\nURL: %s\n\n\0checksum fixture' "${OUTNAME}" "${OUTNAME}" \
>"${OUTPATH}/${OUTNAME}.zsync"
cp -- "${OUTPATH}/${OUTNAME}.zsync" "${OUTPATH}/expected.zsync"
;;
empty) touch "${OUTPATH}/${OUTNAME}.zsync" ;;
missing) : ;;
esac
fi
exit 0
fi
[[ "$#" == 7 && "$1" == /usr/bin/mocktail &&
"$2" == /usr/lib/mocktail/mocktail_webview_helper &&
"$3" == /usr/lib/mocktail/mocktail_failure_dialog &&
"$4" == /usr/lib/mocktail/mocktail_updater &&
"$5" == /usr/lib/mocktail && "$6" == /usr/share/mocktail &&
"$7" == /usr/bin/bash ]]
[[ "${DESKTOP}" == /usr/share/applications/io.github.CoderDayton.nightcap.desktop ]]
[[ "${MAIN_BIN}" == mocktail && "${STRACE_MODE}" == 1 ]]
[[ "${STRACE_BINARY}" == mocktail && "${STRACE_FLAGS}" == --help ]]
[[ -z "${NO_STRIP:-}" ]]
if [[ -n "${SHARUN_LINK:-}" ]]; then
wget -qO "${MOCKTAIL_PACKAGING_FIXTURES}/downloaded-sharun" "${SHARUN_LINK}"
cmp "${MOCKTAIL_ANYLINUX_SHARUN}" "${MOCKTAIL_PACKAGING_FIXTURES}/downloaded-sharun"
wget -qO "${MOCKTAIL_PACKAGING_FIXTURES}/downloaded-source" "${ANYLINUX_LIB_SOURCE}"
cmp "${MOCKTAIL_ANYLINUX_C_SOURCE}" "${MOCKTAIL_PACKAGING_FIXTURES}/downloaded-source"
fi
# Only runtime metadata may be preseeded: no libs, custom AppRun, or bundle.
[[ "$(find "${APPDIR}" -mindepth 1 -maxdepth 1 -printf '%f\n')" == .env ]]
printf 'deploy-usr\n' >>"${MOCKTAIL_PACKAGING_FIXTURES}/calls"
mkdir -p "${APPDIR}/bin" "${APPDIR}/lib/mocktail" \
"${APPDIR}/lib/webkitgtk-6.0/injected-bundle" "${APPDIR}/share/mocktail/metadata"
cp -- "${MOCKTAIL_PACKAGING_FIXTURES}/mocktail" "${APPDIR}/bin/mocktail"
cp -- "${MOCKTAIL_PACKAGING_FIXTURES}/libEGL.so" "${APPDIR}/lib/mocktail/libEGL.so"
cp -- "${MOCKTAIL_PACKAGING_FIXTURES}/libEGL.so" "${APPDIR}/lib/mocktail/libvulkan.so"
cp -- "${MOCKTAIL_PACKAGING_FIXTURES}/libEGL.so" "${APPDIR}/lib/mocktail/libmediandk.so"
for metadata in roblox_compatibility roblox_bootstrap_sources \
roblox_host_abi_reference roblox_signing_certificates; do
printf '{}\n' >"${APPDIR}/share/mocktail/metadata/${metadata}.json"
done
for name in mocktail_updater mocktail_failure_dialog mocktail_webview_helper \
WebKitNetworkProcess WebKitWebProcess WebKitGPUProcess; do
cp -- "$(type -P true)" "${APPDIR}/bin/${name}"
done
for name in WebKitNetworkProcess WebKitWebProcess WebKitGPUProcess; do
ln -s -- "../../bin/${name}" "${APPDIR}/lib/webkitgtk-6.0/${name}"
done
printf 'injected bundle fixture\n' \
>"${APPDIR}/lib/webkitgtk-6.0/injected-bundle/libwebkitgtkinjectedbundle.so"
printf '#!/bin/sh\n' >"${APPDIR}/AppRun.sh"
printf '#!/bin/sh\n' >"${APPDIR}/AppRun.lib"
EOF
chmod 0755 "${TEMP_DIR}/quick-sharun"
mkdir -p "${TEMP_DIR}/fake-bin" "${TEMP_DIR}/local inputs"
cat >"${TEMP_DIR}/fake-bin/wget" <<'EOF'
#!/usr/bin/env bash
printf 'unadapted wget must not be used for local inputs\n' >&2
exit 1
EOF
chmod 0755 "${TEMP_DIR}/fake-bin/wget"
cp -- "$(type -P true)" "${TEMP_DIR}/fake-bin/fixture-appimagetool"
cp -- "$(type -P true)" "${TEMP_DIR}/local inputs/sharun"
printf 'local source fixture\n' >"${TEMP_DIR}/local inputs/anylinux.c"
export PATH="${TEMP_DIR}/fake-bin:${PATH}"
export MOCKTAIL_ANYLINUX_SHARUN="${TEMP_DIR}/local inputs/sharun"
export MOCKTAIL_ANYLINUX_C_SOURCE="${TEMP_DIR}/local inputs/anylinux.c"
export MOCKTAIL_PACKAGING_FIXTURES="${TEMP_DIR}/fixtures"
ANYLINUX_TOOL="${TEMP_DIR}/quick-sharun"
ANYLINUX_APPIMAGETOOL="${TEMP_DIR}/fake-bin/fixture-appimagetool"
ANYLINUX_WORK="${TEMP_DIR}/packaging"
ANYLINUX_OUTPUT="${TEMP_DIR}/Mocktail.AppImage"
mkdir -p -- "${ANYLINUX_WORK}"
AnyLinuxVerifyInstalled() { :; }
AnyLinuxDeploy
[[ -x "${ANYLINUX_OUTPUT}" ]]
[[ ! -e "${ANYLINUX_OUTPUT}.zsync" ]]
[[ "$(<"${TEMP_DIR}/fixtures/calls")" == $'deploy-usr\nmake-appimage' ]]
for filename in mocktail-nightly.AppImage mocktail-nightly-aarch64.AppImage \
Mocktail-1.0.5-x86_64.AppImage; do
(
ANYLINUX_WORK="${TEMP_DIR}/updates/${filename}"
ANYLINUX_OUTPUT="${TEMP_DIR}/${filename}"
export UPINFO="gh-releases-zsync|owner|repository|continuous|${filename}.zsync"
if [[ "${filename}" == Mocktail-* ]]; then
UPINFO='gh-releases-zsync|owner|repository|latest|Mocktail-*-x86_64.AppImage.zsync'
fi
AnyLinuxDeploy
[[ "$(<"${ANYLINUX_WORK}/image/update-info")" == "${UPINFO}" ]]
cmp -- "${ANYLINUX_WORK}/image/expected.zsync" "${ANYLINUX_OUTPUT}.zsync"
grep -aFxq "URL: ${filename}" "${ANYLINUX_OUTPUT}.zsync"
[[ ! -e "${ANYLINUX_WORK}/image/${filename}.zsync" ]]
)
done
for sidecar in missing empty; do
if (
ANYLINUX_WORK="${TEMP_DIR}/updates/${sidecar}"
ANYLINUX_OUTPUT="${TEMP_DIR}/${sidecar}.AppImage"
export UPINFO="gh-releases-zsync|owner|repository|continuous|${sidecar}.AppImage.zsync"
export MOCKTAIL_TEST_ZSYNC="${sidecar}"
AnyLinuxDeploy
) >"${TEMP_DIR}/${sidecar}.log" 2>&1; then
printf 'packager accepted a %s .zsync file\n' "${sidecar}" >&2
exit 1
fi
grep -Fq 'no .zsync was generated' "${TEMP_DIR}/${sidecar}.log"
[[ ! -e "${TEMP_DIR}/${sidecar}.AppImage" ]]
done
mv -- "${ANYLINUX_WORK}/AppDir" "${TEMP_DIR}/relocated AppDir"
app_dir="${TEMP_DIR}/relocated AppDir"
AnyLinuxVerifyAppDir "${app_dir}"
output="$(
export SHARUN_DIR="${app_dir}" MOCKTAIL_TEST_CANARY_ROOT="${TEMP_DIR}/canary"
export MOCKTAIL_RUNTIME_LIBRARY_DIR="${TEMP_DIR}/stale-runtime"
export XDG_CACHE_HOME="${TEMP_DIR}/user-cache"
export MOCKTAIL_COMPATIBILITY_MANIFEST="${TEMP_DIR}/candidate-override.json"
set -a
source "${app_dir}/.env"
set +a
[[ "${USE_HOST_XDG_CACHE_HOME}" == 1 &&
"${XDG_CACHE_HOME}" == "${TEMP_DIR}/user-cache" ]]
[[ "${MOCKTAIL_WEBVIEW_HELPER}" == "${app_dir}/bin/mocktail_webview_helper" ]]
[[ "${MOCKTAIL_UPDATE_SIGNING_TRUST_PATH}" == \
"${app_dir}/share/mocktail/metadata/roblox_signing_certificates.json" ]]
[[ "${MOCKTAIL_COMPATIBILITY_MANIFEST}" == "${TEMP_DIR}/candidate-override.json" ]]
[[ "${MOCKTAIL_PACKAGED_COMPATIBILITY_MANIFEST}" == \
"${app_dir}/share/mocktail/metadata/roblox_compatibility.json" ]]
"${MOCKTAIL_BIN}" --help
)"
grep -Fxq "adapter=${app_dir}/lib/mocktail/libEGL.so" <<<"${output}"
grep -Fxq 'canary adapter loaded' <<<"${output}"
mkdir "${app_dir}/usr"
if (AnyLinuxVerifyAppDir "${app_dir}") >"${TEMP_DIR}/unexpected-usr.log" 2>&1; then
printf 'packager accepted an old /usr-prefixed AppDir\n' >&2
exit 1
fi
rmdir "${app_dir}/usr"
mv -- "${app_dir}/bin/WebKitNetworkProcess" "${TEMP_DIR}/WebKitNetworkProcess"
if (AnyLinuxVerifyAppDir "${app_dir}") >"${TEMP_DIR}/missing-process.log" 2>&1; then
printf 'packager accepted a missing WebKit process\n' >&2
exit 1
fi
grep -Fq 'WebKitNetworkProcess' "${TEMP_DIR}/missing-process.log"
mv -- "${TEMP_DIR}/WebKitNetworkProcess" "${app_dir}/bin/WebKitNetworkProcess"
mv -- "${app_dir}/share/mocktail/metadata/roblox_signing_certificates.json" \
"${TEMP_DIR}/signing-certificates.json"
if (AnyLinuxVerifyAppDir "${app_dir}") >"${TEMP_DIR}/missing-metadata.log" 2>&1; then
printf 'packager accepted missing signature verification metadata\n' >&2
exit 1
fi
grep -Fq 'roblox_signing_certificates.json' "${TEMP_DIR}/missing-metadata.log"
prefix="${TEMP_DIR}/install/usr"
mkdir -p -- "${prefix}/lib/mocktail"
printf 'FreeBSD helper fixture\n' >"${prefix}/lib/mocktail/mocktail_freebsd_socket_helper"
AnyLinuxPrepareInstallTree "${prefix}"
[[ ! -e "${prefix}/lib/mocktail/mocktail_freebsd_socket_helper" ]]
grep -Fxq 'FreeBSD helper fixture' \
"${prefix}/share/mocktail/helpers/mocktail_freebsd_socket_helper"
# Exercise public AnyLinux dispatch and the unprivileged install path. A fake CMake
# installer refuses to write without DESTDIR; the real bwrap overlay must
# make those files visible under /usr to the real installed-layout checks.
if command -v bwrap >/dev/null &&
bwrap --ro-bind / / -- /usr/bin/true 2>/dev/null; then
mkdir -p "${TEMP_DIR}/fake-bin" "${TEMP_DIR}/build"
touch "${TEMP_DIR}/build/cmake_install.cmake"
cat >"${TEMP_DIR}/fake-bin/cmake" <<'EOF'
#!/usr/bin/env bash
set -Eeuo pipefail
[[ "$#" == 4 && "$1" == --install && "$3" == --prefix && "$4" == /usr ]]
[[ -n "${DESTDIR:-}" && "${DESTDIR}" == */install ]]
prefix="${DESTDIR}/usr"
mkdir -p "${prefix}/bin" "${prefix}/lib/mocktail" "${prefix}/share/mocktail/metadata"
cp -- "${MOCKTAIL_PACKAGING_FIXTURES}/mocktail" "${prefix}/bin/mocktail"
for helper in mocktail_updater mocktail_failure_dialog mocktail_webview_helper; do
cp -- "$(type -P true)" "${prefix}/lib/mocktail/${helper}"
done
for adapter in libEGL.so libvulkan.so libmediandk.so; do
cp -- "${MOCKTAIL_PACKAGING_FIXTURES}/libEGL.so" "${prefix}/lib/mocktail/${adapter}"
done
for metadata in roblox_compatibility roblox_bootstrap_sources \
roblox_host_abi_reference roblox_signing_certificates; do
printf '{}\n' >"${prefix}/share/mocktail/metadata/${metadata}.json"
done
EOF
chmod 0755 "${TEMP_DIR}/fake-bin/cmake"
PATH="${TEMP_DIR}/fake-bin:${PATH}" \
MOCKTAIL_APPIMAGE_FORMAT=anylinux \
MOCKTAIL_ANYLINUX_PACKAGER="${TEMP_DIR}/quick-sharun" \
MOCKTAIL_ANYLINUX_APPIMAGETOOL=fixture-appimagetool \
MOCKTAIL_ANDROID_BUILD_TOOLS_ROOT="${TEMP_DIR}/not-installed" \
"${ROOT}/scripts/package_portable.sh" --build-dir "${TEMP_DIR}/build" \
--libc glibc --mode full --output "${TEMP_DIR}/no-portable-tree" \
--appimage "${TEMP_DIR}/overlay.AppImage"
[[ -x "${TEMP_DIR}/overlay.AppImage" ]]
[[ ! -e "${TEMP_DIR}/no-portable-tree" ]]
else
printf 'SKIP: private /usr overlay test needs unprivileged bwrap\n'
fi
printf 'AnyLinux packaging test passed\n'