353 lines
11 KiB
Rust
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
|
|
}
|