Files
komp_ac/steel_decimal/tests/registry_tests.rs
2025-07-05 10:00:04 +02:00

353 lines
11 KiB
Rust

use rstest::*;
use steel_decimal::{FunctionRegistry, FunctionRegistryBuilder};
use steel::steel_vm::engine::Engine;
use steel::rvals::SteelVal;
use std::collections::HashMap;
#[fixture]
fn fresh_vm() -> Engine {
Engine::new()
}
// Test basic function registration
#[rstest]
fn test_register_all_functions(mut fresh_vm: Engine) {
FunctionRegistry::register_all(&mut fresh_vm);
// Test that all major function categories work
let scripts = vec![
r#"(decimal-add "1" "2")"#,
r#"(decimal-pow "2" "3")"#,
r#"(decimal-sin "0")"#,
r#"(decimal-gt "5" "3")"#,
r#"(decimal-abs "-5")"#,
r#"(decimal-zero)"#,
r#"(decimal-percentage "100" "10")"#,
r#"(to-decimal "123.45")"#,
];
for script in scripts {
let result = fresh_vm.compile_and_run_raw_program(script.to_string());
assert!(result.is_ok(), "Failed to execute: {}", script);
}
}
// Test selective function registration
#[rstest]
fn test_basic_arithmetic_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(true)
.advanced_math(false)
.trigonometric(false)
.comparison(false)
.utility(false)
.constants(false)
.financial(false)
.conversion(false)
.register(&mut fresh_vm);
// Basic arithmetic should work
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-add "1" "2")"#.to_string());
assert!(result.is_ok());
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-mul "3" "4")"#.to_string());
assert!(result.is_ok());
// Note: Advanced math, trig, etc. won't be available but we can't easily test
// their absence without expecting compilation/runtime errors
}
#[rstest]
fn test_advanced_math_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(false)
.advanced_math(true)
.trigonometric(false)
.comparison(false)
.utility(false)
.constants(false)
.financial(false)
.conversion(false)
.register(&mut fresh_vm);
// Advanced math should work
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-pow "2" "3")"#.to_string());
assert!(result.is_ok());
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-sqrt "16")"#.to_string());
assert!(result.is_ok());
}
#[rstest]
fn test_trigonometric_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(false)
.advanced_math(false)
.trigonometric(true)
.comparison(false)
.utility(false)
.constants(false)
.financial(false)
.conversion(false)
.register(&mut fresh_vm);
// Trigonometric functions should work
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-sin "0")"#.to_string());
assert!(result.is_ok());
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-cos "0")"#.to_string());
assert!(result.is_ok());
}
#[rstest]
fn test_comparison_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(false)
.advanced_math(false)
.trigonometric(false)
.comparison(true)
.utility(false)
.constants(false)
.financial(false)
.conversion(false)
.register(&mut fresh_vm);
// Comparison functions should work
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-gt "5" "3")"#.to_string());
assert!(result.is_ok());
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-eq "5" "5")"#.to_string());
assert!(result.is_ok());
}
#[rstest]
fn test_utility_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(false)
.advanced_math(false)
.trigonometric(false)
.comparison(false)
.utility(true)
.constants(false)
.financial(false)
.conversion(false)
.register(&mut fresh_vm);
// Utility functions should work
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-abs "-5")"#.to_string());
assert!(result.is_ok());
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-min "3" "5")"#.to_string());
assert!(result.is_ok());
}
#[rstest]
fn test_constants_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(false)
.advanced_math(false)
.trigonometric(false)
.comparison(false)
.utility(false)
.constants(true)
.financial(false)
.conversion(false)
.register(&mut fresh_vm);
// Constants should work
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-pi)"#.to_string());
assert!(result.is_ok());
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-zero)"#.to_string());
assert!(result.is_ok());
}
#[rstest]
fn test_financial_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(false)
.advanced_math(false)
.trigonometric(false)
.comparison(false)
.utility(false)
.constants(false)
.financial(true)
.conversion(false)
.register(&mut fresh_vm);
// Financial functions should work
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-percentage "100" "10")"#.to_string());
assert!(result.is_ok());
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-compound "1000" "0.05" "1")"#.to_string());
assert!(result.is_ok());
}
#[rstest]
fn test_conversion_only(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(false)
.advanced_math(false)
.trigonometric(false)
.comparison(false)
.utility(false)
.constants(false)
.financial(false)
.conversion(true)
.register(&mut fresh_vm);
// Conversion functions should work
let result = fresh_vm.compile_and_run_raw_program(r#"(to-decimal "123.45")"#.to_string());
assert!(result.is_ok());
}
// Test variable registration
#[rstest]
fn test_variable_registration(mut fresh_vm: Engine) {
let mut variables = HashMap::new();
variables.insert("x".to_string(), "10.5".to_string());
variables.insert("y".to_string(), "20.3".to_string());
variables.insert("name".to_string(), "test_value".to_string());
FunctionRegistryBuilder::new()
.basic_arithmetic(true)
.with_variables(variables)
.register(&mut fresh_vm);
// Test getting variables
let result = fresh_vm.compile_and_run_raw_program(r#"(get-var "x")"#.to_string()).unwrap();
assert_eq!(result.len(), 1);
if let SteelVal::StringV(s) = &result[0] {
assert_eq!(s.to_string(), "10.5");
}
let result = fresh_vm.compile_and_run_raw_program(r#"(get-var "y")"#.to_string()).unwrap();
assert_eq!(result.len(), 1);
if let SteelVal::StringV(s) = &result[0] {
assert_eq!(s.to_string(), "20.3");
}
// Test checking if variables exist
let result = fresh_vm.compile_and_run_raw_program(r#"(has-var? "x")"#.to_string()).unwrap();
assert_eq!(result.len(), 1);
if let SteelVal::BoolV(b) = &result[0] {
assert!(b);
}
let result = fresh_vm.compile_and_run_raw_program(r#"(has-var? "nonexistent")"#.to_string()).unwrap();
assert_eq!(result.len(), 1);
if let SteelVal::BoolV(b) = &result[0] {
assert!(!b);
}
// Test using variables in arithmetic
let result = fresh_vm.compile_and_run_raw_program(r#"(decimal-add (get-var "x") (get-var "y"))"#.to_string()).unwrap();
assert_eq!(result.len(), 1);
if let SteelVal::StringV(s) = &result[0] {
assert_eq!(s.to_string(), "30.8");
}
}
// Test error handling in variable access
#[rstest]
fn test_variable_error_handling(mut fresh_vm: Engine) {
let variables = HashMap::new(); // Empty variables
FunctionRegistryBuilder::new()
.with_variables(variables)
.register(&mut fresh_vm);
// Should get an error for non-existent variable
let result = fresh_vm.compile_and_run_raw_program(r#"(get-var "nonexistent")"#.to_string());
// The function should return an error, but Steel might handle it differently
// This test ensures the function is registered and callable
assert!(result.is_ok() || result.is_err()); // Either way is fine, just shouldn't panic
}
// Test function name listing
#[rstest]
fn test_function_names() {
let names = FunctionRegistry::get_function_names();
// Check that all expected function categories are present
assert!(names.contains(&"decimal-add"));
assert!(names.contains(&"decimal-pow"));
assert!(names.contains(&"decimal-sin"));
assert!(names.contains(&"decimal-gt"));
assert!(names.contains(&"decimal-abs"));
assert!(names.contains(&"decimal-pi"));
assert!(names.contains(&"decimal-percentage"));
assert!(names.contains(&"to-decimal"));
assert!(names.contains(&"get-var"));
assert!(names.contains(&"has-var?"));
// Check total count is reasonable
assert!(names.len() >= 25); // Should have at least 25 functions
}
// Test builder pattern combinations
#[rstest]
fn test_builder_combinations(mut fresh_vm: Engine) {
FunctionRegistryBuilder::new()
.basic_arithmetic(true)
.comparison(true)
.constants(true)
.register(&mut fresh_vm);
// Should be able to use arithmetic, comparison, and constants
let result = fresh_vm.compile_and_run_raw_program(
r#"(decimal-gt (decimal-add "1" "2") (decimal-zero))"#.to_string()
).unwrap();
assert_eq!(result.len(), 1);
if let SteelVal::BoolV(b) = &result[0] {
assert!(b); // 3 > 0 should be true
}
}
// Test default builder behavior
#[rstest]
fn test_builder_defaults(mut fresh_vm: Engine) {
// Default should include everything
FunctionRegistryBuilder::new().register(&mut fresh_vm);
// Should be able to use any function
let scripts = vec![
r#"(decimal-add "1" "2")"#,
r#"(decimal-pow "2" "3")"#,
r#"(decimal-sin "0")"#,
r#"(decimal-gt "5" "3")"#,
r#"(decimal-abs "-5")"#,
r#"(decimal-pi)"#,
r#"(decimal-percentage "100" "10")"#,
r#"(to-decimal "123.45")"#,
];
for script in scripts {
let result = fresh_vm.compile_and_run_raw_program(script.to_string());
assert!(result.is_ok(), "Failed to execute with default builder: {}", script);
}
}
// Test multiple variable sets
#[rstest]
fn test_multiple_variable_registration(mut fresh_vm: Engine) {
let mut variables1 = HashMap::new();
variables1.insert("a".to_string(), "1".to_string());
variables1.insert("b".to_string(), "2".to_string());
// Register first set
FunctionRegistryBuilder::new()
.with_variables(variables1)
.register(&mut fresh_vm);
// Test first set works
let result = fresh_vm.compile_and_run_raw_program(r#"(get-var "a")"#.to_string()).unwrap();
if let SteelVal::StringV(s) = &result[0] {
assert_eq!(s.to_string(), "1");
}
// Note: In a real scenario, you probably wouldn't register variables twice
// This is just testing that the registration mechanism works
}