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>
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
//! 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}"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user