use super::*;
#[test]
fn test_evaluate_single_rule_relative_resolves_against_anchor_zero() {
let rule = MagicRule {
offset: OffsetSpec::Relative(3),
typ: TypeKind::Byte { signed: false },
op: Operator::Equal,
value: Value::Uint(0xCC),
message: "relative-no-context".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
value_transform: None,
};
let buffer = &[0xAA, 0xBB, 0xDD, 0xCC, 0xEE];
let result = evaluate_single_rule_legacy(&rule, buffer).unwrap();
assert!(
result.is_some(),
"evaluate_single_rule with Relative(3) should resolve to absolute 3"
);
let (offset, value) = result.unwrap();
assert_eq!(offset, 3);
assert_eq!(value, Value::Uint(0xCC));
}
#[test]
fn test_evaluate_rules_anchor_near_saturation_skips_relative_child_gracefully() {
use crate::EvaluationConfig;
use crate::evaluator::EvaluationContext;
let buffer = [0xAA, 0xBB, 0xCC, 0xDD];
let mut ctx = EvaluationContext::new(EvaluationConfig::default());
ctx.set_last_match_end(usize::MAX);
let rule_zero = MagicRule {
offset: OffsetSpec::Relative(0),
typ: TypeKind::Byte { signed: false },
op: Operator::Equal,
value: Value::Uint(0xAA),
message: "rel-zero-near-sat".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
value_transform: None,
};
let matches = evaluate_rules(&[rule_zero], &buffer, &mut ctx).unwrap();
assert!(
matches.is_empty(),
"Relative(0) at usize::MAX anchor must skip, not match or panic"
);
ctx.set_last_match_end(usize::MAX);
let rule_pos = MagicRule {
offset: OffsetSpec::Relative(1),
typ: TypeKind::Byte { signed: false },
op: Operator::Equal,
value: Value::Uint(0xAA),
message: "rel-plus-one-near-sat".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
value_transform: None,
};
let matches = evaluate_rules(&[rule_pos], &buffer, &mut ctx).unwrap();
assert!(
matches.is_empty(),
"Relative(+1) at usize::MAX anchor must skip via InvalidOffset, not panic"
);
ctx.set_last_match_end(usize::MAX);
let rule_neg = MagicRule {
offset: OffsetSpec::Relative(-1),
typ: TypeKind::Byte { signed: false },
op: Operator::Equal,
value: Value::Uint(0xAA),
message: "rel-minus-one-near-sat".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
value_transform: None,
};
let matches = evaluate_rules(&[rule_neg], &buffer, &mut ctx).unwrap();
assert!(
matches.is_empty(),
"Relative(-1) at usize::MAX anchor must skip, not panic"
);
}
#[test]
fn test_evaluate_single_rule_relative_negative_with_zero_anchor_errors() {
use crate::LibmagicError;
use crate::error::EvaluationError;
let rule = MagicRule {
offset: OffsetSpec::Relative(-1),
typ: TypeKind::Byte { signed: false },
op: Operator::Equal,
value: Value::Uint(0xAA),
message: "rel-neg-top-level".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
value_transform: None,
};
let buffer = &[0xAA, 0xBB];
let err = evaluate_single_rule_legacy(&rule, buffer).unwrap_err();
assert!(
matches!(
err,
LibmagicError::EvaluationError(EvaluationError::InvalidOffset { offset: -1 })
),
"Relative(-1) at anchor 0 must Err(InvalidOffset), got {err:?}"
);
}
#[test]
fn test_evaluate_single_rule_relative_zero_resolves_to_buffer_start() {
let rule = MagicRule {
offset: OffsetSpec::Relative(0),
typ: TypeKind::Byte { signed: false },
op: Operator::Equal,
value: Value::Uint(0xAA),
message: "relative-zero".to_string(),
children: vec![],
level: 0,
strength_modifier: None,
value_transform: None,
};
let buffer = &[0xAA, 0xBB];
let result = evaluate_single_rule_legacy(&rule, buffer).unwrap().unwrap();
assert_eq!(result.0, 0);
}