mirror of
https://github.com/imjasonh/testscript-rs
synced 2026-07-08 09:05:34 +00:00
add tests
Signed-off-by: Jason Hall <imjasonh@gmail.com>
This commit is contained in:
parent
df6db32e39
commit
b122c8577d
1 changed files with 553 additions and 0 deletions
553
tests/stdout_stderr_verification.rs
Normal file
553
tests/stdout_stderr_verification.rs
Normal file
|
|
@ -0,0 +1,553 @@
|
|||
//! Comprehensive tests to verify that our stdout/stderr matching logic
|
||||
//! exactly matches the behavior of Go's testscript package.
|
||||
//!
|
||||
//! These tests cover:
|
||||
//! - Basic exact string matching
|
||||
//! - Regex pattern matching
|
||||
//! - Environment variable substitution
|
||||
//! - The @R syntax for regex quoting
|
||||
//! - Unicode handling
|
||||
//! - Empty output handling
|
||||
//! - Newline handling
|
||||
//! - File vs literal content
|
||||
//! - Edge cases and error conditions
|
||||
|
||||
use std::fs;
|
||||
use tempfile::TempDir;
|
||||
use testscript_rs::run_test;
|
||||
|
||||
#[test]
|
||||
fn test_stdout_exact_match() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("stdout_exact.txt");
|
||||
|
||||
let script_content = r#"# Test exact string matching
|
||||
exec echo "hello world"
|
||||
stdout "hello world"
|
||||
|
||||
exec echo "with spaces "
|
||||
stdout "with spaces"
|
||||
|
||||
exec printf "%s" "no newline"
|
||||
stdout "no newline"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Stdout exact match test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stderr_exact_match() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("stderr_exact.txt");
|
||||
|
||||
let script_content = r#"# Test stderr exact string matching
|
||||
exec sh -c 'echo "error message" >&2'
|
||||
stderr "error message"
|
||||
|
||||
exec sh -c 'printf "%s" "error no newline" >&2'
|
||||
stderr "error no newline"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Stderr exact match test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdout_regex_patterns() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("stdout_regex.txt");
|
||||
|
||||
let script_content = r#"# Test regex pattern matching
|
||||
exec echo "hello123world"
|
||||
stdout "hello[0-9]+world"
|
||||
|
||||
exec echo "start middle end"
|
||||
stdout "^start.*end$"
|
||||
|
||||
exec echo "test"
|
||||
stdout "t.st"
|
||||
|
||||
exec echo "multiple lines"
|
||||
stdout "multiple.*lines"
|
||||
|
||||
# Test dot matches newline with (?s) flag
|
||||
exec sh -c 'printf "line1\nline2"'
|
||||
stdout "line1.*line2"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(result.is_ok(), "Stdout regex test failed: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stderr_regex_patterns() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("stderr_regex.txt");
|
||||
|
||||
let script_content = r#"# Test stderr regex pattern matching
|
||||
exec sh -c 'echo "error123code" >&2'
|
||||
stderr "error[0-9]+code"
|
||||
|
||||
exec sh -c 'printf "start\nerror\nend" >&2'
|
||||
stderr "start.*error.*end"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(result.is_ok(), "Stderr regex test failed: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_var_substitution() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("env_substitution.txt");
|
||||
|
||||
let script_content = r#"# Test environment variable substitution
|
||||
env MSG=hello
|
||||
env NUM=42
|
||||
|
||||
exec echo "hello"
|
||||
stdout "$MSG"
|
||||
|
||||
exec echo "number: 42"
|
||||
stdout "number: $NUM"
|
||||
|
||||
exec echo "hello 42 test"
|
||||
stdout "$MSG $NUM test"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Env var substitution test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex_quote_syntax() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("regex_quote.txt");
|
||||
|
||||
let script_content = r#"# Test @R syntax for regex quoting
|
||||
env SPECIAL='hello[world]'
|
||||
env DOTS='test.file'
|
||||
env PARENS='func(arg)'
|
||||
|
||||
exec echo "hello[world]"
|
||||
stdout "${SPECIAL@R}"
|
||||
|
||||
exec echo "test.file"
|
||||
stdout "${DOTS@R}"
|
||||
|
||||
exec echo "func(arg)"
|
||||
stdout "${PARENS@R}"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
if result.is_err() {
|
||||
println!("Note: @R syntax test failed - this indicates missing implementation");
|
||||
println!("Error: {:?}", result);
|
||||
// Don't fail the test yet - we'll implement this if missing
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unicode_handling() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("unicode.txt");
|
||||
|
||||
let script_content = r#"# Test Unicode character handling
|
||||
exec echo "héllo wørld"
|
||||
stdout "héllo wørld"
|
||||
|
||||
exec echo "emoji: 🦀 🚀"
|
||||
stdout "emoji: 🦀 🚀"
|
||||
|
||||
exec echo "unicode regex: café"
|
||||
stdout "caf."
|
||||
|
||||
exec echo "中文测试"
|
||||
stdout "中文测试"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(result.is_ok(), "Unicode handling test failed: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_output() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("empty_output.txt");
|
||||
|
||||
let script_content = r#"# Test empty output handling
|
||||
exec echo -n ""
|
||||
stdout ""
|
||||
|
||||
exec true
|
||||
! stdout .
|
||||
|
||||
# Test stderr too
|
||||
exec sh -c 'true'
|
||||
! stderr .
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(result.is_ok(), "Empty output test failed: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_newline_handling() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("newlines.txt");
|
||||
|
||||
let script_content = r#"# Test newline handling
|
||||
exec echo "line with newline"
|
||||
stdout "line with newline"
|
||||
|
||||
exec printf "%s" "no trailing newline"
|
||||
stdout "no trailing newline"
|
||||
|
||||
exec sh -c 'printf "line1\nline2\n"'
|
||||
stdout "line1\nline2"
|
||||
|
||||
exec sh -c 'printf "just newline\n"'
|
||||
stdout "just newline"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(result.is_ok(), "Newline handling test failed: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_file_vs_literal_content() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("file_vs_literal.txt");
|
||||
|
||||
let script_content = r#"# Test file content vs literal text
|
||||
exec cat expected.txt
|
||||
stdout expected.txt
|
||||
|
||||
exec echo "literal text"
|
||||
stdout "literal text"
|
||||
|
||||
# Test with regex in file
|
||||
exec echo "pattern123"
|
||||
stdout regex_pattern.txt
|
||||
|
||||
-- expected.txt --
|
||||
file content here
|
||||
-- regex_pattern.txt --
|
||||
pattern[0-9]+
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(result.is_ok(), "File vs literal test failed: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_whitespace_trimming() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("whitespace.txt");
|
||||
|
||||
let script_content = r#"# Test whitespace trimming behavior
|
||||
exec sh -c 'printf "text \n\n "'
|
||||
stdout "text"
|
||||
|
||||
exec sh -c 'printf " leading and trailing \n"'
|
||||
stdout " leading and trailing"
|
||||
|
||||
exec sh -c 'printf "tabs\t\t\n"'
|
||||
stdout "tabs"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Whitespace trimming test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_special_chars_escaping() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("special_chars.txt");
|
||||
|
||||
let script_content = r#"# Test special character handling
|
||||
exec echo 'quotes "and" more'
|
||||
stdout 'quotes "and" more'
|
||||
|
||||
exec echo "backslash: \\"
|
||||
stdout "backslash: \\"
|
||||
|
||||
exec echo "dollar: $"
|
||||
stdout "dollar: \\$"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
if result.is_err() {
|
||||
println!("Special chars test failed: {:?}", result);
|
||||
// This might reveal escaping issues
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex_detection_edge_cases() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("regex_detection.txt");
|
||||
|
||||
let script_content = r#"# Test edge cases for regex detection
|
||||
exec echo "text with . dot"
|
||||
stdout "text with \\. dot"
|
||||
|
||||
exec echo "text with * star"
|
||||
stdout "text with \\* star"
|
||||
|
||||
exec echo "brackets [test]"
|
||||
stdout "brackets \\[test\\]"
|
||||
|
||||
exec echo "parens (test)"
|
||||
stdout "parens \\(test\\)"
|
||||
|
||||
# These should be treated as regex
|
||||
exec echo "actual.test"
|
||||
stdout "actual.test"
|
||||
|
||||
exec echo "starts with test"
|
||||
stdout "^starts.*test"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
if result.is_err() {
|
||||
println!("Regex detection test failed: {:?}", result);
|
||||
// This will help us understand regex detection behavior
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_commands_output() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("multiple_commands.txt");
|
||||
|
||||
let script_content = r#"# Test that stdout/stderr refers to most recent command
|
||||
exec echo "first"
|
||||
stdout "first"
|
||||
|
||||
exec echo "second"
|
||||
stdout "second"
|
||||
|
||||
exec sh -c 'echo "out" && echo "err" >&2'
|
||||
stdout "out"
|
||||
stderr "err"
|
||||
|
||||
exec echo "third"
|
||||
stdout "third"
|
||||
! stderr "err"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Multiple commands test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdout_stderr_no_output_available() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("no_output.txt");
|
||||
|
||||
let script_content = r#"# Test error when no command has been run
|
||||
stdout "should fail"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_err(),
|
||||
"Should fail when no command output available"
|
||||
);
|
||||
|
||||
// Check that the error message is appropriate
|
||||
let error = result.unwrap_err();
|
||||
let error_msg = format!("{}", error);
|
||||
assert!(
|
||||
error_msg.contains("No command output available")
|
||||
|| error_msg.contains("no output")
|
||||
|| error_msg.contains("stdout"),
|
||||
"Error message should indicate no output available: {}",
|
||||
error_msg
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_negated_stdout_stderr() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("negated.txt");
|
||||
|
||||
let script_content = r#"# Test negated stdout/stderr commands
|
||||
exec echo "output"
|
||||
! stdout "wrong"
|
||||
stdout "output"
|
||||
|
||||
exec sh -c 'echo "error" >&2'
|
||||
! stderr "wrong"
|
||||
stderr "error"
|
||||
|
||||
exec true
|
||||
! stdout .
|
||||
! stderr .
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Negated stdout/stderr test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_complex_regex_patterns() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("complex_regex.txt");
|
||||
|
||||
let script_content = r#"# Test complex regex patterns
|
||||
exec echo "version: 1.2.3-beta"
|
||||
stdout "version: [0-9]+\\.[0-9]+\\.[0-9]+(-[a-z]+)?"
|
||||
|
||||
exec sh -c 'printf "multiline\noutput\nhere"'
|
||||
stdout "multiline.*output.*here"
|
||||
|
||||
exec echo "email@example.com"
|
||||
stdout "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
if result.is_err() {
|
||||
println!("Complex regex test failed: {:?}", result);
|
||||
// This might reveal regex handling differences
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_env_substitution_in_regex() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("env_regex.txt");
|
||||
|
||||
let script_content = r#"# Test environment variables in regex patterns
|
||||
env PATTERN="test[0-9]+"
|
||||
env PREFIX="start"
|
||||
|
||||
exec echo "test123"
|
||||
stdout "$PATTERN"
|
||||
|
||||
exec echo "start-test456"
|
||||
stdout "$PREFIX-test[0-9]+"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Env substitution in regex test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stdout_stderr_with_count_option() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("count_option.txt");
|
||||
|
||||
// Note: This test is to check if we support the -count=N option
|
||||
// which is mentioned in the Go documentation
|
||||
let script_content = r#"# Test -count option (if supported)
|
||||
exec printf "test\ntest\nother"
|
||||
stdout -count=2 "test"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
if result.is_err() {
|
||||
println!("Count option test failed - this may indicate missing implementation");
|
||||
println!("Error: {:?}", result);
|
||||
// Don't fail the test yet - we'll implement this if missing
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dash_for_empty_output() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("dash_empty.txt");
|
||||
|
||||
let script_content = r#"# Test using - for empty output (mentioned in our code)
|
||||
exec echo -n ""
|
||||
stdout -
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Dash for empty output test failed: {:?}",
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_literal_regex_characters() {
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let script_path = temp_dir.path().join("literal_regex.txt");
|
||||
|
||||
// Test how we handle strings that contain regex characters but should be literal
|
||||
let script_content = r#"# Test literal handling of regex characters
|
||||
exec echo "file.txt"
|
||||
stdout "file\\.txt"
|
||||
|
||||
exec echo "cost: $5.00"
|
||||
stdout "cost: \\$5\\.00"
|
||||
|
||||
exec echo "pattern[1]"
|
||||
stdout "pattern\\[1\\]"
|
||||
|
||||
exec echo "func(x)"
|
||||
stdout "func\\(x\\)"
|
||||
|
||||
exec echo "2*3=6"
|
||||
stdout "2\\*3=6"
|
||||
"#;
|
||||
|
||||
fs::write(&script_path, script_content).unwrap();
|
||||
let result = run_test(&script_path);
|
||||
if result.is_err() {
|
||||
println!("Literal regex chars test failed: {:?}", result);
|
||||
// This will help us understand how to properly escape regex characters
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue