use crate::eval::environment::Environment;
use crate::eval::error::EvalError;
use crate::eval::evaluator::eval;
use crate::registry::EvalRegistry;
use hamelin_lib::tree::options::ExpressionTypeCheckOptions;
use hamelin_lib::tree::typed_ast::expression::TypedExpressionKind;
use hamelin_lib::type_check_expression;
use std::sync::Arc;
use super::test_helpers::TestContext;
#[test]
fn test_function_evaluation_error_types() {
let eval_error = EvalError::NoEvalImplementation {
function_name: "test_function".to_string(),
};
match &eval_error {
EvalError::NoEvalImplementation { function_name } => {
assert_eq!(function_name, "test_function");
}
_ => panic!("Expected NoEvalImplementation error"),
}
assert_eq!(
eval_error.to_string(),
"Function 'test_function' has no evaluation implementation"
);
}
#[test]
fn test_eval_error_display() {
let no_impl_error = EvalError::NoEvalImplementation {
function_name: "missing_func".to_string(),
};
assert_eq!(
no_impl_error.to_string(),
"Function 'missing_func' has no evaluation implementation"
);
let execution_error = EvalError::ExecutionError {
message: "Division by zero".to_string(),
source: None,
};
assert_eq!(execution_error.to_string(), "Division by zero");
}
#[test]
fn test_error_expression_evaluation() {
use hamelin_lib::tree::builder::{field_ref, ExpressionBuilder};
let ctx = TestContext::new();
let env = Environment::new();
let expr = type_check_expression(
field_ref("undefined").build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
assert!(matches!(expr.kind, TypedExpressionKind::Error(_)));
let result = eval(&expr, &env);
assert!(result.is_err());
let error = result.unwrap_err();
assert!(matches!(error, EvalError::ExecutionError { .. }));
}
#[test]
fn test_error_propagation_in_operations() {
use hamelin_lib::tree::builder::{add, field_ref, ExpressionBuilder};
let ctx = TestContext::new();
let env = Environment::new();
let expr = type_check_expression(
add(field_ref("undefined"), 42).build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
assert!(matches!(expr.kind, TypedExpressionKind::Error(_)));
let result = eval(&expr, &env);
assert!(result.is_err());
}
#[test]
fn test_multiple_error_sources() {
use hamelin_lib::tree::builder::{add, field_ref, ExpressionBuilder};
let ctx = TestContext::new();
let env = Environment::new();
let expr = type_check_expression(
add(field_ref("undefined1"), field_ref("undefined2")).build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &env);
assert!(result.is_err());
let error = result.unwrap_err();
assert!(matches!(error, EvalError::ExecutionError { .. }));
}
#[test]
fn test_error_in_nested_expressions() {
use hamelin_lib::tree::builder::{add, field_ref, multiply, ExpressionBuilder};
let ctx = TestContext::new();
let env = Environment::new();
let expr = type_check_expression(
multiply(add(5, field_ref("undefined")), 3).build(),
ExpressionTypeCheckOptions::builder()
.bindings(Arc::new(ctx.translation_env.clone()))
.build(),
)
.output;
let result = eval(&expr, &env);
assert!(result.is_err());
let error = result.unwrap_err();
assert!(matches!(error, EvalError::ExecutionError { .. }));
}
#[test]
fn test_runtime_error_handling() {
use hamelin_lib::tree::builder::{divide, ExpressionBuilder};
let env = Environment::new();
let expr =
type_check_expression(divide(10, 0).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env);
assert!(result.is_err());
let error = result.unwrap_err();
assert!(matches!(error, EvalError::ExecutionError { .. }));
}
#[test]
fn test_error_message_quality() {
let execution_error = EvalError::ExecutionError {
message: "Array index 5 out of bounds for array of length 3".to_string(),
source: None,
};
let error_msg = execution_error.to_string();
assert!(error_msg.contains("out of bounds"));
assert!(error_msg.contains("5"));
assert!(error_msg.contains("3"));
}
#[test]
fn test_eval_error_variants() {
let no_impl = EvalError::NoEvalImplementation {
function_name: "custom_func".to_string(),
};
assert!(no_impl.to_string().contains("custom_func"));
assert!(no_impl.to_string().contains("no evaluation implementation"));
let exec_error = EvalError::execution_with_source(
"Operation failed",
std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied"),
);
assert!(exec_error.to_string().contains("Operation failed"));
}
#[test]
fn test_no_eval_implementation_error_from_registry() {
use hamelin_lib::tree::builder::{add, ExpressionBuilder};
let empty_registry = Arc::new(EvalRegistry::new());
let env = Environment::with_registry(empty_registry);
let expr =
type_check_expression(add(1, 2).build(), ExpressionTypeCheckOptions::default()).output;
let result = eval(&expr, &env);
assert!(result.is_err());
let error = result.unwrap_err();
match error {
EvalError::NoEvalImplementation { function_name } => {
assert!(
function_name.contains('+') || function_name.contains("plus"),
"Expected function name to indicate addition, got: {}",
function_name
);
}
other => panic!("Expected NoEvalImplementation error, got: {:?}", other),
}
}