use super::*;
#[test]
fn test_term_creation_and_display() {
let atom = Term::Atom("hello".to_string());
assert_eq!(format!("{}", atom), "hello");
let var = Term::Variable("X".to_string());
assert_eq!(format!("{}", var), "X");
let num = Term::Number(42);
assert_eq!(format!("{}", num), "42");
let compound = Term::Compound("foo".to_string(), vec![
Term::Atom("bar".to_string()),
Term::Variable("X".to_string())
]);
assert_eq!(format!("{}", compound), "foo(bar, X)");
}
#[test]
fn test_empty_atom() {
let atom = Term::Atom("".to_string());
assert_eq!(format!("{}", atom), "");
assert!(atom.is_atom());
assert_eq!(atom.functor_arity(), Some(("", 0)));
}
#[test]
fn test_negative_numbers() {
let neg = Term::Number(-42);
assert_eq!(format!("{}", neg), "-42");
assert!(neg.is_number());
assert_eq!(neg.as_number(), Some(-42));
let min_int = Term::Number(i64::MIN);
assert_eq!(min_int.as_number(), Some(i64::MIN));
let max_int = Term::Number(i64::MAX);
assert_eq!(max_int.as_number(), Some(i64::MAX));
}
#[test]
fn test_compound_with_no_args() {
let compound = Term::Compound("foo".to_string(), vec![]);
assert_eq!(format!("{}", compound), "foo()");
assert_eq!(compound.functor_arity(), Some(("foo", 0)));
assert!(compound.is_compound());
}
#[test]
fn test_deeply_nested_compound() {
let nested = Term::Compound("f".to_string(), vec![
Term::Compound("g".to_string(), vec![
Term::Compound("h".to_string(), vec![
Term::Number(1)
])
])
]);
assert_eq!(format!("{}", nested), "f(g(h(1)))");
assert_eq!(nested.size(), 4);
}
#[test]
fn test_functor_arity() {
let atom = Term::Atom("hello".to_string());
assert_eq!(atom.functor_arity(), Some(("hello", 0)));
let compound = Term::Compound("foo".to_string(), vec![
Term::Atom("bar".to_string()),
Term::Variable("X".to_string())
]);
assert_eq!(compound.functor_arity(), Some(("foo", 2)));
let var = Term::Variable("X".to_string());
assert_eq!(var.functor_arity(), None);
let num = Term::Number(42);
assert_eq!(num.functor_arity(), None);
}
#[test]
fn test_type_checks() {
let atom = Term::Atom("hello".to_string());
assert!(atom.is_atom());
assert!(!atom.is_variable());
assert!(!atom.is_compound());
assert!(!atom.is_number());
let var = Term::Variable("X".to_string());
assert!(var.is_variable());
assert!(!var.is_atom());
assert!(!var.is_compound());
assert!(!var.is_number());
let num = Term::Number(42);
assert!(num.is_number());
assert!(!num.is_compound());
assert!(!num.is_atom());
assert!(!num.is_variable());
let compound = Term::Compound("foo".to_string(), vec![]);
assert!(compound.is_compound());
assert!(!compound.is_atom());
assert!(!compound.is_variable());
assert!(!compound.is_number());
}
#[test]
fn test_list_operations() {
let list = Term::from_list(vec![
Term::Number(1),
Term::Number(2),
Term::Number(3)
]);
assert!(list.is_proper_list());
let elements = list.to_list().unwrap();
assert_eq!(elements.len(), 3);
assert_eq!(elements[0], Term::Number(1));
assert_eq!(elements[1], Term::Number(2));
assert_eq!(elements[2], Term::Number(3));
assert_eq!(format!("{}", list), "[1, 2, 3]");
let empty = Term::Atom("[]".to_string());
assert!(empty.is_proper_list());
assert_eq!(empty.to_list().unwrap().len(), 0);
}
#[test]
fn test_improper_list() {
let improper = Term::Compound(".".to_string(), vec![
Term::Number(1),
Term::Variable("X".to_string())
]);
assert!(!improper.is_proper_list());
assert!(improper.to_list().is_none());
assert_eq!(format!("{}", improper), "[1|X]");
}
#[test]
fn test_list_with_compound_elements() {
let list = Term::from_list(vec![
Term::Compound("foo".to_string(), vec![Term::Number(1)]),
Term::Atom("bar".to_string()),
Term::Variable("X".to_string())
]);
assert!(list.is_proper_list());
let elements = list.to_list().unwrap();
assert_eq!(elements.len(), 3);
assert_eq!(format!("{}", list), "[foo(1), bar, X]");
}
#[test]
fn test_empty_list_conversion() {
let empty_vec: Vec<Term> = vec![];
let empty_list = Term::from_list(empty_vec);
assert_eq!(empty_list, Term::Atom("[]".to_string()));
assert!(empty_list.is_proper_list());
}
#[test]
fn test_single_element_list() {
let list = Term::from_list(vec![Term::Number(42)]);
assert!(list.is_proper_list());
let elements = list.to_list().unwrap();
assert_eq!(elements.len(), 1);
assert_eq!(elements[0], Term::Number(42));
assert_eq!(format!("{}", list), "[42]");
}
#[test]
fn test_variable_collection() {
let term = Term::Compound("foo".to_string(), vec![
Term::Variable("X".to_string()),
Term::Compound("bar".to_string(), vec![
Term::Variable("Y".to_string()),
Term::Variable("X".to_string()), ])
]);
let vars = term.variables();
assert_eq!(vars.len(), 2);
assert!(vars.contains(&&"X".to_string()));
assert!(vars.contains(&&"Y".to_string()));
}
#[test]
fn test_no_variables() {
let term = Term::Compound("foo".to_string(), vec![
Term::Atom("a".to_string()),
Term::Number(42)
]);
let vars = term.variables();
assert_eq!(vars.len(), 0);
}
#[test]
fn test_underscore_variable() {
let term = Term::Compound("foo".to_string(), vec![
Term::Variable("_".to_string()),
Term::Variable("_123".to_string())
]);
let vars = term.variables();
assert_eq!(vars.len(), 2);
}
#[test]
fn test_ground_terms() {
let ground_term = Term::Compound("foo".to_string(), vec![
Term::Atom("a".to_string()),
Term::Number(42)
]);
assert!(ground_term.is_ground());
let non_ground_term = Term::Compound("foo".to_string(), vec![
Term::Variable("X".to_string()),
Term::Number(42)
]);
assert!(!non_ground_term.is_ground());
}
#[test]
fn test_ground_deeply_nested() {
let ground = Term::Compound("f".to_string(), vec![
Term::Compound("g".to_string(), vec![
Term::Compound("h".to_string(), vec![
Term::Atom("a".to_string())
])
])
]);
assert!(ground.is_ground());
let non_ground = Term::Compound("f".to_string(), vec![
Term::Compound("g".to_string(), vec![
Term::Compound("h".to_string(), vec![
Term::Variable("X".to_string())
])
])
]);
assert!(!non_ground.is_ground());
}
#[test]
fn test_accessor_methods() {
let var = Term::Variable("X".to_string());
assert_eq!(var.as_variable(), Some(&"X".to_string()));
assert_eq!(var.as_atom(), None);
assert_eq!(var.as_number(), None);
assert_eq!(var.as_compound(), None);
let atom = Term::Atom("hello".to_string());
assert_eq!(atom.as_atom(), Some(&"hello".to_string()));
assert_eq!(atom.as_variable(), None);
assert_eq!(atom.as_number(), None);
assert_eq!(atom.as_compound(), None);
let num = Term::Number(42);
assert_eq!(num.as_number(), Some(42));
assert_eq!(num.as_atom(), None);
assert_eq!(num.as_variable(), None);
assert_eq!(num.as_compound(), None);
let compound = Term::Compound("foo".to_string(), vec![Term::Atom("bar".to_string())]);
if let Some((functor, args)) = compound.as_compound() {
assert_eq!(functor, "foo");
assert_eq!(args.len(), 1);
} else {
panic!("Expected compound term");
}
assert_eq!(compound.as_atom(), None);
assert_eq!(compound.as_variable(), None);
assert_eq!(compound.as_number(), None);
}
#[test]
fn test_term_size() {
let atom = Term::Atom("hello".to_string());
assert_eq!(atom.size(), 1);
let compound = Term::Compound("foo".to_string(), vec![
Term::Atom("bar".to_string()),
Term::Compound("baz".to_string(), vec![Term::Number(42)])
]);
assert_eq!(compound.size(), 4);
}
#[test]
fn test_size_empty_compound() {
let compound = Term::Compound("foo".to_string(), vec![]);
assert_eq!(compound.size(), 1);
}
#[test]
fn test_size_large_list() {
let list = Term::from_list((0..100).map(Term::Number).collect());
assert_eq!(list.size(), 201);
}
#[test]
fn test_clause_creation() {
let fact = Clause::fact(Term::Atom("parent(tom, bob)".to_string()));
assert!(fact.is_fact());
assert!(!fact.is_rule());
let rule = Clause::rule(
Term::Atom("grandparent(X, Z)".to_string()),
vec![
Term::Atom("parent(X, Y)".to_string()),
Term::Atom("parent(Y, Z)".to_string()),
]
);
assert!(rule.is_rule());
assert!(!rule.is_fact());
}
#[test]
fn test_clause_display() {
let fact = Clause::fact(Term::Atom("likes(mary, wine)".to_string()));
assert_eq!(format!("{}", fact), "likes(mary, wine)");
let rule = Clause::rule(
Term::Atom("happy(X)".to_string()),
vec![Term::Atom("likes(X, wine)".to_string())]
);
assert_eq!(format!("{}", rule), "happy(X) :- likes(X, wine)");
}
#[test]
fn test_clause_with_multiple_goals() {
let rule = Clause::rule(
Term::Compound("ancestor".to_string(), vec![
Term::Variable("X".to_string()),
Term::Variable("Z".to_string())
]),
vec![
Term::Compound("parent".to_string(), vec![
Term::Variable("X".to_string()),
Term::Variable("Y".to_string())
]),
Term::Compound("parent".to_string(), vec![
Term::Variable("Y".to_string()),
Term::Variable("Z".to_string())
])
]
);
assert_eq!(rule.body.len(), 2);
assert!(rule.is_rule());
let display = format!("{}", rule);
assert!(display.contains(" :- "));
assert!(display.contains(", "));
}
#[test]
fn test_clause_functor_arity() {
let clause = Clause::fact(Term::Compound("parent".to_string(), vec![
Term::Atom("tom".to_string()),
Term::Atom("bob".to_string())
]));
assert_eq!(clause.head_functor_arity(), Some(("parent", 2)));
assert_eq!(clause.arity(), 2);
assert_eq!(clause.functor(), Some("parent"));
}
#[test]
fn test_clause_with_atom_head() {
let clause = Clause::fact(Term::Atom("true".to_string()));
assert_eq!(clause.head_functor_arity(), Some(("true", 0)));
assert_eq!(clause.arity(), 0);
assert_eq!(clause.functor(), Some("true"));
}
#[test]
fn test_clause_variables() {
let clause = Clause::rule(
Term::Compound("foo".to_string(), vec![
Term::Variable("X".to_string()),
Term::Variable("Y".to_string())
]),
vec![
Term::Compound("bar".to_string(), vec![
Term::Variable("X".to_string()), Term::Variable("Z".to_string()) ])
]
);
let vars = clause.variables();
assert_eq!(vars.len(), 3);
assert!(vars.contains(&&"X".to_string()));
assert!(vars.contains(&&"Y".to_string()));
assert!(vars.contains(&&"Z".to_string()));
}
#[test]
fn test_clause_no_variables() {
let clause = Clause::fact(Term::Compound("parent".to_string(), vec![
Term::Atom("tom".to_string()),
Term::Atom("bob".to_string())
]));
let vars = clause.variables();
assert_eq!(vars.len(), 0);
}
#[test]
fn test_clause_is_ground() {
let ground_clause = Clause::fact(Term::Compound("parent".to_string(), vec![
Term::Atom("tom".to_string()),
Term::Atom("bob".to_string())
]));
assert!(ground_clause.is_ground());
let non_ground_clause = Clause::rule(
Term::Compound("parent".to_string(), vec![
Term::Variable("X".to_string()),
Term::Atom("bob".to_string())
]),
vec![Term::Atom("true".to_string())]
);
assert!(!non_ground_clause.is_ground());
}
#[test]
fn test_special_characters_in_atoms() {
let atom = Term::Atom("foo_bar-123".to_string());
assert_eq!(format!("{}", atom), "foo_bar-123");
assert_eq!(atom.functor_arity(), Some(("foo_bar-123", 0)));
}
#[test]
fn test_unicode_in_atoms() {
let atom = Term::Atom("こんにちは".to_string());
assert_eq!(format!("{}", atom), "こんにちは");
assert!(atom.is_atom());
}
#[test]
fn test_very_long_names() {
let long_name = "a".repeat(1000);
let atom = Term::Atom(long_name.clone());
assert_eq!(atom.as_atom(), Some(&long_name));
let var = Term::Variable(long_name.clone());
assert_eq!(var.as_variable(), Some(&long_name));
}
#[test]
fn test_deeply_nested_list() {
let inner = Term::from_list(vec![Term::Number(1)]);
let level2 = Term::from_list(vec![inner]);
let level3 = Term::from_list(vec![level2]);
let level4 = Term::from_list(vec![level3]);
assert!(level4.is_proper_list());
assert_eq!(format!("{}", level4), "[[[[1]]]]");
}
#[test]
fn test_mixed_list_structures() {
let inner_list = Term::from_list(vec![Term::Number(2), Term::Number(3)]);
let outer_list = Term::from_list(vec![
Term::Number(1),
inner_list,
Term::Number(4)
]);
assert!(outer_list.is_proper_list());
let elements = outer_list.to_list().unwrap();
assert_eq!(elements.len(), 3);
assert_eq!(format!("{}", outer_list), "[1, [2, 3], 4]");
}
#[test]
fn test_compound_with_many_arguments() {
let args: Vec<Term> = (0..100).map(Term::Number).collect();
let compound = Term::Compound("big".to_string(), args);
assert_eq!(compound.functor_arity(), Some(("big", 100)));
assert_eq!(compound.size(), 101);
}
#[test]
fn test_empty_body_clause() {
let clause = Clause::rule(
Term::Atom("foo".to_string()),
vec![]
);
assert!(clause.is_fact());
assert!(!clause.is_rule());
}
#[test]
fn test_clause_with_variable_head() {
let clause = Clause::fact(Term::Variable("X".to_string()));
assert_eq!(clause.functor(), None);
assert_eq!(clause.arity(), 0);
assert_eq!(clause.head_functor_arity(), None);
}
#[test]
fn test_clause_with_number_head() {
let clause = Clause::fact(Term::Number(42));
assert_eq!(clause.functor(), None);
assert_eq!(clause.arity(), 0);
assert_eq!(clause.head_functor_arity(), None);
}
#[test]
fn test_display_list_with_non_list_tail() {
let improper = Term::Compound(".".to_string(), vec![
Term::Number(1),
Term::Compound(".".to_string(), vec![
Term::Number(2),
Term::Atom("foo".to_string()) ])
]);
assert!(!improper.is_proper_list());
let display = format!("{}", improper);
assert!(display.contains("|") || display.contains("."));
}
#[test]
fn test_variables_ordering_preserved() {
let term = Term::Compound("foo".to_string(), vec![
Term::Variable("Z".to_string()),
Term::Variable("A".to_string()),
Term::Variable("Z".to_string()), Term::Variable("M".to_string()),
]);
let vars = term.variables();
assert_eq!(vars.len(), 3);
assert_eq!(vars[0], &"Z".to_string());
assert_eq!(vars[1], &"A".to_string());
assert_eq!(vars[2], &"M".to_string());
}
#[test]
fn test_compound_equality() {
let term1 = Term::Compound("foo".to_string(), vec![
Term::Number(1),
Term::Atom("bar".to_string())
]);
let term2 = Term::Compound("foo".to_string(), vec![
Term::Number(1),
Term::Atom("bar".to_string())
]);
let term3 = Term::Compound("foo".to_string(), vec![
Term::Number(2), Term::Atom("bar".to_string())
]);
assert_eq!(term1, term2);
assert_ne!(term1, term3);
}
#[test]
fn test_clone_terms() {
let original = Term::Compound("foo".to_string(), vec![
Term::Variable("X".to_string()),
Term::Number(42)
]);
let cloned = original.clone();
assert_eq!(original, cloned);
if let Term::Compound(_, args) = cloned {
assert_eq!(args.len(), 2);
}
}
#[test]
fn test_debug_format() {
let term = Term::Atom("test".to_string());
let debug_str = format!("{:?}", term);
assert!(debug_str.contains("Atom"));
assert!(debug_str.contains("test"));
}