use std::io::Write;
use std::path::PathBuf;
use std::sync::atomic::AtomicBool;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use epsh::ast;
use epsh::builtins;
use epsh::encoding;
use epsh::error;
use epsh::eval;
use epsh::expand;
use epsh::glob;
use epsh::lexer;
use epsh::parser;
use epsh::var;
#[test]
fn shell_construction() {
let _shell = eval::Shell::new();
let _shell: eval::Shell = Default::default();
let _shell = eval::Shell::builder().build();
}
#[test]
fn shell_builder_methods() {
let cancel = Arc::new(AtomicBool::new(false));
let sink: Arc<Mutex<dyn Write + Send>> = Arc::new(Mutex::new(Vec::<u8>::new()));
let handler: eval::ExternalHandler = Box::new(|_args, _env| Ok(error::ExitStatus::SUCCESS));
let _shell = eval::Shell::builder()
.cwd(PathBuf::from("/"))
.errexit(true)
.nounset(true)
.xtrace(true)
.pipefail(true)
.interactive(true)
.cancel_flag(cancel)
.stdout_sink(sink.clone())
.stderr_sink(sink)
.timeout(Duration::from_secs(1))
.env_clear()
.external_handler(handler)
.build();
}
#[test]
fn shell_public_methods() {
let mut shell = eval::Shell::new();
let _code: i32 = shell.run_script("true");
let program = parser::Parser::new("true").parse().unwrap();
let _status: error::ExitStatus = shell.run_program(&program);
let _ = shell.set_var("X", "1");
let _val: Option<&str> = shell.get_var("X");
shell.set_args(&["arg0", "arg1"]);
shell.set_cwd(PathBuf::from("/tmp"));
shell.set_timeout(Duration::from_secs(60));
shell.set_cancel_flag(Arc::new(AtomicBool::new(false)));
shell.set_stdout_sink(Arc::new(Mutex::new(Vec::<u8>::new())));
shell.set_stderr_sink(Arc::new(Mutex::new(Vec::<u8>::new())));
shell.set_external_handler(Box::new(|_args, _env| Ok(error::ExitStatus::SUCCESS)));
shell.opts_mut().interactive = true;
let _p: PathBuf = shell.resolve_path("relative");
let _p: PathBuf = shell.resolve_path("/absolute");
let cmd = &parser::Parser::new("true").parse().unwrap().commands[0];
let _status = shell.eval_command(cmd);
}
#[test]
fn shell_accessor_methods() {
let mut shell = eval::Shell::new();
let _vars: &var::Variables = shell.vars();
let _funcs: &std::collections::HashMap<String, ast::Command> = shell.functions();
let _status: error::ExitStatus = shell.exit_status();
let _pid: u32 = shell.pid();
let _cwd: &std::path::Path = shell.cwd();
let _opts: &eval::ShellOpts = shell.opts();
shell.opts_mut().errexit = true;
shell.opts_mut().nounset = true;
shell.opts_mut().xtrace = true;
shell.opts_mut().pipefail = true;
}
#[test]
fn exit_status_api() {
let _: error::ExitStatus = error::ExitStatus::SUCCESS;
let _: error::ExitStatus = error::ExitStatus::FAILURE;
let _: error::ExitStatus = error::ExitStatus::MISUSE;
let _: error::ExitStatus = error::ExitStatus::NOT_FOUND;
let _: error::ExitStatus = error::ExitStatus::NOT_EXECUTABLE;
let s = error::ExitStatus::SUCCESS;
let _: i32 = s.code();
let _: bool = s.success();
let _: error::ExitStatus = s.inverted();
let _: error::ExitStatus = error::ExitStatus::from_bool(true);
let _: error::ExitStatus = error::ExitStatus::from_signal(2);
let _: error::ExitStatus = error::ExitStatus::from(42);
let _: i32 = i32::from(s);
assert_eq!(format!("{s}"), "0");
let _ = format!("{s:?}");
assert_eq!(s, s);
let _copy = s;
}
#[test]
fn shell_error_api() {
let _ = error::ShellError::Exit(error::ExitStatus::SUCCESS);
let _ = error::ShellError::Return(error::ExitStatus::FAILURE);
let _ = error::ShellError::Break(1);
let _ = error::ShellError::Continue(1);
let _ = error::ShellError::Syntax {
msg: "test".into(),
span: error::Span::default(),
};
let _ = error::ShellError::CommandNotFound("x".into());
let _ = error::ShellError::Io(std::io::Error::other("test"));
let _ = error::ShellError::Runtime {
msg: "test".into(),
span: error::Span::default(),
};
let cancelled = error::ShellError::Cancelled;
let timed_out = error::ShellError::TimedOut;
let stopped = error::ShellError::Stopped {
pid: 1234,
pgid: 1234,
};
assert!(cancelled.is_cancelled());
assert!(!cancelled.is_timed_out());
assert!(cancelled.is_interrupted());
assert!(timed_out.is_timed_out());
assert!(!timed_out.is_cancelled());
assert!(timed_out.is_interrupted());
assert!(stopped.is_stopped());
assert!(!stopped.is_interrupted());
let exit = error::ShellError::Exit(error::ExitStatus::from(42));
assert_eq!(exit.exit_code().unwrap().code(), 42);
assert!(cancelled.exit_code().is_none());
let _ = format!("{cancelled}");
let _ = format!("{cancelled:?}");
let err: &dyn std::error::Error = &cancelled;
let _ = err.source();
let _: error::ShellError = std::io::Error::other("test").into();
}
#[test]
fn parser_api() {
let mut parser = parser::Parser::new("echo hello");
let program: ast::Program = parser.parse().unwrap();
assert!(!program.commands.is_empty());
let _cmds: &Vec<ast::Command> = &program.commands;
}
#[test]
fn ast_types_accessible() {
let _: ast::Program = ast::Program { commands: vec![] };
let _: ast::Word = ast::Word {
parts: vec![],
span: error::Span::default(),
};
let program = parser::Parser::new("echo hi").parse().unwrap();
match &program.commands[0] {
ast::Command::Simple {
assigns,
args,
redirs,
span: _,
} => {
let _: &Vec<ast::Assignment> = assigns;
let _: &Vec<ast::Word> = args;
let _: &Vec<ast::Redir> = redirs;
}
_ => panic!("expected Simple"),
}
let _ = ast::WordPart::Literal("test".into());
let _ = ast::WordPart::SingleQuoted("test".into());
let _ = ast::WordPart::DoubleQuoted(vec![]);
let _ = ast::WordPart::Tilde("".into());
}
#[test]
fn builtin_list_api() {
let names: &[&str] = builtins::BUILTIN_NAMES;
assert!(names.contains(&"echo"));
assert!(names.contains(&"cd"));
assert!(names.len() >= 25);
assert!(builtins::is_builtin("echo"));
assert!(!builtins::is_builtin("nonexistent"));
}
#[test]
fn encoding_api() {
let s: String = encoding::bytes_to_str(b"hello");
let b: Vec<u8> = encoding::str_to_bytes(&s);
assert_eq!(b, b"hello");
let input = &[0x80u8, 0xFF];
let encoded = encoding::bytes_to_str(input);
let decoded = encoding::str_to_bytes(&encoded);
assert_eq!(decoded, input);
}
#[test]
fn expand_trait_accessible() {
fn _takes_expand(_: &mut dyn expand::ShellExpand) {}
}
#[test]
fn glob_api() {
use std::path::Path;
let _: bool = glob::has_glob_chars("*.txt");
let _: bool = glob::fnmatch("*.txt", "file.txt");
let _: Vec<String> = glob::glob("*.nonexistent", Path::new("/tmp"));
}
#[test]
fn variables_api() {
let mut vars = var::Variables::new();
let _ = var::Variables::new_clean();
vars.set("X", "1").unwrap();
let _: Option<&str> = vars.get("X");
let _: Option<i64> = vars.get_int("X");
vars.set_int("Y", 42).unwrap();
vars.export("X");
vars.set_readonly("X");
let _ = vars.unset("nonexistent");
vars.push_scope();
vars.make_local("Z");
vars.pop_scope();
let _: Option<String> = vars.get_special("?", error::ExitStatus::SUCCESS, 1, "", None);
let _: &str = vars.ifs();
let _: Vec<(String, String)> = vars.exported_env();
vars.positional = vec!["a".into()];
let _: &str = &vars.arg0;
}
#[test]
fn span_api() {
let span = error::Span {
offset: 0,
line: 1,
col: 1,
};
let _default = error::Span::default();
assert_eq!(format!("{span}"), "1:1");
let _ = format!("{span:?}");
assert_eq!(span, span);
let _copy = span;
}
#[test]
fn lexer_ctlesc_public() {
let _: char = lexer::CTLESC;
}
#[test]
fn result_type_alias() {
fn _example() -> error::Result<()> {
Ok(())
}
}