#![cfg(test)]
#![allow(warnings)]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::unreadable_literal)]
#![allow(clippy::unwrap_used)]
use ruchy::frontend::ast::{BinaryOp, Expr, ExprKind, Literal, MatchArm, Pattern, Span};
use ruchy::lints::{LintRule, LintViolation, RuchyLinter, Severity};
fn create_test_expr(kind: ExprKind) -> Expr {
Expr::new(kind, Span::new(0, 10))
}
#[test]
fn test_linter_creation() {
let _linter = RuchyLinter::new();
let _default_linter = RuchyLinter::default();
}
#[test]
fn test_linter_add_custom_rule() {
let mut linter = RuchyLinter::new();
struct TestRule;
impl LintRule for TestRule {
fn name(&self) -> &'static str {
"test_rule"
}
fn check_expression(&self, _expr: &Expr) -> Vec<LintViolation> {
vec![]
}
}
linter.add_rule(Box::new(TestRule));
}
#[test]
fn test_lint_simple_expressions() {
let linter = RuchyLinter::new();
let expr = create_test_expr(ExprKind::Literal(Literal::Integer(42)));
let violations = linter.lint(&expr);
assert!(violations.is_empty());
}
#[test]
fn test_lint_string_expressions() {
let linter = RuchyLinter::new();
let expr = create_test_expr(ExprKind::Literal(Literal::String("hello".to_string())));
let violations = linter.lint(&expr);
assert!(violations.is_empty());
}
#[test]
fn test_lint_boolean_expressions() {
let linter = RuchyLinter::new();
let expr = create_test_expr(ExprKind::Literal(Literal::Bool(true)));
let violations = linter.lint(&expr);
assert!(violations.is_empty());
}
#[test]
fn test_complexity_rule_simple_if() {
let linter = RuchyLinter::new();
let condition = Box::new(create_test_expr(ExprKind::Literal(Literal::Bool(true))));
let then_branch = Box::new(create_test_expr(ExprKind::Literal(Literal::Integer(1))));
let else_branch = Some(Box::new(create_test_expr(ExprKind::Literal(
Literal::Integer(2),
))));
let if_expr = create_test_expr(ExprKind::If {
condition,
then_branch,
else_branch,
});
let violations = linter.lint(&if_expr);
assert!(violations.is_empty());
}
#[test]
fn test_complexity_rule_nested_complexity() {
let linter = RuchyLinter::new();
let mut nested_expr = create_test_expr(ExprKind::Literal(Literal::Integer(1)));
for _ in 0..12 {
let condition = Box::new(create_test_expr(ExprKind::Literal(Literal::Bool(true))));
let then_branch = Box::new(nested_expr.clone());
let else_branch = Some(Box::new(create_test_expr(ExprKind::Literal(
Literal::Integer(2),
))));
nested_expr = create_test_expr(ExprKind::If {
condition,
then_branch,
else_branch,
});
}
let violations = linter.lint(&nested_expr);
assert!(!violations.is_empty());
let first_violation = &violations[0];
let LintViolation::Violation {
message, severity, ..
} = first_violation;
assert!(message.contains("complexity"));
assert!(matches!(severity, Severity::Warning));
}
#[test]
fn test_no_debug_print_rule_regular_calls() {
let linter = RuchyLinter::new();
let func = Box::new(create_test_expr(ExprKind::Identifier(
"println".to_string(),
)));
let args = vec![];
let call_expr = create_test_expr(ExprKind::Call { func, args });
let violations = linter.lint(&call_expr);
assert!(
violations.is_empty()
|| !violations.iter().any(|v| {
let LintViolation::Violation { message, .. } = v;
message.contains("debug")
})
);
}
#[test]
fn test_no_debug_print_rule_debug_calls() {
let linter = RuchyLinter::new();
let func = Box::new(create_test_expr(ExprKind::Identifier("dbg".to_string())));
let args = vec![create_test_expr(ExprKind::Literal(Literal::Integer(42)))];
let debug_call_expr = create_test_expr(ExprKind::Call { func, args });
let violations = linter.lint(&debug_call_expr);
println!("Debug: Violations found: {violations:?}");
assert!(!violations.is_empty());
let has_debug_violation = violations.iter().any(|v| {
let LintViolation::Violation { message, .. } = v;
message.contains("debug") || message.contains("Debug")
});
assert!(has_debug_violation);
}
#[test]
fn test_no_debug_print_rule_debug_print_calls() {
let linter = RuchyLinter::new();
let func = Box::new(create_test_expr(ExprKind::Identifier(
"debug_print".to_string(),
)));
let args = vec![create_test_expr(ExprKind::Literal(Literal::String(
"debug".to_string(),
)))];
let debug_print_expr = create_test_expr(ExprKind::Call { func, args });
let violations = linter.lint(&debug_print_expr);
assert!(!violations.is_empty());
}
#[test]
fn test_complexity_rule_match_expression() {
let linter = RuchyLinter::new();
let expr = Box::new(create_test_expr(ExprKind::Literal(Literal::Integer(42))));
let arms = vec![
MatchArm {
pattern: Pattern::Literal(Literal::Integer(1)),
guard: None,
body: Box::new(create_test_expr(ExprKind::Literal(Literal::String(
"one".to_string(),
)))),
span: Span::new(0, 10),
},
MatchArm {
pattern: Pattern::Literal(Literal::Integer(2)),
guard: None,
body: Box::new(create_test_expr(ExprKind::Literal(Literal::String(
"two".to_string(),
)))),
span: Span::new(0, 10),
},
];
let match_expr = create_test_expr(ExprKind::Match { expr, arms });
let violations = linter.lint(&match_expr);
assert!(violations.is_empty());
}
#[test]
fn test_complexity_rule_while_loop() {
let linter = RuchyLinter::new();
let condition = Box::new(create_test_expr(ExprKind::Literal(Literal::Bool(true))));
let body = Box::new(create_test_expr(ExprKind::Literal(Literal::Integer(1))));
let while_expr = create_test_expr(ExprKind::While { condition, body });
let violations = linter.lint(&while_expr);
assert!(violations.is_empty());
}
#[test]
fn test_complexity_rule_for_loop() {
let linter = RuchyLinter::new();
let var = "i".to_string();
let pattern = Some(Pattern::Identifier("i".to_string()));
let iter = Box::new(create_test_expr(ExprKind::Literal(Literal::Integer(10))));
let body = Box::new(create_test_expr(ExprKind::Literal(Literal::Integer(1))));
let for_expr = create_test_expr(ExprKind::For {
var,
pattern,
iter,
body,
});
let violations = linter.lint(&for_expr);
assert!(violations.is_empty());
}
#[test]
fn test_complexity_rule_binary_expression() {
let linter = RuchyLinter::new();
let left = Box::new(create_test_expr(ExprKind::Literal(Literal::Integer(1))));
let right = Box::new(create_test_expr(ExprKind::Literal(Literal::Integer(2))));
let binary_expr = create_test_expr(ExprKind::Binary {
left,
op: BinaryOp::Add,
right,
});
let violations = linter.lint(&binary_expr);
assert!(violations.is_empty());
}
#[test]
fn test_lint_violation_formatting() {
let violation = LintViolation::Violation {
location: "line 10".to_string(),
message: "Test violation".to_string(),
severity: Severity::Error,
suggestion: Some("Fix this".to_string()),
};
let formatted = format!("{violation}");
assert!(formatted.contains("line 10"));
assert!(formatted.contains("Test violation"));
assert!(formatted.contains("Error"));
}
#[test]
fn test_severity_enum() {
let error = Severity::Error;
let warning = Severity::Warning;
let info = Severity::Info;
assert_eq!(error, Severity::Error);
assert_eq!(warning, Severity::Warning);
assert_eq!(info, Severity::Info);
assert_ne!(error, warning);
assert_ne!(warning, info);
assert_ne!(error, info);
}
#[test]
fn test_custom_lint_rule() {
struct AlwaysViolatesRule;
impl LintRule for AlwaysViolatesRule {
fn name(&self) -> &'static str {
"always_violates"
}
fn check_expression(&self, expr: &Expr) -> Vec<LintViolation> {
vec![LintViolation::Violation {
location: format!("position {}", expr.span.start),
message: "This rule always triggers".to_string(),
severity: Severity::Info,
suggestion: None,
}]
}
}
let mut linter = RuchyLinter::new();
linter.add_rule(Box::new(AlwaysViolatesRule));
let expr = create_test_expr(ExprKind::Literal(Literal::Integer(42)));
let violations = linter.lint(&expr);
let has_custom_violation = violations.iter().any(|v| {
let LintViolation::Violation { message, .. } = v;
message.contains("always triggers")
});
assert!(has_custom_violation);
}
#[test]
fn test_lint_rule_names() {
struct TestRule;
impl LintRule for TestRule {
fn name(&self) -> &'static str {
"test_rule_name"
}
fn check_expression(&self, _expr: &Expr) -> Vec<LintViolation> {
vec![]
}
}
let rule = TestRule;
assert_eq!(rule.name(), "test_rule_name");
}