#![allow(clippy::unwrap_used)]
#![allow(unused_imports)]
use super::super::ast::Redirect;
use super::super::lexer::Lexer;
use super::super::parser::BashParser;
use super::super::semantic::SemanticAnalyzer;
use super::super::*;
#[test]
fn test_BUILTIN_009_exit_command_supported() {
let exit_command = r#"
exit 0
exit 1
exit 2
exit
exit $?
"#;
let mut lexer = Lexer::new(exit_command);
match lexer.tokenize() {
Ok(tokens) => {
assert!(
!tokens.is_empty(),
"exit command should tokenize successfully"
);
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_009_exit_with_status_code() {
let exit_status = r#"
exit 0
exit 1
exit 2
exit 126
exit 127
exit 130
"#;
let mut lexer = Lexer::new(exit_status);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "exit with status should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_009_exit_no_args() {
let exit_no_args = r#"
command_that_fails
exit
"#;
let mut lexer = Lexer::new(exit_no_args);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "exit with no args should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_009_exit_vs_return() {
let exit_vs_return = r#"
function my_func() {
if [ error ]; then
return 1 # Returns from function only
fi
exit 1 # Terminates entire script
}
# In subshell
(
exit 1 # Terminates subshell only
)
echo "Parent continues"
"#;
let mut lexer = Lexer::new(exit_vs_return);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "exit vs return should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_009_exit_standard_codes() {
let exit_codes = r#"
# Success
exit 0
# General error
exit 1
# Misuse of shell builtin
exit 2
# Permission problem or command is not executable
exit 126
# Command not found
exit 127
# Invalid argument to exit
exit 128
# Fatal error signal (e.g., 130 = 128+2 for SIGINT/Ctrl-C)
exit 130
# Exit status out of range
exit 255
"#;
let mut lexer = Lexer::new(exit_codes);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "exit codes should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_009_exit_conditional() {
let exit_conditional = r#"
# Exit if variable is empty
[ -z "$VAR" ] && exit 1
# Exit if command fails
command || exit 1
# Exit with error message
[ ! -f "$FILE" ] && { echo "File not found"; exit 1; }
# Early return pattern
if [ error ]; then
echo "Error occurred"
exit 1
fi
"#;
let mut lexer = Lexer::new(exit_conditional);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "conditional exit should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_009_exit_edge_cases() {
let exit_edge_cases = r#"
# Bash wrapping (NOT SUPPORTED in bashrs):
# exit 256 # Wraps to 0 in bash
# exit 257 # Wraps to 1 in bash
# exit -1 # Wraps to 255 in bash
# Subshell termination (SUPPORTED):
(exit 1)
echo "Parent continues after subshell exit"
# Function termination (SUPPORTED):
function func() {
exit 1 # Terminates entire script, not just function
}
"#;
let mut lexer = Lexer::new(exit_edge_cases);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "exit edge cases should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_009_exit_comparison_table() {
let exit_comparison = r#"
# POSIX SUPPORTED (bashrs SUPPORTED):
exit 0 # Success exit
exit 1 # General error
exit 2 # Misuse of builtin
exit # Exit with last command status
exit $? # Explicit last status
exit 126 # Cannot execute
exit 127 # Command not found
exit 130 # Signal exit (128+2 for SIGINT)
# Bash extensions (bashrs NOT SUPPORTED):
# exit 256 # Wraps to 0 (bash only)
# exit 257 # Wraps to 1 (bash only)
# exit -1 # Wraps to 255 (bash only)
# Exit behavior (POSIX):
function my_function() {
exit 1 # Terminates entire script
}
(
exit 1 # Terminates subshell only
)
echo "Parent continues"
# Common patterns:
command || exit 1 # Exit if command fails
[ -z "$VAR" ] && exit 1 # Exit if variable empty
trap "exit 1" INT # Exit on Ctrl-C
# Best practices:
# - Use exit 0 for success
# - Use exit 1 for general errors
# - Use specific codes (2-125) for different error types
# - Document exit codes in script header
# - Use return (not exit) in functions when appropriate
"#;
let mut lexer = Lexer::new(exit_comparison);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "exit comparison should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
include!("part4_2_builtin_010.rs");