Files
shturman/crates/apps/shturman-shell/tests/screenshot.rs
T
kk0t9 9b87751ab8 feat(v0.6): Lima E2E зелёный с нуля + shell software-render screenshot
План 5 ч.2: поднял Lima-VM и довёл сквозной E2E до зелёного из чистого yaml
(just vm-reset && just e2e — exit 0). Приёмка §9.4 (v0.1 + v0.6 + шагающий скелет).

Shell (lib+bin split):
- режим --screenshot <path>: headless software-render первого кадра в PNG
  (Slint software-renderer, без дисплея/композитора, §6); TDD-тест «кадр не
  пустой + тема отражена», зелёный и на dev-Mac, и в VM (Linux).
- shturman-shell.service → oneshot software-render → /run/shturman/frame.png
  (RemainAfterExit → is-active детерминированно, без хрупкого weston;
  живой weston-shell — v0.5). just shell-frame — инспекция кадра.

E2E (tests/e2e/run.sh, двухфазно pre→reboot→post):
- /data+power-safe опции, volatile-tmpfs, first-boot идемпотентность, per-unit
  active, имена на шине + GetPowerState, fake-ACC SetAcc→AccChanged, первый кадр
  PNG, base-бюджеты (journald volatile / zram / oomd / fake-hwclock→/data /
  eMMC-прокси), персист Settings + machine-id every-boot bind после reboot.

Провижининг (lima/shturman.yaml) — правки по реальным ошибкам Lima:
- build-deps Slint/winit на Linux (libfontconfig1-dev/libxkbcommon-dev/libwayland-dev);
- linux-modules-extra (zram/vcan не в vz-ядре); systemd-oomd; rm стокового
  /etc/fake-hwclock.data (A11); VM-локальный CARGO_TARGET_DIR.

Док-синхронизация (спека §13/§8.1/§7.5 + CLAUDE.md): швы реализации, eMMC-порог
T=4096 сект, fake-hwclock masked-в-Lima, dev-mock policy не нужен.

Перф-вердикт — на RK3588 (в VM — функционально, performance §2). just ci зелёный.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Alexander <akotenev2003@gmail.com>
2026-06-24 17:14:31 +03:00

72 lines
2.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! Headless software-render первого кадра в PNG (спека §6 / §9.3 п.6 / §9.4 «шагающий скелет»).
//! Работает без дисплея (и на dev-Mac, и в Lima): Slint software-renderer → PNG.
//!
//! Один тест намеренно: Slint-платформа процесс-глобальна и ставится один раз, а `render_screenshot`
//! в проде зовётся ровно раз на процесс (oneshot-сервис / `just shell-frame`) — параллельный прогон
//! нескольких рендер-тестов в одном бинаре ушёл бы в дефолтный winit-бэкенд (на macOS — только main-thread).
use shturman_shell::{render_screenshot, Initial};
use std::path::Path;
/// Декодировать PNG → (ширина, высота, RGB-байты).
fn decode(path: &Path) -> (u32, u32, Vec<u8>) {
let dec = png::Decoder::new(std::fs::File::open(path).unwrap());
let mut reader = dec.read_info().unwrap();
let (w, h) = (reader.info().width, reader.info().height);
let mut buf = vec![0u8; reader.output_buffer_size()];
let info = reader.next_frame(&mut buf).unwrap();
buf.truncate(info.buffer_size());
(w, h, buf)
}
#[test]
fn renders_first_frame_reflecting_theme() {
let dir = tempfile::tempdir().unwrap();
// --- ночь: кадр не пустой, верный размер, тёмный фон ---
let night = dir.path().join("night.png");
render_screenshot(
&Initial {
theme: "night".into(),
ignition: "running".into(),
network: "unknown".into(),
},
12,
&night,
)
.expect("render ночь");
let (w, h, npx) = decode(&night);
assert_eq!((w, h), (1024, 600), "размер кадра");
// «не пустой» содержательно: кадр не одноцветный (нарисованы тайлы/текст, не только фон).
let first = npx[0];
assert!(
npx.iter().any(|&b| b != first),
"кадр одноцветный — рендер пустой"
);
// угол (0,0) = фон окна — ночью тёмный (#0e1014).
let (nr, ng, nb) = (npx[0], npx[1], npx[2]);
assert!(
nr < 64 && ng < 64 && nb < 64,
"ночной фон не тёмный: {nr},{ng},{nb}"
);
// --- день: тот же угол — светлый (#f4f5f7) — тема отражена ---
let day = dir.path().join("day.png");
render_screenshot(
&Initial {
theme: "day".into(),
..Default::default()
},
12,
&day,
)
.expect("render день");
let (_, _, dpx) = decode(&day);
let (dr, dg, db) = (dpx[0], dpx[1], dpx[2]);
assert!(
dr > 192 && dg > 192 && db > 192,
"дневной фон не светлый: {dr},{dg},{db}"
);
}