Test plan¶
One test per row of FEATURES.md, on a Retroid Pocket Nova running the build
under test. Automatic tests are run by tools/device-tests over SSH and
print PASS, FAIL or SKIP per test; human tests are a checklist with the
exact thing to look or listen for. Configuration expectations reviewed
18 October 2026; recorded runs below include older builds.
tools/device-tests still contains obsolete assertions, including a fixed
256-frame audio quantum, automatic frame delay outside its current
conditions, and old PlayStation profile behavior. Its panel-mode checks
also need to distinguish fixed refresh from VRR. Keep its results, but
check those failures against the current configuration and the manual
procedures below before treating them as regressions. Updating the checker
is tracked in known issues.
Test IDs follow the FEATURES.md groups: D display, A audio, E emulation,
L launcher and system, P power and hardware, M media, B build. A test is
auto when a command on the device reads the answer, human when only
eyes or ears can, and auto+human when a command confirms the setup and
a person confirms the effect.
Running the automatic tests¶
tools/device-tests root@nova # idle checks
tools/device-tests root@nova game snes # with a game of that system running
The first form checks the idle state: config files, sysfs, services. The
second form reads the state of the running emulator from
/tmp/.retroarch.cfg (the main retroarch.cfg for keys the launch does
not write), the RetroArch log and hw_params, so start a game of
the named system first and leave it running. Run it once per system in the
panel-mode table (gb, snes, nes, genesis, psx, n64, neogeo, neocd).
Results go into the Status column of FEATURES.md: a row is verified once its auto test passes and its human test has been signed off on the build named at the top of that file.
Ground truth used by the tests¶
| Thing | Where it is on the device |
|---|---|
| Config RetroArch actually launched with | /tmp/.retroarch.cfg (what setsettings.sh changes for this launch) over /storage/.config/retroarch/retroarch.cfg |
| RetroArch log | /storage/.config/retroarch/logs/ (newest file; log_to_file and verbosity on) |
| Emulator launch log | /var/log/exec.log |
| Boot log | /var/log/boot.log |
| Panel modes | modetest -M msm -c (mode list), /sys/class/drm/card*-DSI-1/modes |
| Vblank counter | /sys/kernel/debug/dri/0/crtc-0/status |
| Audio link rate | /proc/asound/card0/pcm*p/sub0/hw_params (rate: line) |
| PipeWire graph | pw-dump, pw-top -b -n 1, wpctl status |
| Settings | /storage/.config/system/configs/system.cfg |
| Migration markers | /storage/.config/system/.migrations/ |
| Core options | /storage/.config/retroarch/retroarch-core-options.cfg; inspect the launched configuration for overrides |
| GPU frequency | /sys/devices/platform/soc@0/3d00000.gpu/devfreq/3d00000.gpu/cur_freq |
| Battery | /sys/class/power_supply/battery/{temp,current_now,constant_charge_current,constant_charge_current_max,status} |
| Fan | $DEVICE_PWM_FAN from /etc/profile (hwmon pwm1), pwm1_enable |
| Update stage | /storage/.update/ |
Display¶
D1 One panel mode per console family (auto)¶
Idle: the DSI connector lists all eight modes.
grep -c . /sys/class/drm/card*-DSI-1/modes # 8
modetest -M msm -c | grep -oE '[0-9]+\.[0-9]+' | sort -u
With the default built-in-panel VRR path, every core uses the fastest
120.198 Hz mode. Check video_refresh_rate, the live CRTC state and a
display trace; the panel repeats frames at an integer multiple of the
core's rate. See variable refresh.
For a fixed-refresh comparison, save the current VRR setting, set the
tested system's <system>.vrr=0 and relaunch. video_refresh_rate and
the Vulkan log should then name the table's mode. Restore the original
setting afterward.
| System (core) | Expected |
|---|---|
| gb, gbc, gba (gambatte, mgba) | 119.455 |
| snes, nes (snes9x, mesen2) | 120.198 |
| psx, n64, saturn | 119.652 |
| genesis (genesis_plus_gx) | 119.846 |
| neogeo (fbneo) | 118.360 |
| neocd | 119.200 |
| everything else | 119.880 |
Steam: gamescope selects the fastest mode that rounds to 120 Hz, currently 120.198 Hz, with adaptive sync and a 60 fps ceiling. Check live refreshes, game rate and repeat cadence instead of expecting a constant 120.000 Hz. Real-game validation of the final stack is still pending.
Fail if the selected mode disagrees with the path being tested, or a fixed-refresh test falls back to the default for a system with its own row.
D3 Per-core swapchain, no threaded video (auto)¶
Game running, in the launched config:
video_max_swapchain_images = "2" # 3 for ParaLLEl N64 and Dolphin
video_threaded = "false"
video_swap_interval = "0" # RetroArch takes 1 under Sync to Exact Content Framerate
video_black_frame_insertion = "0"
video_vsync = "false" # always: there is no vsync (0021)
vrr_runloop_enable = "true" # always
The MangoHud frame rate over a minute is the core's own rate within 0.01%: 59.826 for Saturn, 60.099 for SNES. Fail at 0.4% or more above it, which is the loop running on audio sync alone.
D4 Automatic frame delay (auto+human)¶
At fixed refresh without preemptive frames,
video_frame_delay_auto = "true" in the launched config. With VRR or
preemptive frames it must be false; VRR uses the timed-present scheduler
instead. Test each path explicitly. For the fixed-refresh path, enable
RetroArch's statistics and check frame delay after thirty seconds of play;
keep frame-delivery logs alongside the configuration.
D5 Integer scaling per system (human)¶
Take a screenshot (RetroArch hotkey, or screenshot from the launcher
Tools menu) in each system and measure the image in an editor.
| System | Expected picture |
|---|---|
| GB / GBC | 960×864, centred, black around |
| GBA | 1200×800 (5×), lcd-grid-v2 visible at 1:1 zoom |
| SNES | 1024×896 (4× of 256×224), crt-guest-advanced visible |
| NES | 4× of 256×240, 32-pixel bars top and bottom accepted |
| PSX | 4× internal, 240-line CRT shader, no half-pixel smear |
| N64 | 2× on ParaLLEl-RDP, no blur |
Fail if any edge shows a non-integer step (a row of pixels wider or narrower than its neighbours).
D6 Color profile Gamma 2.2 and sRGB (human)¶
Settings > Color profile: set each of sRGB and Gamma 2.2, then show a 16-step grey ramp image (copied into roms/media, played with mpv) and count the distinguishable steps at the dark end. Pass for a profile when all 16 are distinct and pure red, green and blue patches do not shift hue against the stock profile. Known state: both fail today on the dark steps; this test records how many steps are lost.
D7 Stock as the default profile (auto)¶
Idle, after an update from a build that had gamma22 or srgb set (the
script skips the value check when system.cfg changed after the marker,
since the user may have picked a profile since):
grep ^display.colorprofile= /storage/.config/system/configs/system.cfg # stock
ls /storage/.config/system/.migrations/colorprofile-stock # exists
stock too. Then set sRGB in the launcher,
reboot, and confirm the key stays srgb: the migration must run once.
D8 De-gamma stage through the LUTDMA (human, SSH open)¶
Never run. Two stages, each with SSH connected before starting so a black screen is recoverable:
- Boot the kernel with patches 1070 and 1071 alone. The panel comes up,
the launcher draws,
dmesg | grep -i lutdmashows no errors. - Bind-mount a three-table profile from
make-igc-profile.pyover the active profile and select it. The picture changes; grey ramp as D6.
Fail at either stage on a black panel, a dmesg error from dpu or
msm, or a hang. Recovery: reboot over SSH.
D9 Lowest GPU operating point (auto)¶
Idle at the launcher for thirty seconds:
cat /sys/devices/platform/soc@0/3d00000.gpu/devfreq/3d00000.gpu/cur_freq # 124800000
cat .../min_freq # 124800000
pwm1 reads 0) with the device below 40 °C.
D10 8bpc output dither (auto+human)¶
Auto, idle: the panel node in the live device tree carries the opt-in.
ls /proc/device-tree/soc@0/display-subsystem@ae00000/dsi@ae94000/panel@0/armada,dpu-8bpc-dither
Audio¶
A1 Link rate follows the stream (auto+human)¶
Game running, per system:
grep rate: /proc/asound/card0/pcm*p/sub0/hw_params
The link runs at the rate RetroArch opened, the at N Hz of the
Driver "pipewire" reports line in the log. That rate is the per-core
audio_out_rate where A3 sets one, else A2's pick:
| System | Expected link rate |
|---|---|
| psx, saturn, dreamcast, psp, neocd, genesis, mastersystem, gamegear, segacd, sega32x | 44100 (per-core) |
| snes | 32000 (per-core) |
| gb, gbc (32768 Hz) | 44100 (picked) |
| gba (65536 Hz) | 48000 (picked) |
| n64, scummvm and the rest | picked from the core's rate |
Human, for 32 kHz: play a SNES game with a known tune (Super Mario World title) next to a recording; pitch and tempo match. A wrong link rate gives a pitch shift of about a semitone.
A2 RetroArch picks the output rate from the core (auto)¶
For any core without a per-core rate, the log's pick follows retroarch patch 0015: the smallest of 32000, 44100, 48000 that the core's rate divides into within 0.5 %, else the smallest above it, else 48000. The script recomputes it from the core rate in the log. Start an N64 game at each of the three common rates (22.05 kHz: Super Mario 64; 32 kHz: Ocarina of Time; 44.1 kHz: Perfect Dark). The RetroArch log has one line per game
[Audio] Output rate picked for the core's 32006.00 Hz: 32000 Hz.
hw_params matches the picked rate. audio_out_rate = "0" stays in
the launched config. Fail if every game picks 48000.
A3 Per-core output rates (auto)¶
Covered by A1's table; additionally audio_out_rate in the per-core
config under /storage/.config/retroarch/config/<core>/<core>.cfg is
44100 for SwanStation, Ymir, Flycast, PPSSPP, NeoCD, Genesis
Plus GX and PicoDrive, and the global file keeps 0, which picks 32000
for Snes9x's 32040 and 44100 for an MSU-1 game.
A4 PipeWire and the 3 ms quantum floor (auto+human)¶
command -v pulseaudio # no separate PulseAudio server
pw-top # read the running sink, its rate and ERR
grep -E 'allowed-rates|min-quantum|power-of-two-quantum' /usr/share/pipewire/pipewire.conf
The graph permits 32, 44.1 and 48 kHz, with a 144-frame minimum at the 48 kHz reference rate and power-of-two rounding disabled. With an 8 ms RetroArch request, its requested quantum is 96, 133 or 144 frames at the three rates. Other clients can raise the running quantum. Test all three rates, retain before/after ERR counters and listen for crackle. A follower's QUANT column may show its request rather than the running sink's quantum. See audio latency for measured limits and open coverage.
Emulation¶
E1 One emulator per system (auto)¶
ls /usr/lib/libretro/*_libretro.so
melonds,
yabause, beetle_saturn alongside beetle_saturn_hw, and so on. The
script carries the expected list.
E2 Preemptive frame per 2D console (auto+human)¶
Auto, game running with <system>.preempt=1 in system.cfg:
preemptive_frames_enable = "true"
run_ahead_enabled = "false"
run_ahead_frames = "1"
/tmp/.retroarch.cfg, plus video_frame_delay_auto = "false", which
the preemptive frame turns off. With preempt=0 the first key is
false; automatic delay returns only at fixed refresh, since VRR disables
it independently.
The log has no [Run-Ahead Preemptive] line: RetroArch logs one only
when the preemptive frame fails. That absence is worth nothing until the
line has been seen, and run_ahead_frames = "0" makes preempt_init
return silently, so a config check alone cannot tell working from inert.
To make it fail on purpose, both steps are needed:
sed -i 's/"deterministic"/"basic"/' /tmp/cores/<core>_libretro.info
rm -f /tmp/cores/core_info.cache # or the cache answers instead
/tmp/cores, not /usr/lib/libretro.
Human: hold the D-pad and tap jump repeatedly for a minute; no frame
flickers backwards and the sound does not stutter. Then set an explicit
<system>.runahead=2 and confirm classic run-ahead
(run_ahead_enabled = "true", run_ahead_frames = "2").
Measured on SNES, Super Mario World, snes9x, with MangoHud as E3 sets it up. The preemptive frame is free here: every run delivered every frame the console asked for, and playing changed nothing that a second run of the same setting would not.
| run | frames | expected | mean | p99 | max |
|---|---|---|---|---|---|
| idle, preempt=0 | 1802 | 1802.9 | 16.638 | 20.87 | 22.42 |
| idle, preempt=1 | 1802 | 1802.9 | 16.637 | 20.77 | 21.69 |
| playing, preempt=0 | 2704 | 2704.4 | 16.637 | 20.82 | 22.56 |
| playing, preempt=1 | 2704 | 2704.4 | 16.638 | 21.11 | 23.46 |
Target 16.639 ms, one refresh 8.32, so a missed refresh is 24.96 and none of the four runs reached it. These are historical fixed-mode runs. SwanStation's readback fix and later VRR changes have separate measurements in SwanStation and latency.
Preemptive frames remove one frame of the game's own lag. Physical button-to-screen latency still needs a controlled trigger and camera test; frametime logs alone do not establish that benefit. Neo Geo CD now enables one preemptive frame by default, with Metal Slug 1/2 pause and resume checks recorded; other systems keep it off. See NeoCD.
E3 PlayStation runs every frame on time (auto+human)¶
The PlayStation defaults to Vulkan at 4x, preemptive frames off and two swapchain images. With VRR it uses the fastest 120.198 Hz mode and automatic frame delay is off. Compare current delivery and latency with SwanStation and latency measurements.
Auto, after a PSX launch:
grep -E 'GPU_Renderer|GPU_ResolutionScale' \
/storage/.config/retroarch/retroarch-core-options.cfg # "Vulkan" and "4"
grep -E 'preemptive_frames_enable|video_frame_delay_auto|video_max_swapchain_images' \
/tmp/.retroarch.cfg # "false", "false", "2" with VRR
grep -E 'video_refresh_rate' /tmp/.retroarch.cfg # about 120.198 with VRR
grep 'Timed presents' /storage/.config/retroarch/logs/retroarch.log
# One present per core frame, about 16.715 ms apart; no vsync counting.
The figures below are the historical fixed-mode baseline, before the current VRR path. Compare them only with that configuration; they are not absolute acceptance thresholds for VRR or every later core build.
With MangoHud logging on (portareos.mangohud.enabled=1, and
output_folder, autostart_log, log_duration in MangoHud.conf): play
Tekken 3 for the whole window. Against the 16.715 ms target the reference
run is mean 16.71, p95 18.23, p99 19.70, max 22.07, and 59.8 fps.
Three things make it a pass, and each fails differently:
- The frame count. Rows logged must be the window in seconds times 59.826 - 1794 in 30 s. Fewer means frames were dropped, whatever the percentiles say.
- No missed vblank. No frametime at or above 25.07 ms, which is the target plus one refresh. The reference run's worst frame was 22.07.
- The mean on target. Within about 0.05 ms of 16.715, so cumulative drift over the run is nil.
Spread below 25.07 ms is the core finishing early or late against a
present deadline the swapchain absorbs; it does not reach the panel and
is not a failure. The figures above are MangoHud's frametime, now
application_interval_ms. display_interval_ms (#595) is when each frame
reached the panel: there a missed vblank is a frame of 25.07 ms or more,
and the spread is gone.
With PRMPT on (psx.preempt=1), mashing through a fight: the launched
config has preemptive_frames_enable = "true" and
video_frame_delay_auto = "false", the CPU and GPU governors read
performance while the game runs, and the reference is 59.8 fps with
about 0.5 frames a second at or above 25.07 ms and p99 24.96. More than 2
a second means patch 002 or the clocks are not in effect.
E4 Game guide on M2 (human)¶
Put game.txt beside game.<ext> for one game in each of: SNES
(snes9x), N64 (parallel_n64), Dreamcast (flycast). Start the game, press
M2: the text appears within a second, D-pad scrolls, M2 or B returns to
the game at the same frame with audio continuing. Repeat five times per
core. Fail on a black screen, a RetroArch restart, or a Vulkan error in
the log after return. N64 and Dreamcast are the open items.
E5 Common quit and brightness hotkeys (human)¶
For every entry in the README table plus ScummVM and mpv: start
it, press Home + Start; it exits to the launcher within three seconds and
/var/log/exec.log ends with a clean exit line. Hold M1 and press volume
up or down: brightness steps visibly in every one of them.
E6 GBC hardware and palette for GBC games (human)¶
Start a GBC-only game (Zelda: Oracle of Ages). Colours are the GBC's
desaturated set, not the pure ones; the RetroArch log shows Gambatte
loading gbc.opt. Start a GB game: the GB palette applies instead.
E7 ScummVM as a libretro core (human)¶
Copy Beneath a Steel Sky (freeware) to roms/scummvm, start it. The intro
plays, MIDI music sounds through FluidLite, a save and load cycle works.
ls /storage/roms/bios/scummvm holds the data bundle.
E9 No automatic savestate load (auto)¶
Game running: savestate_auto_load = "false" in /tmp/.retroarch.cfg.
E10 Emulator pins bumped daily (auto, GitHub)¶
Actions > bump-emulator-pins has a run for each of the last three days and a PR exists for any pin that moved. Checked from the repository, not the device.
E11 Core options from the shipped file (auto)¶
Auto, idle: retroarch.cfg has global_core_options = "true", so every
core reads retroarch-core-options.cfg and no per-core .opt. With a
GameCube game running, Dolphin's geometry is 1280x1056, the shipped 2x;
a per-core file would give the core's own 1x, 640x528.
Launcher and system¶
L1 portarelauncher (auto+human)¶
Auto, idle:
pgrep -x portarelauncher
pidof portarelauncher # busybox pgrep -x misses it
cat /sys/kernel/debug/dri/0/clients # the launcher holds master
L2 Settings rows (human)¶
Each row once. Wi-Fi off and on reconnects; Bluetooth pairs a pad; SSH off
then on (ssh.enabled follows, sshd stops and starts); USB gadget
mounts on a PC; buttons swaps A/B in the launcher; color as D6; profile
changes the CPU governor; charging LED as L7; Language & region as L11,
its time zone changing the clock under About and the Wi-Fi country
(iw reg get: Europe/Berlin is DE, UTC is 00); About shows version,
password and IP; power reboots and shuts down.
L3 Settings > Consoles (auto+human)¶
Human: the row reads defaults until something is changed, then
N changed. Each 2D console toggles PRMPT on/PRMPT off with its
description; PSX toggles latency/visuals with the two-mode text; no
text runs off the 53-column grid. Auto after the changes:
grep -E '^(gb|gbc|gba|nes|snes|genesis)\.preempt=|^psx\.profile=' system.cfg
L4 Updates from GitHub (human)¶
Settings > Update on the nightly channel: the newest nightly downloads to
/storage/.update/, the sha256 check passes, the device reboots into it
and About shows the new version. Settings survive; .migrations gains any
new marker.
L5 Per-device root password on first boot (auto+human)¶
Auto: root.password in system.cfg is twelve characters from the
no-lookalike alphabet and is neither portareos nor rocknix. Human: the
password under About logs in over SSH, and portareos does not.
L6 One-shot migrations (auto)¶
ls /storage/.config/system/.migrations/
# cpugovernor-schedutil es-fpslimit ssh-default-on colorprofile-stock
ssh.enabled=0, reboot: it stays 0 (the migration is not re-run).
L7 Charging LED (human)¶
led.charging=1: plug the charger, both sticks glow yellow within three
seconds; unplug, they go dark - the sticks are an indicator while this is
on, so an rgb colour does not come back. Set it off in Settings: no glow
on plug, and the colour from led.color returns. With led.color=battery
the battery service keeps the sticks either way.
Reboot with the charger already plugged and check the sticks are yellow within a few seconds: the service used to lose a race with the battery appearing and die for the session (#446).
L8 Home + Start for emulators that do not quit (human)¶
Start mpv (it has no hotkey of its own); Home + Start returns to the launcher within three seconds.
L9 A saved stick calibration (auto)¶
GPcal is gone, but a calibration saved with it still runs at boot from
/storage/.config/autostart/GPcal.sh. Where one exists:
grep -o 'echo [-0-9]* > [^ ]*' /storage/.config/autostart/GPcal.sh | \
while read -r _ v _ f; do [ "$(cat $f)" = "$v" ] || echo "MISMATCH $f"; done
L10 PortScope (auto+human)¶
Human: Settings > Diagnostics > PortScope. Every button, the D-pad, both
triggers and both stick clicks light while held; each stick's dot follows
it and reads about +1.00 and -1.00 at the rails; RATE shows a number
while a stick moves and -- a second after. Hold SELECT for a second:
the header says PortScope, raw and the same controls light from the
MCU; hold it again to go back. Home + START leaves, from either layer.
With Button style set to Shapes, the face buttons are outlined marks
here, in every hint line and in the Button style diagram. Auto, after
leaving from the raw layer:
systemctl is-active inputplumber # active
grep -l DualSense /sys/class/input/event*/device/name # the virtual pad is back
L11 Japanese (auto+human)¶
Human: Settings > Language & region, press A on Language / 言語: every
screen redraws in Japanese, and again in English on the next press; a
restart keeps the choice. In Japanese: the console names are the ones
sold in Japan (スーパーファミコン, NINTENDO64); a ROM named in Japanese
shows its title, a long one scrolling with whole characters; Recently
played heads its days 今日, 昨日, with SFC and PS; Wi-Fi, Bluetooth,
Power and About read correctly. A ROM copied from a Mac with an accent in
its name reads correctly in either language. Auto:
grep '^system.language' /storage/.config/system/configs/system.cfg # ja_JP after the switch
ls -l /usr/share/portarelauncher/ # ja26.bin, ja26.NOTICE, OFL-1.1.txt
journalctl -u portarelauncher -b | grep ja26 # nothing: the glyphs loaded
Power and hardware¶
P1 Charge current throttle (auto)¶
systemctl is-active charge-throttle
journalctl -u charge-throttle --no-pager | tail
cat /sys/class/power_supply/battery/{temp,constant_charge_current}
constant_charge_current_max. Play a 3D game
on the charger until temp passes 400: the journal logs
charge current 3000000 uA and the file reads 3000000; at 420 it reads
2000000; cooling to below 380 restores the maximum. The script checks the
limit against the current temperature using the table in the service.
P2 Suspend and resume (human+auto)¶
Press power: screen off within two seconds. Auto during suspend, from a
second device on the charger-less Nova: not possible over SSH (Wi-Fi is
down); instead read current_now before and after a one-hour suspend and
compute drain from charge_now. Under 1 % an hour passes. Wake: panel,
Wi-Fi (ping), gamepad (every button in the launcher), LEDs, microSD
(ls /storage/roms) all work; dmesg after resume has no PCIe or ufs
errors.
P3 Fan control (auto)¶
systemctl is-active fancontrol
cat ${DEVICE_PWM_FAN}_enable # 1
cat ${DEVICE_PWM_FAN} # 0 when the CPU thermal zone is below the first curve step
pwm1 rises with thermal_zone cpu
temperature and never sits at 255 below 70 °C.
P4 microSD at SDR104 (auto)¶
cat /sys/kernel/debug/mmc*/ios | grep -E 'timing spec|clock'
timing spec: 6 (sd uhs SDR104) and clock: 202000000 Hz with a UHS-I
card inserted.
P5 1000 Hz tick, teo, schedutil (auto)¶
zcat /proc/config.gz | grep -E '^CONFIG_HZ_1000=y|^CONFIG_ENERGY_MODEL=y'
cat /sys/devices/system/cpu/cpuidle/current_governor # teo
cat /sys/devices/system/cpu/cpufreq/policy*/scaling_governor # schedutil
P6 Debug tools only outside official releases (auto)¶
OS_BUILD in /etc/os-release says whether the build is official.
Nightlies and releases are both official and carry neither gdb nor
strace; an unofficial local build has both, and strace -V prints 7.2.
P7 Gamepad MCU and stick LED rails off in suspend (auto+human)¶
Auto, idle: the rail exists and is held only by its consumers, none of them always-on.
for r in /sys/class/regulator/regulator.*; do
[ "$(cat $r/name)" = vdd_mcu_3v3 ] && cat $r/state $r/num_users
done # enabled, 3
dmesg | grep -i 'MCU supply' # nothing
dmesg has no "Failed to enable MCU supply"
or "Failed to enable regulator on resume" line, the sticks and every
button work in the launcher, and the stick LEDs show their colour again.
Human: with P2, an overnight suspend on battery; the drain before and
after nightly 156 is the number that says whether this helped #62.
Media¶
M1 mpv direct to the display (auto+human)¶
Play a 1080p H.264 file from Tools > Media. While it plays:
pgrep -af mpv | grep -o -- '--gpu-context=displayvk'
cat /sys/kernel/debug/dri/0/clients # mpv holds DRM master
top -bn1 | grep mpv # well under one core
/storage/.config/mpv/watch_later). Repeat with a HEVC file; a VP9
file plays in software without error.
M2 SD at 4:3 with scanlines (human)¶
Play a 712×478 DVD rip with no aspect flag: the picture is 4:3 with black pillars and visible scanlines. Play a 1080p file: no scanlines, full width.
Build and process¶
B1 Incremental nightly (auto, GitHub)¶
Two consecutive nightlies with a one-package change: the second finishes
in under a third of the full-rebuild time and its log restores the saved
state. A nightly after rebuild: full takes the full time.
B2 Thread logs in the job output (auto, GitHub)¶
Break a package on a branch (a bad PKG_SHA256), push, and the failing
job's "Print the failing thread logs" step shows that package's thread log
with the error. Revert.
B3 Package dependency check (auto, CI)¶
Add a dependency on a package that does not exist on a branch; CI fails
on check-package-deps.py naming it. Revert.
B4 Commit linter (auto, CI)¶
Push a commit titled without the package: prefix or over 72 characters;
validate-commit fails on it. Revert.
Sign-off sheet¶
Copy this table into the PR that updates FEATURES.md after a test round.
| Build | Tester | Auto passed / total | Human passed / total | Failures |
|---|---|---|---|---|
| nightly 155 |