137 lines
4.3 KiB
Rust
137 lines
4.3 KiB
Rust
use std::io::Write;
|
|
use std::process::{Command, Output, Stdio};
|
|
|
|
fn stdin_command(arguments: &[&str], source: &[u8]) -> Output {
|
|
let mut child = Command::new(env!("CARGO_BIN_EXE_cli"))
|
|
.args(arguments)
|
|
.stdin(Stdio::piped())
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
.unwrap();
|
|
child.stdin.take().unwrap().write_all(source).unwrap();
|
|
child.wait_with_output().unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn run_executes_real_endor_files_and_prints_entry_results() {
|
|
for (file, expected) in [
|
|
("hello.endor", "你好,Endor 🌱\n"),
|
|
("calls.endor", "函数调用已接通\n"),
|
|
("shadowing.endor", "局部名称优先\n"),
|
|
("number.endor", "-123456789012345678901234567890.00100\n"),
|
|
] {
|
|
let path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.join("../compiler/examples")
|
|
.join(file);
|
|
let output = Command::new(env!("CARGO_BIN_EXE_cli"))
|
|
.arg("run")
|
|
.arg(path)
|
|
.output()
|
|
.unwrap();
|
|
assert!(
|
|
output.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
assert_eq!(String::from_utf8(output.stdout).unwrap(), expected);
|
|
assert!(output.stderr.is_empty());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn eval_and_run_read_stdin_and_support_explicit_entry_names() {
|
|
let output = stdin_command(&["eval", "-"], "\"表达式🌱\"".as_bytes());
|
|
assert!(
|
|
output.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
assert_eq!(String::from_utf8(output.stdout).unwrap(), "表达式🌱\n");
|
|
let output = stdin_command(
|
|
&["run", "--entry", "greet", "-"],
|
|
b"function greet(): String { return \"ok\" }",
|
|
);
|
|
assert!(
|
|
output.status.success(),
|
|
"{}",
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
assert_eq!(String::from_utf8(output.stdout).unwrap(), "ok\n");
|
|
let path =
|
|
std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../compiler/examples/literal.endor");
|
|
let output = Command::new(env!("CARGO_BIN_EXE_cli"))
|
|
.arg("eval")
|
|
.arg(path)
|
|
.output()
|
|
.unwrap();
|
|
assert!(output.status.success());
|
|
assert_eq!(
|
|
String::from_utf8(output.stdout).unwrap(),
|
|
"表达式 → 字节码 → VM\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn execution_errors_fail_without_success_output_and_report_their_stage() {
|
|
for (arguments, source, expected) in [
|
|
(
|
|
vec!["run", "-"],
|
|
"function main(): T {\n return missing\n}",
|
|
"<stdin>:2:10: undefined name: missing",
|
|
),
|
|
(
|
|
vec!["run", "-"],
|
|
"function main(): T { return f() } function f(x: T): T { return x }",
|
|
"positional arguments",
|
|
),
|
|
(
|
|
vec!["run", "-"],
|
|
"function other(): T { return 1 }",
|
|
"entry function not found: main",
|
|
),
|
|
(
|
|
vec!["eval", "-"],
|
|
"1 + 2",
|
|
"arithmetic bytecode is not implemented",
|
|
),
|
|
(vec!["eval", "-"], "1()", "does not support call"),
|
|
(
|
|
vec!["run", "-"],
|
|
"function main(): T { return ) }",
|
|
"expected",
|
|
),
|
|
] {
|
|
let output = stdin_command(&arguments, source.as_bytes());
|
|
assert!(!output.status.success());
|
|
assert!(output.stdout.is_empty());
|
|
let error = String::from_utf8(output.stderr).unwrap();
|
|
assert!(error.contains(expected), "{error}");
|
|
if expected == "positional arguments" {
|
|
assert!(error.contains("at main (<stdin>:1, code unit"));
|
|
}
|
|
}
|
|
let output = stdin_command(&["eval", "-"], &[0xff]);
|
|
assert!(!output.status.success());
|
|
assert!(
|
|
String::from_utf8(output.stderr)
|
|
.unwrap()
|
|
.contains("invalid UTF-8")
|
|
);
|
|
for arguments in [
|
|
vec!["run"],
|
|
vec!["run", "--entry"],
|
|
vec!["run", "--unknown", "-"],
|
|
vec!["eval"],
|
|
vec!["eval", "-", "extra"],
|
|
] {
|
|
let output = Command::new(env!("CARGO_BIN_EXE_cli"))
|
|
.args(arguments)
|
|
.output()
|
|
.unwrap();
|
|
assert!(!output.status.success());
|
|
assert!(output.stdout.is_empty());
|
|
assert!(String::from_utf8(output.stderr).unwrap().contains("Usage:"));
|
|
}
|
|
}
|