docs(tests): correct ownership and reader failure explanations
This commit is contained in:
@@ -60,7 +60,7 @@ fn strings_and_split_utf8_streams_produce_the_same_ast_and_exact_text() {
|
||||
// Reader 不剥除 BOM,词法器看到原始开头字符后才决定是否接受。
|
||||
let bom = EndorReader::from_stream("<bom>", Cursor::new("\u{feff}1".as_bytes())).unwrap();
|
||||
assert_eq!(bom.text(), "\u{feff}1");
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// BOM 保留了三字节 UTF-8 编码,词法错误范围应恰好覆盖它,而不是 Reader 静默剥除。
|
||||
let error = lex_source(&bom).unwrap_err();
|
||||
assert_eq!(error.diagnostic.kind, DiagnosticKind::Lexical);
|
||||
assert_eq!(error.diagnostic.span.end, 3);
|
||||
@@ -71,7 +71,7 @@ fn strings_and_split_utf8_streams_produce_the_same_ast_and_exact_text() {
|
||||
fn source_diagnostics_keep_session_names_unicode_locations_and_original_offsets() {
|
||||
// 建立带来源名的原文快照,后续错误必须定位这份文本而非临时转换后的内容。
|
||||
let source = EndorReader::from_text("<session:one/input:2>", "\"日\" + )");
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 右括号没有对应表达式,语法失败仍需按含日文字符的完整原文计算列号和跨度。
|
||||
let error = parse_source_expression(&source).unwrap_err();
|
||||
assert_eq!(error.location.line, 1);
|
||||
assert_eq!(error.location.column, 7);
|
||||
@@ -87,7 +87,7 @@ fn source_diagnostics_keep_session_names_unicode_locations_and_original_offsets(
|
||||
|
||||
// 建立带来源名的原文快照,后续错误必须定位这份文本而非临时转换后的内容。
|
||||
let source = EndorReader::from_text("<editor>", "\r\n\"日\\q\"");
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 第二行字符串含不支持的反斜杠转义,词法错误应保留 CRLF 后的行号和 Unicode 列。
|
||||
let error = lex_source(&source).unwrap_err();
|
||||
assert_eq!(error.diagnostic.kind, DiagnosticKind::Lexical);
|
||||
assert_eq!(error.location.line, 2);
|
||||
@@ -100,7 +100,7 @@ fn source_diagnostics_keep_session_names_unicode_locations_and_original_offsets(
|
||||
fn incomplete_or_invalid_utf8_is_rejected_without_replacement_characters() {
|
||||
// 覆盖无效起始字节、不完整 UTF-8 序列,以及一个合法 ASCII 前缀后的错误。
|
||||
for bytes in [vec![0xff], vec![0xe6, 0x97], vec![b'a', 0xff]] {
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 即使每次只读一个字节,聚合后非法 UTF-8 仍须作为读取编码错误拒绝,不能交给 lexer。
|
||||
let error = EndorReader::from_stream("<bytes>", OneByte(Cursor::new(bytes))).unwrap_err();
|
||||
assert!(matches!(error.kind, SourceReadErrorKind::InvalidUtf8(_)));
|
||||
assert!(error.to_string().contains("<bytes>: invalid UTF-8 at byte"));
|
||||
@@ -131,7 +131,7 @@ impl Read for FailingStream {
|
||||
#[test]
|
||||
/// 由 Rust 测试框架调用:读到一个有效字节后发生 IO 失败时仍整体失败,不向解析器提供半份快照。
|
||||
fn a_failed_stream_never_returns_partially_read_source() {
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 流先返回 x 后报错,Reader 必须整体传播 IO 失败,不能把已读前缀作为成功源码。
|
||||
let error = EndorReader::from_stream("<failed>", FailingStream(false)).unwrap_err();
|
||||
assert!(matches!(error.kind, SourceReadErrorKind::Io(_)));
|
||||
assert!(error.to_string().contains("stream failure"));
|
||||
@@ -182,13 +182,13 @@ fn file_sources_preserve_the_path_and_read_errors_are_not_lexical_errors() {
|
||||
assert!(parse_source(&source).is_ok());
|
||||
// 单字节 0xff 不是 UTF-8,打开文件成功后应在解码阶段拒绝。
|
||||
let invalid = SourceFile::new(&[0xff]);
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 文件能打开但 0xff 无法解码,错误应带该文件路径并归为 InvalidUtf8。
|
||||
let error = EndorReader::from_file(&invalid.0).unwrap_err();
|
||||
assert!(matches!(error.kind, SourceReadErrorKind::InvalidUtf8(_)));
|
||||
assert!(error.to_string().contains(&invalid.0.display().to_string()));
|
||||
// 从基线路径换一个未创建的扩展名,验证文件不存在作为 IO 错误暴露。
|
||||
let missing = file.0.with_extension("missing");
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 未创建的路径应返回 NotFound IO 原因,不能误分类为词法或编码失败。
|
||||
let error = EndorReader::from_file(&missing).unwrap_err();
|
||||
// 必须能解构出原始 IO 错误,之后检查 NotFound 或 PermissionDenied 的具体种类。
|
||||
let SourceReadErrorKind::Io(error) = error.kind else {
|
||||
@@ -282,7 +282,7 @@ fn uri_snapshots_require_a_revision_and_preserve_it_in_source_diagnostics() {
|
||||
};
|
||||
// 建立带来源名的原文快照,后续错误必须定位这份文本而非临时转换后的内容。
|
||||
let source = EndorReader::from_endor(uri, &provider).unwrap();
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 已解析 URI 的字节是 UTF-8 但函数语法无效,解析诊断仍须显示 rev-one 来源。
|
||||
let error = parse_source(&source).unwrap_err();
|
||||
assert_eq!(error.origin, source.origin().clone());
|
||||
assert!(error.to_string().contains("[revision rev-one]"));
|
||||
@@ -302,7 +302,7 @@ fn uri_snapshots_require_a_revision_and_preserve_it_in_source_diagnostics() {
|
||||
// 借用测试静态字节,load 时复制给 Reader 解码。
|
||||
bytes: &[0xff],
|
||||
};
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 宿主已选择 rev-two 后才遇到非法 UTF-8,解码失败也必须保留该实际版本身份。
|
||||
let error = EndorReader::from_endor(uri, &invalid).unwrap_err();
|
||||
assert!(matches!(error.kind, SourceReadErrorKind::InvalidUtf8(_)));
|
||||
assert!(error.to_string().contains("[revision rev-two]"));
|
||||
@@ -344,7 +344,7 @@ fn invalid_uris_do_not_call_the_provider_and_denials_do_not_fall_back_to_files()
|
||||
));
|
||||
}
|
||||
assert_eq!(provider.calls.get(), 0);
|
||||
// 当前用例要求此阶段失败,随后检查具体类别、来源或底层原因,避免仅接受任意错误。
|
||||
// 基础 URI 合法但宿主拒绝访问,Reader 应保留 PermissionDenied,不尝试文件回退。
|
||||
let error = EndorReader::from_endor("endor:anywhere", &DeniedProvider).unwrap_err();
|
||||
// 必须能解构出原始 IO 错误,之后检查 NotFound 或 PermissionDenied 的具体种类。
|
||||
let SourceReadErrorKind::Io(error) = error.kind else {
|
||||
|
||||
@@ -16,7 +16,7 @@ impl ObjectPayload for Proxy {
|
||||
fn kind_name(&self) -> &'static str {
|
||||
"proxy"
|
||||
}
|
||||
/// Heap 分配/替换时记录一条拥有边,确保被代理存储或自引用 Cell 存活。
|
||||
/// Heap 分配/替换 Proxy 时记录后端存储边,释放门面或改指后端时归还该边。
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
visit(self.0);
|
||||
}
|
||||
@@ -107,7 +107,7 @@ fn cyclic_and_foreign_attribute_storage_fail_without_mutating_fields() {
|
||||
// 异堆即使槽号相同也必须报 ForeignHandle,不当成普通缺字段。
|
||||
let other = ObjectStore::new().unwrap();
|
||||
assert_eq!(other.attribute_path(proxy), Err(VmError::ForeignHandle));
|
||||
// 归还后端外部根后仍由 Proxy 的载荷边维持存活。
|
||||
// Proxy 已改为自引用,不再持有 storage;归还其唯一外部根即可回收后端。
|
||||
store.release(storage).unwrap();
|
||||
store.release(proxy).unwrap();
|
||||
// 外部根释放后只有 Proxy 自环残留,显式回收恰好一个对象。
|
||||
@@ -130,7 +130,7 @@ impl ObjectPayload for ReflectiveType {
|
||||
fn allows_attributes(&self) -> bool {
|
||||
true
|
||||
}
|
||||
/// Heap 分配/替换时记录一条拥有边,确保被代理存储或自引用 Cell 存活。
|
||||
/// Heap 分配反射载荷时记录 self_cell 强边,与稍后 Cell 反向持边构成测试环。
|
||||
fn trace(&self, visit: &mut dyn FnMut(ObjectRef)) {
|
||||
visit(self.0);
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ fn read_only_scopes_block_existing_container_and_type_writes_but_allow_fresh_res
|
||||
store.set_attr(fresh, "value", objects::Value::Nil).unwrap();
|
||||
// Record 不原地赋值,只读引用重绑定可以返回 false 而不修改旧对象。
|
||||
assert!(!store.assign_from(record, fresh).unwrap());
|
||||
// 嵌套层快照/区间只影响自身,结束必须按 LIFO。
|
||||
// 内层重新快照所有存活对象,fresh 此时也受保护;结束必须按 LIFO。
|
||||
let nested = store.begin_read_only();
|
||||
assert!(
|
||||
store
|
||||
@@ -206,12 +206,12 @@ fn read_tracking_regions_repeat_shared_reads_and_preserve_enclosing_dependencies
|
||||
// 第二对象仅在内层首次读取,外层集合也必须包含它。
|
||||
let second = store.new_object().unwrap();
|
||||
assert!(store.begin_read_tracking().is_err());
|
||||
// 进入时快照已有对象,之后分配的 fresh 不在本层保护集合。
|
||||
// 进入时把 first、second 列为已有对象,后面的读取才会成为本层依赖。
|
||||
let scope = store.begin_read_only();
|
||||
// 外层区间开始后,其内所有读取都要包含在最终结果中。
|
||||
let outer = store.begin_read_tracking().unwrap();
|
||||
store.object(first).unwrap();
|
||||
// 嵌套层快照/区间只影响自身,结束必须按 LIFO。
|
||||
// 子区间嵌套不另建对象保护快照;其读取同时传播给 outer,结束仍须按 LIFO。
|
||||
let nested = store.begin_read_tracking().unwrap();
|
||||
store.object(first).unwrap();
|
||||
store.object(second).unwrap();
|
||||
@@ -232,8 +232,8 @@ fn read_tracking_regions_repeat_shared_reads_and_preserve_enclosing_dependencies
|
||||
// 整个调用结束后未完成区间已丢弃,不能再拿旧令牌结束。
|
||||
assert!(store.end_read_tracking(1).is_err());
|
||||
assert!(!store.is_read_only());
|
||||
// 移除一函数仅减一条代码边,另一函数仍保留代码和常量。
|
||||
// first 没有内部拥有边,归还唯一外部根后可立即回收;借用读取记录不会保活它。
|
||||
store.release(first).unwrap();
|
||||
// 最后一个函数释放后,代码/全局/常量无根链一起回收。
|
||||
// second 同样独立归还唯一根;本场景没有函数、代码对象或对象互引环。
|
||||
store.release(second).unwrap();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user