initial commit

This commit is contained in:
2025-11-01 22:45:55 +01:00
commit 78959d4f22
19 changed files with 2027 additions and 0 deletions

254
tests/interpreter_tests.rs Normal file
View File

@@ -0,0 +1,254 @@
use rush::interpreter::executor::Executor;
use rush::parser::Parser;
use rush::runtime::environment::Environment;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_variable_assignment() {
// Test variable assignment functionality
let parser = Parser::new();
let result = parser.parse("name = \"test\"");
assert!(result.is_ok());
let program = result.unwrap();
let mut env = Environment::new();
let mut executor = Executor::new(&mut env);
for stmt in program.statements {
executor.execute_node(stmt);
}
assert_eq!(env.get_variable("name"), Some(&"test".to_string()));
}
#[test]
fn test_simple_command() {
// Test basic command execution doesn't panic
let parser = Parser::new();
let result = parser.parse("echo \"test\"");
assert!(result.is_ok());
}
#[test]
fn test_script_parsing() {
// Test the parsing of a complete .rsh script
let script = r#"
name = "Louis"
echo "Hello" $name
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
let program = result.unwrap();
assert!(program.statements.len() >= 2);
}
#[test]
fn test_undefined_variable_detection() {
// Test that using an undefined variable is caught
let script = r#"
echo "Hello" $undefined_var
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_err());
let err_msg = format!("{:?}", result.unwrap_err());
assert!(err_msg.contains("undefined_var"));
}
#[test]
fn test_out_of_order_variable_detection() {
// Test that using a variable before it's defined is caught
let script = r#"
echo "Hello" $name
name = "Louis"
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_err());
let err_msg = format!("{:?}", result.unwrap_err());
assert!(err_msg.contains("name"));
}
#[test]
fn test_valid_variable_order() {
// Test that defining before using works correctly
let script = r#"
name = "Louis"
echo "Hello" $name
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_for_loop_variable_scope() {
// Test that for loop variables are properly scoped
let script = r#"
for i in 1 2 3 {
echo "Number:" $i
}
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_for_loop_variable_isolation() {
// Test that for loop variables don't leak to outer scope
let script = r#"
for i in 1 2 3 {
echo "Inside:" $i
}
echo "Outside:" $i
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_err());
let err_msg = format!("{:?}", result.unwrap_err());
assert!(err_msg.contains("i"));
}
#[test]
fn test_parallel_block_variable_access() {
// Test that parallel blocks can access outer scope variables
let script = r#"
name = "Test"
parallel {
run { echo "Task 1:" $name }
run { echo "Task 2:" $name }
}
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_parallel_block_undefined_variable() {
// Test that parallel blocks catch undefined variables
let script = r#"
parallel {
run { echo "Task:" $undefined }
}
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_err());
}
#[test]
fn test_workers_block_variable_access() {
// Test that workers blocks can access outer scope variables
let script = r#"
msg = "Worker"
workers 2 {
echo $msg "Task 1"
echo $msg "Task 2"
}
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_workers_with_for_loop() {
// Test that workers can contain for loops with variables
let script = r#"
prefix = "Item"
workers 2 {
for num in 1 2 3 {
echo $prefix $num
}
}
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_nested_for_loops() {
// Test nested for loops with proper scoping
let script = r#"
for i in 1 2 {
for j in a b {
echo $i $j
}
}
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_multiple_variables() {
// Test multiple variable definitions and usage
let script = r#"
user = "Alice"
project = "RushShell"
version = "1.0"
echo $user "is working on" $project "version" $version
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_for_loop_with_outer_variables() {
// Test for loops can access both loop vars and outer vars
let script = r#"
prefix = "Item"
for num in 1 2 3 {
echo $prefix $num
}
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
#[test]
fn test_comprehensive_variable_usage() {
// Test a comprehensive script with all variable features
let script = r#"
user = "Alice"
project = "RushShell"
echo "Welcome" $user "to" $project
for item in "docs" "tests" "src" {
echo $user "is checking" $item
}
parallel {
run { echo "Parallel 1:" $user }
run { echo "Parallel 2:" $project }
}
workers 2 {
for num in 1 2 {
echo $project "worker task" $num
}
}
echo "Done with" $project
"#;
let parser = Parser::new();
let result = parser.parse(script);
assert!(result.is_ok());
}
}

42
tests/lexer_tests.rs Normal file
View File

@@ -0,0 +1,42 @@
use rush::lexer::{Lexer, Token};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tokenization() {
let mut lexer = Lexer::new("x = 10");
assert_eq!(lexer.next_token(), Token::Identifier("x".to_string()));
assert_eq!(lexer.next_token(), Token::Assign);
assert_eq!(lexer.next_token(), Token::Number(10));
assert_eq!(lexer.next_token(), Token::Eof);
}
#[test]
fn test_identifier_tokens() {
let mut lexer = Lexer::new("foo bar");
assert_eq!(lexer.next_token(), Token::Identifier("foo".to_string()));
assert_eq!(lexer.next_token(), Token::Identifier("bar".to_string()));
assert_eq!(lexer.next_token(), Token::Eof);
}
#[test]
fn test_control_flow_tokens() {
let mut lexer = Lexer::new("if else while");
assert_eq!(lexer.next_token(), Token::If);
assert_eq!(lexer.next_token(), Token::Else);
assert_eq!(lexer.next_token(), Token::While);
assert_eq!(lexer.next_token(), Token::Eof);
}
#[test]
fn test_operators() {
let mut lexer = Lexer::new("+ - * /");
assert_eq!(lexer.next_token(), Token::Plus);
assert_eq!(lexer.next_token(), Token::Minus);
assert_eq!(lexer.next_token(), Token::Multiply);
assert_eq!(lexer.next_token(), Token::Divide);
assert_eq!(lexer.next_token(), Token::Eof);
}
}

64
tests/parser_tests.rs Normal file
View File

@@ -0,0 +1,64 @@
use rush::parser::{AstNode, Parser};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_variable_assignment() {
// Test parsing a variable assignment
let parser = Parser::new();
let input = "x = \"10\"";
let result = parser.parse(input);
assert!(result.is_ok());
let program = result.unwrap();
assert_eq!(program.statements.len(), 1);
match &program.statements[0] {
AstNode::VariableAssignment { name, .. } => {
assert_eq!(name, "x");
}
_ => panic!("Expected variable assignment"),
}
}
#[test]
fn test_command_parsing() {
// Test parsing a simple command
let parser = Parser::new();
let input = "echo \"Hello World\"";
let result = parser.parse(input);
assert!(result.is_ok());
let program = result.unwrap();
assert_eq!(program.statements.len(), 1);
match &program.statements[0] {
AstNode::Command { name, .. } => {
assert_eq!(name, "echo");
}
_ => panic!("Expected command"),
}
}
#[test]
fn test_multiple_statements() {
// Test parsing multiple statements
let parser = Parser::new();
let input = "name = \"Louis\"\necho \"Hello\" $name";
let result = parser.parse(input);
assert!(result.is_ok());
let program = result.unwrap();
assert_eq!(program.statements.len(), 2);
}
#[test]
fn test_skip_comments() {
// Test that comments are skipped
let parser = Parser::new();
let input = "# This is a comment\necho \"test\"";
let result = parser.parse(input);
assert!(result.is_ok());
let program = result.unwrap();
assert_eq!(program.statements.len(), 1);
}
}