#![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_011_pwd_symlink_resolution() {
let pwd_symlink = r#"
# If /home/user/project -> /mnt/storage/projects/myapp
cd /home/user/project
# Logical path (shows symlink)
pwd -L
# Output: /home/user/project
# Physical path (resolves symlink)
pwd -P
# Output: /mnt/storage/projects/myapp
# Get canonical path
canonical_path=$(pwd -P)
"#;
let mut lexer = Lexer::new(pwd_symlink);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "pwd symlink should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_011_pwd_edge_cases() {
let pwd_edge_cases = r#"
# Edge case: directory deleted
# mkdir /tmp/test && cd /tmp/test && rm -rf /tmp/test
# pwd # May fail with error
# Edge case: no permissions
# cd /root/private (as non-root)
# pwd # May fail with permission error
# Edge case: $PWD can be manually modified
PWD="/fake/path"
pwd # Still shows real directory
echo $PWD # Shows /fake/path
# Edge case: chroot environment
# pwd shows path relative to chroot, not actual system path
"#;
let mut lexer = Lexer::new(pwd_edge_cases);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "pwd edge cases should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
#[test]
fn test_BUILTIN_011_pwd_comparison_table() {
let pwd_comparison = r#"
# POSIX SUPPORTED (bashrs SUPPORTED):
pwd # Print current working directory
pwd -L # Logical path (follow symlinks, default)
pwd -P # Physical path (resolve symlinks)
# Common usage patterns:
current=$(pwd) # Save current directory
old=$(pwd); cd /tmp; cd "$old" # Save and restore
# Script directory pattern:
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# Symlink handling:
# cd /path/to/symlink
pwd -L # Shows symlink path
pwd -P # Shows real path
# pwd vs $PWD:
echo $(pwd) # Command (always accurate)
echo $PWD # Variable (can be modified)
# Best practices:
dir="$(pwd)" # Quote for safety
[ "$(pwd)" = "/etc" ] # Directory check
canonical="$(pwd -P)" # Get canonical path
# Exit status:
if pwd; then
echo "Success"
fi
"#;
let mut lexer = Lexer::new(pwd_comparison);
match lexer.tokenize() {
Ok(tokens) => {
assert!(!tokens.is_empty(), "pwd comparison should tokenize");
let _ = tokens; }
Err(_) => {
}
}
}
const BUILTIN_016_TEST_COMMAND_INPUT: &str = r#"
if [ -f "file.txt" ]; then
echo "File exists"
fi
if [ -d "/tmp" ]; then
echo "Directory exists"
fi
if [ "$user" = "admin" ]; then
echo "Admin user"
fi
if [ "$count" -gt 10 ]; then
echo "Count is greater than 10"
fi
"#;
#[test]
fn test_BUILTIN_016_test_command_supported() {
assert_tokenizes(
BUILTIN_016_TEST_COMMAND_INPUT,
"test command should tokenize successfully",
);
}
const BUILTIN_016_FILE_TESTS_INPUT: &str = r#"
# File type tests
if [ -f "/etc/passwd" ]; then echo "regular file"; fi
if [ -d "/tmp" ]; then echo "directory"; fi
if [ -e "/dev/null" ]; then echo "exists"; fi
if [ -L "/usr/bin/vi" ]; then echo "symlink"; fi
# Permission tests
if [ -r "file.txt" ]; then echo "readable"; fi
if [ -w "file.txt" ]; then echo "writable"; fi
if [ -x "script.sh" ]; then echo "executable"; fi
# Size test
if [ -s "data.txt" ]; then echo "non-empty"; fi
"#;
#[test]
fn test_BUILTIN_016_test_file_tests() {
assert_tokenizes(
BUILTIN_016_FILE_TESTS_INPUT,
"file test operators should tokenize",
);
}
const BUILTIN_016_STRING_TESTS_INPUT: &str = r#"
# Empty/non-empty tests
if [ -z "$empty_var" ]; then echo "empty"; fi
if [ -n "$non_empty_var" ]; then echo "non-empty"; fi
# String equality (POSIX uses =, not ==)
if [ "$user" = "admin" ]; then echo "admin user"; fi
if [ "$status" != "error" ]; then echo "ok"; fi
# Always quote variables in tests
if [ -z "$var" ]; then echo "var is empty"; fi
if [ "$a" = "$b" ]; then echo "equal"; fi
"#;
#[test]
fn test_BUILTIN_016_test_string_tests() {
assert_tokenizes(
BUILTIN_016_STRING_TESTS_INPUT,
"string test operators should tokenize",
);
}
const BUILTIN_016_INTEGER_TESTS_INPUT: &str = r#"
# Integer comparisons
if [ "$count" -eq 0 ]; then echo "zero"; fi
if [ "$count" -ne 0 ]; then echo "non-zero"; fi
if [ "$count" -lt 10 ]; then echo "less than 10"; fi
if [ "$count" -le 10 ]; then echo "at most 10"; fi
if [ "$count" -gt 10 ]; then echo "greater than 10"; fi
if [ "$count" -ge 10 ]; then echo "at least 10"; fi
# Common patterns
if [ "$retries" -lt "$max_retries" ]; then
echo "Retry available"
fi
if [ "$exit_code" -ne 0 ]; then
echo "Command failed"
fi
"#;
#[test]
fn test_BUILTIN_016_test_integer_tests() {
assert_tokenizes(
BUILTIN_016_INTEGER_TESTS_INPUT,
"integer test operators should tokenize",
);
}
const BUILTIN_016_LOGICAL_TESTS_INPUT: &str = r#"
# Logical NOT
if [ ! -f "missing.txt" ]; then echo "file does not exist"; fi
# Logical AND (modern style - preferred)
if [ -f "file.txt" ] && [ -r "file.txt" ]; then
cat file.txt
fi
# Logical OR (modern style - preferred)
if [ "$status" = "ok" ] || [ "$status" = "success" ]; then
echo "Operation succeeded"
fi
# Logical AND (old style - deprecated but valid)
if [ -f "file.txt" -a -r "file.txt" ]; then
cat file.txt
fi
# Logical OR (old style - deprecated but valid)
if [ "$a" = "1" -o "$a" = "2" ]; then
echo "a is 1 or 2"
fi
# Complex logic with negation
if [ ! -z "$var" ] && [ -f "$var" ]; then
echo "$var is a non-empty filename"
fi
"#;
#[test]
fn test_BUILTIN_016_test_logical_operators() {
assert_tokenizes(
BUILTIN_016_LOGICAL_TESTS_INPUT,
"logical operators should tokenize",
);
}
const BUILTIN_016_BASH_EXTENSIONS_INPUT: &str = r#"
# BASH EXTENSION: [[ ]] compound command (NOT SUPPORTED)
# Purify: Replace [[ ]] with [ ]
# if [[ -f "file.txt" ]]; then echo "exists"; fi
# →
if [ -f "file.txt" ]; then echo "exists"; fi
# BASH EXTENSION: == operator (NOT SUPPORTED)
# Purify: Replace == with =
# if [[ "$user" == "admin" ]]; then echo "admin"; fi
# →
if [ "$user" = "admin" ]; then echo "admin"; fi
# BASH EXTENSION: =~ regex (NOT SUPPORTED)
# Purify: Use grep instead
# if [[ "$email" =~ ^[a-z]+@[a-z]+\.com$ ]]; then echo "valid"; fi
# →
if printf '%s' "$email" | grep -qE '^[a-z]+@[a-z]+\.com$'; then
echo "valid"
fi
# BASH EXTENSION: Pattern matching with == (NOT SUPPORTED)
# Purify: Use case statement
# if [[ "$file" == *.txt ]]; then echo "text file"; fi
# →
case "$file" in
*.txt)
echo "text file"
;;
esac
# BASH EXTENSION: < > without escaping (NOT SUPPORTED)
# Purify: Add backslash escaping
# if [[ "$a" < "$b" ]]; then echo "less"; fi
# →
if [ "$a" \< "$b" ]; then echo "less"; fi
"#;
#[test]
fn test_BUILTIN_016_test_bash_extensions_not_supported() {
assert_tokenizes(
BUILTIN_016_BASH_EXTENSIONS_INPUT,
"bash extension examples should tokenize",
);
}
const BUILTIN_016_COMMON_PATTERNS_INPUT: &str = r#"
# Pattern 1: Safe file operations
if [ -f "config.sh" ]; then
. config.sh
fi
# Pattern 2: Variable validation
if [ -z "$REQUIRED_VAR" ]; then
echo "Error: REQUIRED_VAR is not set"
exit 1
fi
# Pattern 3: Default values
if [ -z "$PORT" ]; then
PORT=8080
fi
# Pattern 4: Error checking
command_that_might_fail
if [ "$?" -ne 0 ]; then
echo "Command failed with exit code $?"
exit 1
fi
# Pattern 5: Defensive programming
if [ ! -d "$install_dir" ]; then
echo "Error: Install directory does not exist: $install_dir"
exit 1
fi
# Pattern 6: Multi-condition validation
if [ -f "$script" ] && [ -r "$script" ] && [ -x "$script" ]; then
"$script"
else
echo "Error: $script is not a readable executable file"
exit 1
fi
# Pattern 7: Alternative values
if [ -n "$CUSTOM_PATH" ]; then
PATH="$CUSTOM_PATH"
else
PATH="/usr/local/bin:/usr/bin:/bin"
fi
"#;
#[test]
fn test_BUILTIN_016_test_common_patterns() {
assert_tokenizes(
BUILTIN_016_COMMON_PATTERNS_INPUT,
"common test patterns should tokenize",
);
}