#![allow(clippy::expect_used)]
use super::*;
use crate::ast::restricted::{BinaryOp, Literal, UnaryOp};
use crate::ast::{Expr, Function, RestrictedAst, Stmt, Type};
use proptest::prelude::*;
use rstest::*;
#[test]
fn test_should_echo_guard_conditions() {
let ast_with_return = RestrictedAst {
functions: vec![Function {
name: "get_value".to_string(),
params: vec![],
return_type: Type::U32,
body: vec![
Stmt::Let {
name: "x".to_string(),
value: Expr::Literal(Literal::U32(10)),
declaration: true,
},
Stmt::Expr(Expr::Variable("x".to_string())),
],
}],
entry_point: "get_value".to_string(),
};
let ast_void = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Void,
body: vec![
Stmt::Let {
name: "x".to_string(),
value: Expr::Literal(Literal::U32(10)),
declaration: true,
},
Stmt::Expr(Expr::Variable("x".to_string())),
],
}],
entry_point: "main".to_string(),
};
let ir1 = from_ast(&ast_with_return);
let ir2 = from_ast(&ast_void);
assert!(ir1.is_ok(), "Function with return type should convert");
assert!(ir2.is_ok(), "Function with void return type should convert");
}
#[test]
fn test_equality_operator_conversion() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Let {
name: "result".to_string(),
value: Expr::Binary {
op: BinaryOp::Eq, left: Box::new(Expr::Literal(Literal::U32(5))),
right: Box::new(Expr::Literal(Literal::U32(5))),
},
declaration: true,
}],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).unwrap();
match ir {
ShellIR::Sequence(stmts) => {
match &stmts[0] {
ShellIR::Let { value, .. } => {
match value {
ShellValue::Comparison { op, .. } => {
assert!(matches!(op, crate::ir::shell_ir::ComparisonOp::NumEq));
}
other => panic!("Expected Comparison, got {:?}", other),
}
}
_ => panic!("Expected Let"),
}
}
_ => panic!("Expected Sequence"),
}
}
#[test]
fn test_subtraction_operator_conversion() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Let {
name: "result".to_string(),
value: Expr::Binary {
op: BinaryOp::Sub, left: Box::new(Expr::Literal(Literal::U32(10))),
right: Box::new(Expr::Literal(Literal::U32(3))),
},
declaration: true,
}],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).unwrap();
match ir {
ShellIR::Sequence(stmts) => {
match &stmts[0] {
ShellIR::Let { value, .. } => {
match value {
ShellValue::Arithmetic { op, .. } => {
assert!(matches!(op, crate::ir::shell_ir::ArithmeticOp::Sub));
}
other => panic!("Expected Arithmetic with Sub, got {:?}", other),
}
}
_ => panic!("Expected Let"),
}
}
_ => panic!("Expected Sequence"),
}
}
#[test]
fn test_curl_command_network_effect() {
let effects = effects::analyze_command_effects("curl");
assert!(
effects.has_network_effects(),
"curl should have network effects"
);
assert!(!effects.is_pure(), "curl should not be pure");
}
#[test]
fn test_wget_command_network_effect() {
let effects = effects::analyze_command_effects("wget");
assert!(
effects.has_network_effects(),
"wget should have network effects"
);
assert!(!effects.is_pure(), "wget should not be pure");
}
#[test]
fn test_non_network_command_no_effect() {
let effects = effects::analyze_command_effects("ls");
assert!(
!effects.has_network_effects(),
"ls should not have network effects"
);
}
#[test]
fn test_ir_converter_analyze_command_effects_used() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "curl".to_string(),
args: vec![Expr::Literal(Literal::Str(
"http://example.com".to_string(),
))],
})],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).unwrap();
match ir {
ShellIR::Sequence(stmts) => match &stmts[0] {
ShellIR::Exec { effects, .. } => {
assert!(
effects.has_network_effects(),
"curl command should have NetworkAccess effect via IR converter"
);
assert!(!effects.is_pure(), "curl command should not be pure");
}
_ => panic!("Expected Exec statement for curl"),
},
_ => panic!("Expected Sequence"),
}
}
#[test]
fn test_ir_converter_wget_command_effect() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "wget".to_string(),
args: vec![Expr::Literal(Literal::Str(
"http://example.com".to_string(),
))],
})],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).unwrap();
match ir {
ShellIR::Sequence(stmts) => match &stmts[0] {
ShellIR::Exec { cmd, effects } => {
assert_eq!(cmd.program, "wget");
assert!(
effects.has_network_effects(),
"wget should have NetworkAccess effect through IR converter"
);
}
_ => panic!("Expected Exec statement for wget"),
},
_ => panic!("Expected Sequence"),
}
}
#[test]
fn test_ir_converter_printf_command_effect() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Expr(Expr::FunctionCall {
name: "printf".to_string(),
args: vec![Expr::Literal(Literal::Str("Hello\\n".to_string()))],
})],
}],
entry_point: "main".to_string(),
};
let ir = from_ast(&ast).unwrap();
match ir {
ShellIR::Sequence(stmts) => match &stmts[0] {
ShellIR::Exec { cmd, effects } => {
assert_eq!(cmd.program, "printf");
assert!(
effects.has_filesystem_effects(),
"printf should have FileWrite effect through IR converter"
);
}
_ => panic!("Expected Exec statement for printf"),
},
_ => panic!("Expected Sequence"),
}
}
#[test]
fn test_is_string_value_requires_both_parse_failures() {
let ast = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Let {
name: "result".to_string(),
value: Expr::Binary {
op: BinaryOp::Eq,
left: Box::new(Expr::Literal(Literal::Str("123".to_string()))),
right: Box::new(Expr::Literal(Literal::Str("124".to_string()))),
},
declaration: true,
}],
}],
entry_point: "main".to_string(),
};
let _ir = from_ast(&ast).unwrap();
let ast_float = RestrictedAst {
functions: vec![Function {
name: "main".to_string(),
params: vec![],
return_type: Type::Str,
body: vec![Stmt::Let {
name: "result".to_string(),
value: Expr::Binary {
op: BinaryOp::Eq,
left: Box::new(Expr::Literal(Literal::Str("123.5".to_string()))),
right: Box::new(Expr::Literal(Literal::Str("124.5".to_string()))),
},
declaration: true,
}],
}],
entry_point: "main".to_string(),
};
let ir_float = from_ast(&ast_float).unwrap();
match ir_float {
ShellIR::Sequence(stmts) => {
match &stmts[0] {
ShellIR::Let { value, .. } => {
match value {
ShellValue::Comparison { op, .. } => {
assert!(
matches!(op, crate::ir::shell_ir::ComparisonOp::NumEq),
"Float strings like '123.5' should use NumEq, not StrEq. \
If this fails, is_string_value is using || instead of &&"
);
}
other => panic!("Expected Comparison, got {:?}", other),
}
}
_ => panic!("Expected Let"),
}
}
_ => panic!("Expected Sequence"),
}
}
include!("tests_s2_tests_env_call.rs");