docs: 补齐各模块局部回归测试的输入与断言说明

This commit is contained in:
2026-11-12 10:34:22 +08:00
parent e315172ce1
commit 45ff965b01
12 changed files with 29 additions and 2 deletions
+4
View File
@@ -821,6 +821,7 @@ pub fn parse_color(value: &str) -> Result<Color> {
#[cfg(test)]
mod tests {
#[test]
/// 旧文字JSON缺wrap_bounds仍走不换行命令;合法边界随DPI缩放,负宽度在发布验证中拒绝。
fn text_wrap_bounds_validate_and_legacy_text_remains_unwrapped() {
let mut command: RenderCommand = serde_json::from_str(
r##"{"type":"text","x":0,"y":0,"value":"text","size":1,"color":"#000000"}"##,
@@ -846,6 +847,7 @@ mod tests {
use super::*;
#[test]
/// 默认空文档可通过验证且没有结构根/绘图命令,支持初始状态与清空发布。
fn empty_document_is_valid_and_blank() {
let document = RenderDocument::default();
document.validate().unwrap();
@@ -854,6 +856,7 @@ mod tests {
}
#[test]
/// 检查六位色默认alpha255,八位色按RRGGBBAA解码,避免将末尾透明度当成首通道。
fn parses_rgb_and_rgba_colors() {
assert_eq!(
parse_color("#102030").unwrap(),
@@ -870,6 +873,7 @@ mod tests {
mod window_projection_tests {
use super::*;
#[test]
/// 同会话窗口命令可累积,新会话发布先丢旧命令;clear重置runtime_session并清空所有原生命令。
fn replacement_and_reset_discard_old_window_projections() {
let mut state = NamespaceState::default();
let command = crate::window::Command {
+1
View File
@@ -189,6 +189,7 @@ mod tests {
use super::*;
use crate::{layout::Size, ui_bridge::UiKind};
#[test]
/// 同VM中两Presentation分别布局共享树,尺寸/出现身份独立;读观察可追踪共享对象但不能增添GC根。
fn independent_view_geometry_and_shared_vm_reads_do_not_add_roots() {
// 测试在同一 VM 中建立两个 Presentation,验证各自布局尺寸/出现 ID 独立,读取依赖共享业务对象。
let mut vm = Vm::new().unwrap();
+1
View File
@@ -48,6 +48,7 @@ mod tests {
use super::*;
#[test]
/// 矩形左上/右下边界均包含,刚越过左边界的点排除,固定基础命中边界策略。
fn rectangle_contains_edges_but_not_outside_points() {
// 同时验证左上和右下边界被包含,以及仅越过左边界的小偏差也会被排除。
let rect = Rect::new(10.0, 20.0, 30.0, 40.0);
+2
View File
@@ -168,6 +168,7 @@ mod tests {
use super::*;
#[test]
/// 检查页面相机的往返变换,缩放时反求 pan 使指针下世界点固定,并按视口验证世界点可见性。
fn camera_inverse_and_cursor_anchored_zoom_preserve_world_position() {
// 先验证相机正逆变换,再改变 zoom 并反求 pan,保证指针下的世界点不发生漂移。
let mut view = Surface::new(Mode::Canvas);
@@ -199,6 +200,7 @@ mod tests {
}
#[test]
/// 纵向页面将过大偏移夹到内容减视口范围;内容缩短到视口以内后偏移必须归零,横向偏移也禁用。
fn finite_pages_clamp_after_content_shrinks() {
// 先将 900 的旧偏移钳到 800-600=200,再缩短内容至视口以内,确认偏移回零。
let mut view = Surface::new(Mode::Vertical);
+5
View File
@@ -631,6 +631,7 @@ fn render_aspect(
mod tests {
use super::*;
#[test]
/// 构造99个远离视锥的网格,宽阶段只保留正中实例;有/无预存BVH的精确拾取身份与距离必须一致。
fn spatial_index_culls_offscreen_meshes_and_preserves_ray_pick() {
let mut view = view(Geometry::cuboid(1., 1., 1.));
let scene = Arc::make_mut(&mut view.scene);
@@ -657,6 +658,7 @@ mod tests {
assert_eq!(indexed.identity, fallback.identity);
assert_eq!(indexed.distance, fallback.distance);
}
/// 构造测试最小视图:默认相机、原点红网格、纯环境光与白背景,不依赖业务VM或外部光源。
fn view(geometry: Geometry) -> View {
View {
camera: Camera::default(),
@@ -676,6 +678,7 @@ mod tests {
}
}
#[test]
/// 跨越近裁剪面的三角形仍应产生像素,射线命中受同一 near/far 约束;缩短远面后命中消失。
fn triangles_crossing_near_plane_are_clipped_and_pick_uses_the_same_range() {
let mut view = view(Geometry {
uv: vec![],
@@ -700,6 +703,7 @@ mod tests {
assert!(view.pick(0.5, 0.5, 1.).is_none());
}
#[test]
/// 验证局部TRS先于父变换作用于点;正交投影不同水平采样的射线方向相同但原点按可见宽度平移。
fn group_transform_order_and_orthographic_rays_are_consistent() {
let local = Matrix::trs(
Vec3::new(1., 0., 0.),
@@ -723,6 +727,7 @@ mod tests {
assert_eq!(right.origin.x - left.origin.x, 2.);
}
#[test]
/// 同一不可变视图/尺寸/aspect复用像素Arc;改变aspect重绘,超像素预算或退化相机必须拒绝。
fn surface_cache_reuses_immutable_frames_and_rejects_invalid_resources() {
let view = Arc::new(view(Geometry::cuboid(1., 1., 1.)));
let first = surface(&view, 64, 64, 1.).unwrap();
+3
View File
@@ -358,6 +358,7 @@ mod tests {
use super::*;
#[test]
/// 画长文字进入嵌套裁剪,逐像素检查多行与裁剪交集;PopClip后的蓝矩形验证后续命令不沿用旧clip。
fn wrapped_text_uses_multiple_lines_respects_nested_clips_and_restores_the_clip() {
let mut list = DisplayList::default();
list.push(DrawCommand::Clear(Color::rgb(255, 255, 255)));
@@ -398,6 +399,7 @@ mod tests {
}
#[test]
/// 在黑底4x4缓冲画2x2红矩形,核对内部像素变红、外部仍黑,覆盖最小坐标/打包路径。
fn renderer_changes_pixels_inside_a_rectangle() {
let mut list = DisplayList::default();
list.push(DrawCommand::Clear(Color::rgb(0, 0, 0)));
@@ -413,6 +415,7 @@ mod tests {
}
#[test]
/// 半透明白字叠黑底必须含中间抗锯齿值且通道不超128;alpha=0的文字完全不改像素。
fn text_edges_are_antialiased_and_respect_opacity() {
let mut list = DisplayList::default();
list.push(DrawCommand::Clear(Color::rgb(0, 0, 0)));
+5
View File
@@ -404,6 +404,7 @@ mod tests {
use unicode_segmentation::UnicodeSegmentation;
#[test]
/// 用一万倍缩放检验绘制回调被限制在32x32裁剪内,同时检查字形缓存字号不超过512以控制中间资源。
fn extreme_zoom_rasterizes_only_the_clip_with_bounded_glyph_sizes() {
// 极端缩放测试限制实际回调在 32×32 裁剪区,同时检查缓存字号不超过 512。
let mut count = 0;
@@ -431,6 +432,7 @@ mod tests {
}
#[test]
/// 混合中文、单词、长标识符、组合字符和emoji,检查窄宽约束分别换行/缓存,且不污染无约束自然测量。
fn width_sensitive_shaping_wraps_words_and_clusters_without_changing_intrinsic_metrics() {
let style = TextStyle::ui(1);
// 混合中文、长英文词、组合字符和 emoji,验证窄/宽换行缓存隔离且不改变无换行测量结果。
@@ -451,6 +453,7 @@ mod tests {
}
#[test]
/// 比较比例字体宽窄字母、等宽字体、多行高度与缩放比例,证明尺寸来自字体整形而非按字符数估计。
fn proportional_metrics_monospace_and_multiline_height_come_from_the_fonts() {
// 用宽窄拉丁字母验证比例字体差异、等宽字体一致性、多行行高和同比例缩放。
let ui = TextStyle::ui(1);
@@ -463,6 +466,7 @@ mod tests {
}
#[test]
/// 普通和代码字体都需为四个中文字符产生非零glyph_id;依赖测试环境安装可用CJK回退字体。
fn system_fallback_shapes_chinese_without_missing_glyphs() {
let mut engine = engine().lock().unwrap();
// 两种字体风格都要求四个中文字符生成非零 glyph_id,确保系统回退实际找到了字形。
@@ -480,6 +484,7 @@ mod tests {
}
#[test]
/// 按实际测量宽度截断带组合字符的文本,结果需是原文字素边界前缀加省略号,且整体不超宽。
fn ellipsis_fits_actual_width_and_preserves_graphemes() {
let style = TextStyle::code();
let short_source = "let s = ui.text(\"中文字体\")";
+2 -2
View File
@@ -386,9 +386,9 @@ pub fn project(
mod tests {
use super::*;
#[test]
/// 以实际两次点变换核对嵌套组合顺序、逆变换和矩形尺寸,并允许大相机投影平移而非套用较小世界坐标限额。
fn nested_views_compose_and_invert_without_changing_layout_coordinates() {
// Camera coordinates may be +/-1e6 at zoom 100. Their projected
// translation must not inherit the smaller world-coordinate limit.
// 相机坐标可达正负一百万,缩放100后投影平移可达一亿;不能继续使用较小的世界坐标限制。
assert!(
Transform {
zoom: 100.,
+1
View File
@@ -109,6 +109,7 @@ fn cell(value: f32) -> i32 {
mod tests {
use super::*;
#[test]
/// 200个分散节点只返回局部网格候选;覆盖海量格的巨大前景节点进入后备集合后仍按前景顺序参与精确命中。
fn screen_grid_limits_candidates_and_keeps_huge_rectangles_hittable() {
let (_, document) = crate::endor_host::ReplSession::load(
"page=Page()\npage.render(Button(\"hit\"))",
+3
View File
@@ -301,6 +301,7 @@ fn send(input: &Input) -> anyhow::Result<()> {
mod tests {
use super::*;
#[test]
/// 重复消费旧帧捕获的连续 pan,允许刷新到当前版本且保留每条 delta;离散 click 不改版本,会话/目标替换拒绝手势。
fn queued_native_pan_keeps_each_delta_and_rejects_replaced_targets() {
let (mut session, document) = crate::endor_host::ReplSession::load(
"view=CanvasViewport(200,100,Scene2D(RectBlock(20,20,0)))\npage=Page()\npage.render(view)",
@@ -352,6 +353,7 @@ mod tests {
assert!(!refresh_continuous_input(&mut original.clone(), &current));
}
#[test]
/// 对 tab-drop 元数据、位置上限与 tab-detach 桌面坐标逐项改坏,检查辅助字段只在对应事件类型和预算内合法。
fn drop_metadata_is_scoped_to_its_event_kind_and_bounded() {
let mut input:Input=serde_json::from_str(r#"{"window":1,"session":1,"layout_version":2,"mount":3,"kind":"tab-drop","tab_drop":{"window":2,"layout_version":4,"index":1}}"#).unwrap();
assert!(input.validate().is_ok());
@@ -369,6 +371,7 @@ mod tests {
}
#[test]
/// 旧 JSON 未带 window 时默认主窗口以保持兼容;显式 window=0 仍是非法资源,不能当成默认值。
fn legacy_event_window_defaults_but_zero_is_never_a_resource() {
let mut input: Input =
serde_json::from_str(r#"{"session":1,"layout_version":2,"mount":3,"kind":"focus"}"#)
+1
View File
@@ -155,6 +155,7 @@ mod tests {
use super::*;
use endor_core::{ExecutionLimits, Vm};
#[test]
/// 生成超过单字节跳距的循环,false 条件正常返回,true 条件只耗尽指令预算而非一帧预算;释放后对象数归零。
fn repeated_bytecode_handles_extended_operands_and_keeps_one_frame() {
// 300 个 Nop 强制跨越单字节跳转距离:false 应正常退出;true 应耗尽指令预算,
// 而非耗尽帧预算,从而验证循环确实复用同一个执行帧。
+1
View File
@@ -169,6 +169,7 @@ impl State {
mod tests {
use super::*;
#[test]
/// 先展开绘制再折叠重绘,检查交互目标只剩 Toggle、页面左侧让宽归零,其他区域不再拦截指针。
fn collapsing_removes_every_host_target_except_the_toggle() {
let mut sidebar = State::default();
sidebar.draw(&[], 800.0, theme::PALETTE[0]);