refactor(runtime tests): split off builtin tests
Signed-off-by: Charlotte Meyer <dev@buffet.sh>
This commit is contained in:
parent
e1fcd94ef1
commit
ac8a22ad99
3 changed files with 135 additions and 125 deletions
70
crates/oyster_runtime/tests/it/builtins/mod.rs
Normal file
70
crates/oyster_runtime/tests/it/builtins/mod.rs
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
use std::{borrow::Cow, ffi::OsStr, io::Write};
|
||||||
|
|
||||||
|
use expect_test::{expect, Expect};
|
||||||
|
use oyster_builtin_proc::builtin;
|
||||||
|
use oyster_parser::ast;
|
||||||
|
use oyster_runtime::Shell;
|
||||||
|
|
||||||
|
use crate::collect_output;
|
||||||
|
|
||||||
|
#[builtin(description = "test builtin")]
|
||||||
|
fn test_builtin(_: &mut Shell, _: &[Cow<OsStr>]) {
|
||||||
|
// XXX: this is a workaround around libtest's use of io::set_output_capture
|
||||||
|
let _ = write!(std::io::stdout(), "this is a test\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check(ast: &ast::Code, expect: Expect) {
|
||||||
|
let actual = collect_output(|| {
|
||||||
|
let mut shell = Shell::new().unwrap();
|
||||||
|
shell.builtins_mut().add(test_builtin);
|
||||||
|
shell.run(ast).unwrap();
|
||||||
|
});
|
||||||
|
expect.assert_debug_eq(&actual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_builtin() {
|
||||||
|
let ast = {
|
||||||
|
use oyster_parser::ast::*;
|
||||||
|
|
||||||
|
Code(vec![Statement::Pipeline(Pipeline(vec![Command(
|
||||||
|
vec![Word(vec![WordPart::Text("test_builtin")])],
|
||||||
|
Redirect::None,
|
||||||
|
)]))])
|
||||||
|
};
|
||||||
|
|
||||||
|
check(
|
||||||
|
&ast,
|
||||||
|
expect![[r#"
|
||||||
|
"this is a test\r\n"
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builtin_redirection() {
|
||||||
|
let ast = {
|
||||||
|
use oyster_parser::ast::*;
|
||||||
|
|
||||||
|
Code(vec![Statement::Pipeline(Pipeline(vec![
|
||||||
|
Command(
|
||||||
|
vec![Word(vec![WordPart::Text("test_builtin")])],
|
||||||
|
Redirect::Stdout,
|
||||||
|
),
|
||||||
|
Command(
|
||||||
|
vec![
|
||||||
|
Word(vec![WordPart::Text("wc")]),
|
||||||
|
Word(vec![WordPart::Text("-c")]),
|
||||||
|
],
|
||||||
|
Redirect::None,
|
||||||
|
),
|
||||||
|
]))])
|
||||||
|
};
|
||||||
|
|
||||||
|
check(
|
||||||
|
&ast,
|
||||||
|
expect![[r#"
|
||||||
|
"15\r\n"
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
|
@ -1 +1,59 @@
|
||||||
|
mod builtins;
|
||||||
mod pipeline;
|
mod pipeline;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
ffi::OsString,
|
||||||
|
fs::File,
|
||||||
|
io::{BufRead, BufReader, Write},
|
||||||
|
os::unix::{ffi::OsStringExt, io::FromRawFd},
|
||||||
|
process,
|
||||||
|
};
|
||||||
|
|
||||||
|
use nix::{
|
||||||
|
ioctl_write_int_bad, libc,
|
||||||
|
pty::{self, OpenptyResult},
|
||||||
|
sys,
|
||||||
|
unistd::{self, ForkResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
ioctl_write_int_bad!(tiocsctty, libc::TIOCSCTTY);
|
||||||
|
|
||||||
|
/// Forks to redirect stdin, stderr, stdout, then run the commands.
|
||||||
|
/// Relies on inserting a NUL byte in the end, so there shouldn't be NUL in the output.
|
||||||
|
fn collect_output<F>(mut f: F) -> OsString
|
||||||
|
where
|
||||||
|
F: FnMut(),
|
||||||
|
{
|
||||||
|
let OpenptyResult { master, slave } = pty::openpty(None, None).unwrap();
|
||||||
|
|
||||||
|
match unsafe { unistd::fork() }.unwrap() {
|
||||||
|
ForkResult::Parent { child } => {
|
||||||
|
sys::wait::waitpid(child, None).unwrap();
|
||||||
|
}
|
||||||
|
ForkResult::Child => {
|
||||||
|
unistd::setsid().unwrap();
|
||||||
|
unsafe { tiocsctty(slave, 0) }.unwrap();
|
||||||
|
|
||||||
|
unistd::dup2(slave, libc::STDIN_FILENO).unwrap();
|
||||||
|
unistd::dup2(slave, libc::STDOUT_FILENO).unwrap();
|
||||||
|
unistd::dup2(slave, libc::STDERR_FILENO).unwrap();
|
||||||
|
|
||||||
|
let _ = unistd::close(master);
|
||||||
|
let _ = unistd::close(slave);
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let master = unsafe { File::from_raw_fd(master) };
|
||||||
|
let mut slave = unsafe { File::from_raw_fd(slave) };
|
||||||
|
|
||||||
|
slave.write(&[0]).unwrap();
|
||||||
|
let mut r = BufReader::new(master);
|
||||||
|
let mut buf = vec![];
|
||||||
|
r.read_until(0, &mut buf).unwrap();
|
||||||
|
|
||||||
|
OsString::from_vec(buf[..buf.len() - 1].to_vec())
|
||||||
|
}
|
||||||
|
|
|
@ -1,74 +1,12 @@
|
||||||
use std::{
|
|
||||||
borrow::Cow,
|
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
fs::File,
|
|
||||||
io::{BufRead, BufReader, Write},
|
|
||||||
os::unix::{ffi::OsStringExt, io::FromRawFd},
|
|
||||||
process,
|
|
||||||
};
|
|
||||||
|
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
use nix::{
|
|
||||||
ioctl_write_int_bad, libc,
|
|
||||||
pty::{self, OpenptyResult},
|
|
||||||
sys,
|
|
||||||
unistd::{self, ForkResult},
|
|
||||||
};
|
|
||||||
use oyster_builtin_proc::builtin;
|
|
||||||
use oyster_parser::ast;
|
use oyster_parser::ast;
|
||||||
use oyster_runtime::Shell;
|
use oyster_runtime::Shell;
|
||||||
|
|
||||||
|
use crate::collect_output;
|
||||||
|
|
||||||
// TODO: test signal return codes
|
// TODO: test signal return codes
|
||||||
|
|
||||||
ioctl_write_int_bad!(tiocsctty, libc::TIOCSCTTY);
|
fn check(ast: &ast::Code, expect: Expect) {
|
||||||
|
|
||||||
#[builtin(description = "test builtin")]
|
|
||||||
fn test_builtin(_: &mut Shell, _: &[Cow<OsStr>]) {
|
|
||||||
// XXX: this is a workaround around libtest's use of io::set_output_capture
|
|
||||||
let _ = write!(std::io::stdout(), "this is a test\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Forks to redirect stdin, stderr, stdout, then run the commands.
|
|
||||||
/// Relies on inserting a NUL byte in the end, so there shouldn't be NUL in the output.
|
|
||||||
fn collect_output<F>(mut f: F) -> OsString
|
|
||||||
where
|
|
||||||
F: FnMut(),
|
|
||||||
{
|
|
||||||
let OpenptyResult { master, slave } = pty::openpty(None, None).unwrap();
|
|
||||||
|
|
||||||
match unsafe { unistd::fork() }.unwrap() {
|
|
||||||
ForkResult::Parent { child } => {
|
|
||||||
sys::wait::waitpid(child, None).unwrap();
|
|
||||||
}
|
|
||||||
ForkResult::Child => {
|
|
||||||
unistd::setsid().unwrap();
|
|
||||||
unsafe { tiocsctty(slave, 0) }.unwrap();
|
|
||||||
|
|
||||||
unistd::dup2(slave, libc::STDIN_FILENO).unwrap();
|
|
||||||
unistd::dup2(slave, libc::STDOUT_FILENO).unwrap();
|
|
||||||
unistd::dup2(slave, libc::STDERR_FILENO).unwrap();
|
|
||||||
|
|
||||||
let _ = unistd::close(master);
|
|
||||||
let _ = unistd::close(slave);
|
|
||||||
|
|
||||||
f();
|
|
||||||
|
|
||||||
process::exit(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let master = unsafe { File::from_raw_fd(master) };
|
|
||||||
let mut slave = unsafe { File::from_raw_fd(slave) };
|
|
||||||
|
|
||||||
slave.write(&[0]).unwrap();
|
|
||||||
let mut r = BufReader::new(master);
|
|
||||||
let mut buf = vec![];
|
|
||||||
r.read_until(0, &mut buf).unwrap();
|
|
||||||
|
|
||||||
OsString::from_vec(buf[..buf.len() - 1].to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_collect(ast: &ast::Code, expect: Expect) {
|
|
||||||
let actual = collect_output(|| {
|
let actual = collect_output(|| {
|
||||||
let mut shell = Shell::new().unwrap();
|
let mut shell = Shell::new().unwrap();
|
||||||
shell.run(ast).unwrap();
|
shell.run(ast).unwrap();
|
||||||
|
@ -76,15 +14,6 @@ fn check_collect(ast: &ast::Code, expect: Expect) {
|
||||||
expect.assert_debug_eq(&actual);
|
expect.assert_debug_eq(&actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_builtin(ast: &ast::Code, expect: Expect) {
|
|
||||||
let actual = collect_output(|| {
|
|
||||||
let mut shell = Shell::new().unwrap();
|
|
||||||
shell.builtins_mut().add(test_builtin);
|
|
||||||
shell.run(ast).unwrap();
|
|
||||||
});
|
|
||||||
expect.assert_debug_eq(&actual);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn simple_command() {
|
fn simple_command() {
|
||||||
let ast = {
|
let ast = {
|
||||||
|
@ -99,7 +28,7 @@ fn simple_command() {
|
||||||
)]))])
|
)]))])
|
||||||
};
|
};
|
||||||
|
|
||||||
check_collect(
|
check(
|
||||||
&ast,
|
&ast,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
"hi\r\n"
|
"hi\r\n"
|
||||||
|
@ -130,7 +59,7 @@ fn pipeline() {
|
||||||
]))])
|
]))])
|
||||||
};
|
};
|
||||||
|
|
||||||
check_collect(
|
check(
|
||||||
&ast,
|
&ast,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
"3\r\n"
|
"3\r\n"
|
||||||
|
@ -201,7 +130,7 @@ fn multipart_word() {
|
||||||
)]))])
|
)]))])
|
||||||
};
|
};
|
||||||
|
|
||||||
check_collect(
|
check(
|
||||||
&ast,
|
&ast,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
"hello\r\n"
|
"hello\r\n"
|
||||||
|
@ -209,53 +138,6 @@ fn multipart_word() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn simple_builtin() {
|
|
||||||
let ast = {
|
|
||||||
use oyster_parser::ast::*;
|
|
||||||
|
|
||||||
Code(vec![Statement::Pipeline(Pipeline(vec![Command(
|
|
||||||
vec![Word(vec![WordPart::Text("test_builtin")])],
|
|
||||||
Redirect::None,
|
|
||||||
)]))])
|
|
||||||
};
|
|
||||||
|
|
||||||
check_builtin(
|
|
||||||
&ast,
|
|
||||||
expect![[r#"
|
|
||||||
"this is a test\r\n"
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn builtin_redirection() {
|
|
||||||
let ast = {
|
|
||||||
use oyster_parser::ast::*;
|
|
||||||
|
|
||||||
Code(vec![Statement::Pipeline(Pipeline(vec![
|
|
||||||
Command(
|
|
||||||
vec![Word(vec![WordPart::Text("test_builtin")])],
|
|
||||||
Redirect::Stdout,
|
|
||||||
),
|
|
||||||
Command(
|
|
||||||
vec![
|
|
||||||
Word(vec![WordPart::Text("wc")]),
|
|
||||||
Word(vec![WordPart::Text("-c")]),
|
|
||||||
],
|
|
||||||
Redirect::None,
|
|
||||||
),
|
|
||||||
]))])
|
|
||||||
};
|
|
||||||
|
|
||||||
check_builtin(
|
|
||||||
&ast,
|
|
||||||
expect![[r#"
|
|
||||||
"15\r\n"
|
|
||||||
"#]],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn command_substitution() {
|
fn command_substitution() {
|
||||||
let ast = {
|
let ast = {
|
||||||
|
@ -278,7 +160,7 @@ fn command_substitution() {
|
||||||
)]))])
|
)]))])
|
||||||
};
|
};
|
||||||
|
|
||||||
check_builtin(
|
check(
|
||||||
&ast,
|
&ast,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
"hello\r\n"
|
"hello\r\n"
|
||||||
|
|
Loading…
Reference in a new issue