merge: resolve manuscript conflicts with latest main comments
This commit is contained in:
@@ -10,6 +10,8 @@ use std::{
|
||||
};
|
||||
|
||||
#[test]
|
||||
/// 模拟 reset 后旧画面仍含编号 2 的窗口,而新 Console VM 恰好再次分配编号 2。
|
||||
/// 关闭 session=0 的旧投影只能移除画面,不能按相同数字误关新 VM 的真实资源。
|
||||
fn preserved_picture_close_does_not_retire_same_number_in_fresh_console_vm() {
|
||||
let (_, mut picture) = endor_host::ReplSession::load(
|
||||
include_str!("../../../examples/windows.endor"),
|
||||
@@ -18,6 +20,7 @@ fn preserved_picture_close_does_not_retire_same_number_in_fresh_console_vm() {
|
||||
150,
|
||||
)
|
||||
.unwrap();
|
||||
// 把旧文档标成无活动 VM 的保留投影;编号相同也不能据此识别当前资源。
|
||||
picture.frame.session = 0;
|
||||
for view in picture.windows.values_mut() {
|
||||
view.frame.session = 0;
|
||||
@@ -80,6 +83,7 @@ fn preserved_picture_close_does_not_retire_same_number_in_fresh_console_vm() {
|
||||
);
|
||||
}
|
||||
|
||||
/// 一次测试独占的临时 HTTP 服务;端口由系统分配,持有线程句柄以便结束时解除阻塞并回收。
|
||||
struct TestServer {
|
||||
server: Arc<Server>,
|
||||
address: SocketAddr,
|
||||
@@ -87,6 +91,8 @@ struct TestServer {
|
||||
}
|
||||
|
||||
impl TestServer {
|
||||
/// 发送带明确 Content-Length 的 UTF-8 文本请求,使用 Connection: close 读至响应结束。
|
||||
/// 返回状态码、原始头和正文;10 秒超时使服务端死锁表现为测试失败而非无限等待。
|
||||
fn request(&self, method: &str, path: &str, body: &str) -> (u16, String, String) {
|
||||
let mut stream = TcpStream::connect(self.address).unwrap();
|
||||
stream
|
||||
@@ -105,6 +111,7 @@ impl TestServer {
|
||||
}
|
||||
|
||||
impl Drop for TestServer {
|
||||
/// 解除服务端 accept 等待并等待线程结束,避免测试之间遗留监听线程。
|
||||
fn drop(&mut self) {
|
||||
self.server.unblock();
|
||||
if let Some(api) = self.api.take() {
|
||||
@@ -114,6 +121,8 @@ impl Drop for TestServer {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 通过 HTTP 提交覆盖 moveTab 的 Page 子类,再发送带帧身份的 tab-drop。
|
||||
/// 检查源覆盖恰好调用一次、页面顺序改变,以及重放旧事件返回 409 而不损坏运行时。
|
||||
fn native_tab_drop_route_reorders_live_pages_and_rejects_reused_frames() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState::default()));
|
||||
@@ -157,6 +166,7 @@ fn native_tab_drop_route_reorders_live_pages_and_rejects_reused_frames() {
|
||||
}),
|
||||
}
|
||||
};
|
||||
// 保留同一事件正文用于重放测试,不能重新读取新 layout_version,否则就不是过期输入。
|
||||
let body = serde_json::to_string(&input).unwrap();
|
||||
let response = client.request("POST", "/v1/namespaces/app.main/events", &body);
|
||||
assert_eq!(response.0, 200, "{}", response.2);
|
||||
@@ -177,6 +187,8 @@ fn native_tab_drop_route_reorders_live_pages_and_rejects_reused_frames() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 零延时任务触发非法窗口尺寸,使后台回调先产生运行时故障。
|
||||
/// 之后多次 REPL 请求都应被拒绝,同时保留原始 stop/window size 诊断,而非用后续错误覆盖。
|
||||
fn background_failure_preserves_its_cause_after_rejected_console_input() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState::default()));
|
||||
@@ -215,6 +227,8 @@ fn background_failure_preserves_its_cause_after_rejected_console_input() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 依次创建未挂载对象、读取共享值、挂载、reset,再用完整 PUT 替换会话。
|
||||
/// 验证未挂载输入不增 revision,reset 清绑定但保留画面,成功 PUT 才切换到新 VM。
|
||||
fn console_keeps_memory_until_reset_or_complete_put_without_erasing_the_page() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState::default()));
|
||||
@@ -226,6 +240,7 @@ fn console_keeps_memory_until_reset_or_complete_put_without_erasing_the_page() {
|
||||
};
|
||||
let repl = "/v1/namespaces/app.main/repl";
|
||||
let code = "/v1/namespaces/app.main/code";
|
||||
// 先创建对象但不 render;返回值可以跨 REPL 请求复用,发布 revision 此时仍为 0。
|
||||
let created = client.request("POST", repl, "title = ui.text(\"中文\")");
|
||||
assert_eq!(created.0, 200, "{}", created.2);
|
||||
let created: serde_json::Value = serde_json::from_str(&created.2).unwrap();
|
||||
@@ -262,6 +277,8 @@ fn console_keeps_memory_until_reset_or_complete_put_without_erasing_the_page() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 经真实 HTTP 完成指南读取、PUT/GET、失败候选、再次 PUT 和 DELETE。
|
||||
/// 同时检查诊断位置、源文本/版本头、空文档、错误 namespace 与通知次数,确保仅成功发布改变版本。
|
||||
fn http_executes_real_endor_preserves_failed_updates_and_supports_delete() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState::default()));
|
||||
@@ -317,6 +334,8 @@ fn http_executes_real_endor_preserves_failed_updates_and_supports_delete() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 直接推进 worker 的 submission,模拟新候选已接受但尚未成功替换的时刻。
|
||||
/// 旧 VM 的 retained 仍可读;clear 撤销 namespace 后再刷新,才释放 live owner。
|
||||
fn queued_candidate_keeps_live_owner_until_delete_revokes_it() {
|
||||
let (runtime, document) = endor_host::ReplSession::load(
|
||||
"retained = ui.state(9)\npage = Page()\npage.render(ui.textOf(retained,\"value\"))",
|
||||
@@ -361,6 +380,8 @@ fn queued_candidate_keeps_live_owner_until_delete_revokes_it() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 用通知回调中的通道阻塞第一轮 worker,再使第二个 PUT 排队。
|
||||
/// 此时 GET/DELETE 仍应可用;释放 worker 后第一请求成功、被 DELETE 超越的候选返回 409,文档保持清空。
|
||||
fn http_delete_remains_available_and_invalidates_queued_put() {
|
||||
use std::{
|
||||
sync::{Mutex, mpsc},
|
||||
@@ -370,6 +391,7 @@ fn http_delete_remains_available_and_invalidates_queued_put() {
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState::default()));
|
||||
let (entered_tx, entered_rx) = mpsc::channel();
|
||||
let (release_tx, release_rx) = mpsc::channel();
|
||||
// 通知是共享闭包;用 Mutex 包装接收端使其满足线程共享约束,只阻塞第一次通知。
|
||||
let release_rx = Mutex::new(release_rx);
|
||||
let calls = AtomicUsize::new(0);
|
||||
let notify: Notify = Arc::new(move || {
|
||||
@@ -384,9 +406,11 @@ fn http_delete_remains_available_and_invalidates_queued_put() {
|
||||
server,
|
||||
api: Some(api),
|
||||
};
|
||||
// Unblock even if a test assertion fails, before scoped threads join.
|
||||
// 析构守卫在 scoped 线程 join 前释放阻塞通知;即使断言 panic 也不能让 worker 永久等通道。
|
||||
// 持有释放信号的发送端;离开作用域即通知 worker 继续,包括异常展开路径。
|
||||
struct Release(mpsc::Sender<()>);
|
||||
impl Drop for Release {
|
||||
// send 失败意味着接收端已结束,此时无需再阻塞测试清理。
|
||||
fn drop(&mut self) {
|
||||
let _ = self.0.send(());
|
||||
}
|
||||
@@ -406,6 +430,7 @@ fn http_delete_remains_available_and_invalidates_queued_put() {
|
||||
}
|
||||
assert_eq!(client.request("GET", route, "").2, example);
|
||||
assert_eq!(client.request("DELETE", route, "").0, 200);
|
||||
// DELETE 已同步清空文档并提升提交身份,现在解除第一请求通知阻塞,让排队候选观察到失效。
|
||||
drop(release);
|
||||
assert_eq!(first.join().unwrap().0, 200);
|
||||
assert_eq!(pending.join().unwrap().0, 409);
|
||||
@@ -415,6 +440,7 @@ fn http_delete_remains_available_and_invalidates_queued_put() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 检查 code 路径解析只接受完整的单段 namespace;缺少后缀和嵌套斜线均返回 None。
|
||||
fn extracts_namespace_only_from_code_routes() {
|
||||
assert_eq!(
|
||||
namespace_from_path("/v1/namespaces/app.main/code"),
|
||||
@@ -425,6 +451,7 @@ fn extracts_namespace_only_from_code_routes() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 对生成的指南做契约回归:保留代码 CRUD、可运行示例和当前地址,移除旧 export scene/display_list 暴露。
|
||||
fn guide_contains_only_code_crud_routes() {
|
||||
let guide = agent_guide(&NamespaceState::default());
|
||||
assert!(guide.contains("Code CRUD"));
|
||||
@@ -435,6 +462,8 @@ fn guide_contains_only_code_crud_routes() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 创建已经就绪的定时任务,先按前台模式刷新再读取计数,确保输入前不抢跑回调。
|
||||
/// 随后允许空闲刷新并推进待完成工作,计数应从 0 变为 1。
|
||||
fn foreground_refresh_does_not_deliver_new_callbacks_ahead_of_input() {
|
||||
let source = r#"
|
||||
class Action {
|
||||
@@ -459,6 +488,7 @@ page.render(TextBlock("input priority"))
|
||||
next_session: 801,
|
||||
};
|
||||
let notify: Notify = Arc::new(|| {});
|
||||
// 前台模式仅处理输入前必须收敛的刷新,不引入新的任务交付。
|
||||
worker.refresh_viewport_mode(&namespace, ¬ify, true);
|
||||
let value = worker
|
||||
.repl
|
||||
@@ -484,6 +514,8 @@ page.render(TextBlock("input priority"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 在负世界坐标放置按钮,通过 HTTP 平移/锚定缩放,再发送世界坐标 click。
|
||||
/// 检查相机数值、旧帧重放 409、按钮计数,以及零倍率 400 不导致 VM fault。
|
||||
fn canvas_camera_crosses_real_http_route_and_preserves_negative_world_clicks() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState {
|
||||
@@ -586,6 +618,8 @@ page.render(button)
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 给局部 CanvasViewport 添加父内边距和自身左边框,检验页面坐标到内容相机锚点的转换。
|
||||
/// 成功平移/缩放后核对相机状态;边框、边界与视口外锚点均须返回 400,保留健康运行时。
|
||||
fn explicit_viewport_camera_crosses_http_and_rejects_bad_anchors_without_faulting() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState {
|
||||
@@ -658,6 +692,7 @@ fn explicit_viewport_camera_crosses_http_and_rejects_bad_anchors_without_faultin
|
||||
expected
|
||||
);
|
||||
}
|
||||
// 分别覆盖左边框、右侧边界及远离视口的锚点,避免只校验整个窗口范围。
|
||||
for point in [[25.0, 30.0], [220.0, 30.0], [700.0, 700.0]] {
|
||||
let response = client.request(
|
||||
"POST",
|
||||
@@ -670,6 +705,8 @@ fn explicit_viewport_camera_crosses_http_and_rejects_bad_anchors_without_faultin
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 通过真实 HTTP 检查透视缩放/orbit、正交缩放,再替换为源实现 PanOnly。
|
||||
/// 比较相机数值与自定义控制器结果,并验证视口外锚点和禁用控制器的拒绝状态不会停掉 VM。
|
||||
fn scene3d_controllers_cross_http_and_can_be_replaced_by_endor_code() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState {
|
||||
@@ -761,6 +798,8 @@ fn scene3d_controllers_cross_http_and_can_be_replaced_by_endor_code() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// 在旋转的 Panel3D 上嵌入二维 CanvasViewport,经射线/UV 映射解释相机事件。
|
||||
/// 缺失指针必须拒绝;带指针的平移/缩放只改变面板内相机,外层三维相机保持原值。
|
||||
fn canvas_in_world_ui_uses_ray_uv_coordinates_for_real_http_camera_events() {
|
||||
let server = Arc::new(Server::http("127.0.0.1:0").unwrap());
|
||||
let namespace = Arc::new(RwLock::new(NamespaceState {
|
||||
@@ -833,6 +872,7 @@ fn canvas_in_world_ui_uses_ray_uv_coordinates_for_real_http_camera_events() {
|
||||
.unwrap()
|
||||
};
|
||||
let pan = value("canvas.camera.x");
|
||||
// 面板倾斜后的投影位移不等于 UI 内部位移,不能把屏幕 delta 直接写入局部相机。
|
||||
assert!(pan > 10.);
|
||||
assert_eq!(value("view.camera.x"), 0.);
|
||||
let response = client.request(
|
||||
|
||||
Reference in New Issue
Block a user