diff --git a/crates/oxide_runtime_api/src/lib.rs b/crates/oxide_runtime_api/src/lib.rs index b99bc55c..6cb297e1 100644 --- a/crates/oxide_runtime_api/src/lib.rs +++ b/crates/oxide_runtime_api/src/lib.rs @@ -18,9 +18,6 @@ mod host; mod native_result; mod runtime_api_log; -#[cfg(test)] -use oxide_types::value::JsValue; - pub use coercion::{ abstract_eq, bigint_data, bigint_to_f64, bigint_to_string, format_error_message, js_number_to_string, push_to_string, push_units_to, relational_compare, same_value, same_value_zero, strict_equality, string_concat, @@ -31,105 +28,3 @@ pub use coercion::{ }; pub use host::VmHost; pub use native_result::NativeResult; - -#[cfg(test)] -mod tests { - use super::*; - - fn fmt(d: f64) -> String { - js_number_to_string(d) - } - - #[test] - fn number_to_string_boundaries() { - assert_eq!(fmt(1e21), "1e+21"); - assert_eq!(fmt(1e22), "1e+22"); - assert_eq!(fmt(1e20), "100000000000000000000"); - assert_eq!(fmt(123.45), "123.45"); - assert_eq!(fmt(0.000001), "0.000001"); - assert_eq!(fmt(0.0000001), "1e-7"); - assert_eq!(fmt(1e15), "1000000000000000"); - assert_eq!(fmt(0.1 + 0.2), "0.30000000000000004"); - assert_eq!(fmt(0.0), "0"); - assert_eq!(fmt(-0.0), "0"); - assert_eq!(fmt(-123.45), "-123.45"); - assert_eq!(fmt(-1e21), "-1e+21"); - assert_eq!(fmt(1e16), "10000000000000000"); - assert_eq!(fmt(f64::MAX), "1.7976931348623157e+308"); - assert_eq!(fmt(5e-324), "5e-324"); - assert_eq!(fmt(1.5e20), "150000000000000000000"); - assert_eq!(fmt(f64::NAN), "NaN"); - assert_eq!(fmt(f64::INFINITY), "Infinity"); - assert_eq!(fmt(f64::NEG_INFINITY), "-Infinity"); - // 整数 double 快路径:2^53 内直写(与最短表示一致)。 - assert_eq!(fmt(42.0), "42"); - assert_eq!(fmt(-42.0), "-42"); - assert_eq!(fmt(123.0), "123"); - assert_eq!(fmt(2.0), "2"); - assert_eq!(fmt(1000000.0), "1000000"); - assert_eq!(fmt(-1000000.0), "-1000000"); - // 2^53 边界:等于 2^53 落 ryu 路径,输出仍为定点。 - assert_eq!(fmt(9_007_199_254_740_992.0), "9007199254740992"); - // 2^63:精确值 9223372036854775808 的最短表示为 17 位舍入 - // "9223372036854776000"(规范输出,非精确值)。 - assert_eq!(fmt(2f64.powi(63)), "9223372036854776000"); - } - - #[test] - fn push_to_string_matches_to_string() { - let samples: Vec = vec![ - 0.0, - -0.0, - 1.0, - -1.0, - 42.0, - -42.0, - std::f64::consts::PI, - 1e15, - 1e16, - 1e20, - 1e21, - 1e22, - 9_007_199_254_740_992.0, - 2f64.powi(63), - f64::MAX, - 5e-324, - 0.1 + 0.2, - 123.45, - -123.45, - 1e-7, - 0.000001, - f64::NAN, - f64::INFINITY, - f64::NEG_INFINITY, - ]; - for &d in &samples { - let mut buf = String::new(); - push_to_string(JsValue::float(d), &mut buf); - assert_eq!(buf, to_string(JsValue::float(d)), "double {d} 的 push/to_string 输出不一致"); - assert_eq!(buf, js_number_to_string(d), "double {d} 的 push/js_number_to_string 输出不一致"); - } - } - - #[test] - fn push_to_string_negative_double_appends_to_nonempty_buffer() { - // 追加语义回归:非空前缀下负数 double 的负号必须紧跟当前数值,不能插到 - // 整个缓冲最前。覆盖整数快路径、ryu 定点(≥2^53)、ryu 科学计数与边界值。 - let samples: Vec = vec![ - -42.0, // 整数快路径 - -1.5, // ryu 定点 - -9_007_199_254_740_994.0, // 2^53+2:≥2^53 可精确表示整数,落 ryu 路径 - -2f64.powi(63), // ryu 科学计数(规范最短表示) - -1e21, // ryu 科学计数 - -1e-7, // ryu 科学计数(小指数) - -f64::MAX, - -5e-324, - ]; - for &d in &samples { - let mut buf = String::from("pre"); - push_to_string(JsValue::float(d), &mut buf); - let expected = format!("pre{}", js_number_to_string(d)); - assert_eq!(buf, expected, "非空前缀 + double {d} 的追加语义破坏,实际 {buf}"); - } - } -} diff --git a/crates/oxide_runtime_api/tests/coercion_tests.rs b/crates/oxide_runtime_api/tests/coercion_tests.rs new file mode 100644 index 00000000..c83665c8 --- /dev/null +++ b/crates/oxide_runtime_api/tests/coercion_tests.rs @@ -0,0 +1,101 @@ +//! 公开强转 API 集成测试:数字字符串化的边界断言与 push/to_string 输出一致性。 + +use oxide_runtime_api::{js_number_to_string, push_to_string, to_string}; +use oxide_types::value::JsValue; + +fn fmt(d: f64) -> String { + js_number_to_string(d) +} + +#[test] +fn number_to_string_boundaries() { + assert_eq!(fmt(1e21), "1e+21"); + assert_eq!(fmt(1e22), "1e+22"); + assert_eq!(fmt(1e20), "100000000000000000000"); + assert_eq!(fmt(123.45), "123.45"); + assert_eq!(fmt(0.000001), "0.000001"); + assert_eq!(fmt(0.0000001), "1e-7"); + assert_eq!(fmt(1e15), "1000000000000000"); + assert_eq!(fmt(0.1 + 0.2), "0.30000000000000004"); + assert_eq!(fmt(0.0), "0"); + assert_eq!(fmt(-0.0), "0"); + assert_eq!(fmt(-123.45), "-123.45"); + assert_eq!(fmt(-1e21), "-1e+21"); + assert_eq!(fmt(1e16), "10000000000000000"); + assert_eq!(fmt(f64::MAX), "1.7976931348623157e+308"); + assert_eq!(fmt(5e-324), "5e-324"); + assert_eq!(fmt(1.5e20), "150000000000000000000"); + assert_eq!(fmt(f64::NAN), "NaN"); + assert_eq!(fmt(f64::INFINITY), "Infinity"); + assert_eq!(fmt(f64::NEG_INFINITY), "-Infinity"); + // 整数 double 快路径:2^53 内直写(与最短表示一致)。 + assert_eq!(fmt(42.0), "42"); + assert_eq!(fmt(-42.0), "-42"); + assert_eq!(fmt(123.0), "123"); + assert_eq!(fmt(2.0), "2"); + assert_eq!(fmt(1000000.0), "1000000"); + assert_eq!(fmt(-1000000.0), "-1000000"); + // 2^53 边界:等于 2^53 落 ryu 路径,输出仍为定点。 + assert_eq!(fmt(9_007_199_254_740_992.0), "9007199254740992"); + // 2^63:精确值 9223372036854775808 的最短表示为 17 位舍入 + // "9223372036854776000"(规范输出,非精确值)。 + assert_eq!(fmt(2f64.powi(63)), "9223372036854776000"); +} + +#[test] +fn push_to_string_matches_to_string() { + let samples: Vec = vec![ + 0.0, + -0.0, + 1.0, + -1.0, + 42.0, + -42.0, + std::f64::consts::PI, + 1e15, + 1e16, + 1e20, + 1e21, + 1e22, + 9_007_199_254_740_992.0, + 2f64.powi(63), + f64::MAX, + 5e-324, + 0.1 + 0.2, + 123.45, + -123.45, + 1e-7, + 0.000001, + f64::NAN, + f64::INFINITY, + f64::NEG_INFINITY, + ]; + for &d in &samples { + let mut buf = String::new(); + push_to_string(JsValue::float(d), &mut buf); + assert_eq!(buf, to_string(JsValue::float(d)), "double {d} 的 push/to_string 输出不一致"); + assert_eq!(buf, js_number_to_string(d), "double {d} 的 push/js_number_to_string 输出不一致"); + } +} + +#[test] +fn push_to_string_negative_double_appends_to_nonempty_buffer() { + // 追加语义回归:非空前缀下负数 double 的负号必须紧跟当前数值,不能插到 + // 整个缓冲最前。覆盖整数快路径、ryu 定点(≥2^53)、ryu 科学计数与边界值。 + let samples: Vec = vec![ + -42.0, // 整数快路径 + -1.5, // ryu 定点 + -9_007_199_254_740_994.0, // 2^53+2:≥2^53 可精确表示整数,落 ryu 路径 + -2f64.powi(63), // ryu 科学计数(规范最短表示) + -1e21, // ryu 科学计数 + -1e-7, // ryu 科学计数(小指数) + -f64::MAX, + -5e-324, + ]; + for &d in &samples { + let mut buf = String::from("pre"); + push_to_string(JsValue::float(d), &mut buf); + let expected = format!("pre{}", js_number_to_string(d)); + assert_eq!(buf, expected, "非空前缀 + double {d} 的追加语义破坏,实际 {buf}"); + } +}