openpencil/crates/op-host-native/tests/teardown_idempotent.rs
Kayshen-X 1e0b3cab7a refactor(rust): rename native + desktop hosts to op-host-* crates
Phase 7.3 strangler reorg — rename the native widget host and the
desktop runner crates to the op- prefix. The desktop+native merge was
declined: keeping the library / binary split preserves the mobile-
checkable op-host-native lib (cargo check -p op-host-native on iOS /
Android, relied on by check-jian-boundaries.sh + the CI mobile job),
which a folded-in winit binary would break. A clean separate rename is
purely mechanical and the brief permits it.

- openpencil-shell-native -> op-host-native (lib op_host_native)
- openpencil-desktop -> op-host-desktop crate; the shipped executable
  keeps the stable openpencil-desktop [[bin]] name so release
  artifacts + external CLI integrations are unaffected
- every openpencil_shell_core:: path -> op_editor_ui::
- every openpencil_shell_native:: path -> op_host_native::
- doc-comment / manual-smoke note refs updated
2026-05-16 23:49:58 +08:00

46 lines
1.6 KiB
Rust

//! Spec v19 §3.3 / plan v7 Task 2 Step 16a:
//! `teardown` + lifecycle hooks must be idempotent.
//!
//! Three repeated calls of every hook (and `teardown`) must complete
//! without panic. Acceptance #6 covers this for the post-loop case;
//! this test pins the structural invariant on the empty-context
//! branch (the hardest path to get wrong is "already-`None` fields
//! shouldn't blow up").
mod common;
use common::setup_headless_context;
#[test]
fn teardown_three_times_no_panic() {
let mut ctx = setup_headless_context();
ctx.teardown().expect("first teardown");
ctx.teardown().expect("second teardown");
ctx.teardown().expect("third teardown");
}
#[test]
fn lifecycle_hooks_are_idempotent() {
// Mirrors spec §11.4 invariant + plan v7 Task 2 Step 16a:
// on_pause/on_resume/on_low_memory/teardown must each survive
// back-to-back invocation without panic.
let mut ctx = setup_headless_context();
ctx.on_pause().expect("on_pause #1");
ctx.on_pause().expect("on_pause #2");
ctx.on_resume(None).expect("on_resume #1");
ctx.on_resume(None).expect("on_resume #2");
ctx.on_low_memory();
ctx.on_low_memory();
ctx.teardown().expect("teardown #1");
ctx.teardown().expect("teardown #2");
}
#[test]
fn drop_after_teardown_is_safe() {
// Drop's `teardown` fallback must coexist with explicit teardown
// without double-free / panic (matches spec §3.3 Drop impl).
let mut ctx = setup_headless_context();
ctx.teardown().expect("explicit teardown");
drop(ctx); // implicit teardown — must be no-op
}