completely rewrote test suite for top level src files and all builtin files
This commit is contained in:
@@ -94,7 +94,7 @@ impl FromStr for TestOp {
|
||||
"-ge" => Ok(Self::IntGe),
|
||||
"-le" => Ok(Self::IntLe),
|
||||
_ if TEST_UNARY_OPS.contains(&s) => Ok(Self::Unary(s.parse::<UnaryOp>()?)),
|
||||
_ => Err(ShErr::simple(ShErrKind::SyntaxErr, "Invalid test operator")),
|
||||
_ => Err(ShErr::simple(ShErrKind::SyntaxErr, format!("Invalid test operator '{}'", s))),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -121,6 +121,7 @@ pub fn double_bracket_test(node: Node) -> ShResult<bool> {
|
||||
};
|
||||
let mut last_result = false;
|
||||
let mut conjunct_op: Option<ConjunctOp>;
|
||||
log::trace!("test cases: {:#?}", cases);
|
||||
|
||||
for case in cases {
|
||||
let result = match case {
|
||||
@@ -290,21 +291,332 @@ pub fn double_bracket_test(node: Node) -> ShResult<bool> {
|
||||
}
|
||||
};
|
||||
|
||||
last_result = result;
|
||||
if let Some(op) = conjunct_op {
|
||||
match op {
|
||||
ConjunctOp::And if !last_result => {
|
||||
last_result = result;
|
||||
break;
|
||||
}
|
||||
ConjunctOp::Or if last_result => {
|
||||
last_result = result;
|
||||
break;
|
||||
}
|
||||
ConjunctOp::And if !last_result => break,
|
||||
ConjunctOp::Or if last_result => break,
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
last_result = result;
|
||||
}
|
||||
}
|
||||
Ok(last_result)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::fs;
|
||||
use tempfile::{TempDir, NamedTempFile};
|
||||
use crate::state;
|
||||
use crate::testutil::{TestGuard, test_input};
|
||||
|
||||
// ===================== Unary: file tests =====================
|
||||
|
||||
#[test]
|
||||
fn test_exists_true() {
|
||||
let _g = TestGuard::new();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
test_input(format!("[[ -e {} ]]", file.path().display())).unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exists_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -e /tmp/__no_such_file_test_rs__ ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_directory() {
|
||||
let _g = TestGuard::new();
|
||||
let dir = TempDir::new().unwrap();
|
||||
test_input(format!("[[ -d {} ]]", dir.path().display())).unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_directory_false() {
|
||||
let _g = TestGuard::new();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
test_input(format!("[[ -d {} ]]", file.path().display())).unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_file() {
|
||||
let _g = TestGuard::new();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
test_input(format!("[[ -f {} ]]", file.path().display())).unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_file_false() {
|
||||
let _g = TestGuard::new();
|
||||
let dir = TempDir::new().unwrap();
|
||||
test_input(format!("[[ -f {} ]]", dir.path().display())).unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_readable() {
|
||||
let _g = TestGuard::new();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
test_input(format!("[[ -r {} ]]", file.path().display())).unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_writable() {
|
||||
let _g = TestGuard::new();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
test_input(format!("[[ -w {} ]]", file.path().display())).unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_empty_file() {
|
||||
let _g = TestGuard::new();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
fs::write(file.path(), "content").unwrap();
|
||||
test_input(format!("[[ -s {} ]]", file.path().display())).unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_empty_file() {
|
||||
let _g = TestGuard::new();
|
||||
let file = NamedTempFile::new().unwrap();
|
||||
test_input(format!("[[ -s {} ]]", file.path().display())).unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
// ===================== Unary: string tests =====================
|
||||
|
||||
#[test]
|
||||
fn test_non_null_true() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -n hello ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_null_empty() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -n '' ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_true() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -z '' ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -z hello ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
// ===================== Binary: string comparison =====================
|
||||
|
||||
#[test]
|
||||
fn test_string_eq() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ hello == hello ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_eq_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ hello == world ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_neq() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ hello != world ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_neq_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ hello != hello ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_glob_match() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ hello == hel* ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_glob_no_match() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ hello == wor* ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
// ===================== Binary: integer comparison =====================
|
||||
|
||||
#[test]
|
||||
fn test_int_eq() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 42 -eq 42 ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_eq_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 42 -eq 43 ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_ne() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 1 -ne 2 ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_gt() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 10 -gt 5 ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_gt_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 5 -gt 10 ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_lt() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 5 -lt 10 ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_ge() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 10 -ge 10 ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_le() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ 5 -le 5 ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_negative() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -5 -lt 0 ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_int_non_integer_errors() {
|
||||
let _g = TestGuard::new();
|
||||
let result = test_input("[[ abc -eq 1 ]]");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
// ===================== Binary: regex match =====================
|
||||
|
||||
#[test]
|
||||
fn test_regex_match() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ hello123 =~ ^hello[0-9]+$ ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regex_no_match() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ goodbye =~ ^hello ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
// ===================== Conjuncts =====================
|
||||
|
||||
#[test]
|
||||
fn test_and_both_true() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -n hello && -n world ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_and_first_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -z hello && -n world ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_or_first_true() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -n hello || -z hello ]]").unwrap();
|
||||
assert_eq!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_or_both_false() {
|
||||
let _g = TestGuard::new();
|
||||
test_input("[[ -z hello || -z world ]]").unwrap();
|
||||
assert_ne!(state::get_status(), 0);
|
||||
}
|
||||
|
||||
// ===================== Pure: operator parsing =====================
|
||||
|
||||
#[test]
|
||||
fn parse_unary_ops() {
|
||||
use super::UnaryOp;
|
||||
use std::str::FromStr;
|
||||
for op in ["-e", "-d", "-f", "-h", "-L", "-r", "-w", "-x", "-s",
|
||||
"-p", "-S", "-b", "-c", "-k", "-O", "-G", "-N", "-u",
|
||||
"-g", "-t", "-n", "-z"] {
|
||||
assert!(UnaryOp::from_str(op).is_ok(), "failed to parse {op}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_invalid_unary_op() {
|
||||
use super::UnaryOp;
|
||||
use std::str::FromStr;
|
||||
assert!(UnaryOp::from_str("-Q").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_binary_ops() {
|
||||
use super::TestOp;
|
||||
use std::str::FromStr;
|
||||
for op in ["==", "!=", "=~", "-eq", "-ne", "-gt", "-lt", "-ge", "-le"] {
|
||||
assert!(TestOp::from_str(op).is_ok(), "failed to parse {op}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_invalid_binary_op() {
|
||||
use super::TestOp;
|
||||
use std::str::FromStr;
|
||||
assert!(TestOp::from_str("~=").is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user