240 lines
6.3 KiB
Rust
240 lines
6.3 KiB
Rust
use std::{
|
|
ffi::OsStr,
|
|
fs,
|
|
path::PathBuf,
|
|
process::{Command, Output},
|
|
sync::atomic::{AtomicUsize, Ordering},
|
|
time::{SystemTime, UNIX_EPOCH},
|
|
};
|
|
|
|
static NEXT_FIXTURE_ID: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
const CASES: &[ReservedCase] = &[
|
|
ReservedCase {
|
|
name: "generic-function",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(fn id (type_params T) ((value T)) -> T
|
|
value)
|
|
|
|
(fn main () -> i32
|
|
0)
|
|
"#,
|
|
code: "UnsupportedGenericFunction",
|
|
message: "generic function declarations are reserved but not supported in the current beta",
|
|
},
|
|
ReservedCase {
|
|
name: "generic-type-alias",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(type VecOf (type_params T) (vec T))
|
|
|
|
(fn main () -> i32
|
|
0)
|
|
"#,
|
|
code: "UnsupportedGenericTypeAlias",
|
|
message: "parameterized type aliases are reserved but not supported in the current beta",
|
|
},
|
|
ReservedCase {
|
|
name: "generic-type-parameter",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(let xs (vec T) (std.vec.i32.empty))
|
|
0)
|
|
"#,
|
|
code: "UnsupportedGenericTypeParameter",
|
|
message: "generic type parameter `T` is reserved but not supported in the current beta",
|
|
},
|
|
ReservedCase {
|
|
name: "generic-vector-spelling",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(fn main () -> (vec)
|
|
(std.vec.i32.empty))
|
|
"#,
|
|
code: "UnsupportedGenericTypeParameter",
|
|
message: "generic vector syntax is reserved but not supported in the current beta",
|
|
},
|
|
ReservedCase {
|
|
name: "map-type",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(fn main () -> (map string i32)
|
|
0)
|
|
"#,
|
|
code: "UnsupportedMapType",
|
|
message: "`map` types are reserved but not supported in the current beta",
|
|
},
|
|
ReservedCase {
|
|
name: "set-type",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(fn main () -> (set string)
|
|
0)
|
|
"#,
|
|
code: "UnsupportedSetType",
|
|
message: "`set` types are reserved but not supported in the current beta",
|
|
},
|
|
ReservedCase {
|
|
name: "std-vec-empty",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(std.vec.empty i32)
|
|
0)
|
|
"#,
|
|
code: "UnsupportedGenericStandardLibraryCall",
|
|
message:
|
|
"generic standard-library call `std.vec.empty` is reserved but not supported in the current beta",
|
|
},
|
|
ReservedCase {
|
|
name: "std-result-map",
|
|
source: r#"
|
|
(module main)
|
|
|
|
(fn main () -> i32
|
|
(std.result.map (ok string i32 "a") mapper)
|
|
0)
|
|
"#,
|
|
code: "UnsupportedGenericStandardLibraryCall",
|
|
message:
|
|
"generic standard-library call `std.result.map` is reserved but not supported in the current beta",
|
|
},
|
|
];
|
|
|
|
#[test]
|
|
fn check_fmt_and_project_paths_reject_reserved_generic_collection_surface() {
|
|
for case in CASES {
|
|
let fixture = write_fixture(case);
|
|
|
|
let check = run_glagol([OsStr::new("check"), fixture.as_os_str()]);
|
|
assert_rejection(&format!("{} check", case.name), &check, case);
|
|
|
|
let fmt = run_glagol([
|
|
OsStr::new("fmt"),
|
|
OsStr::new("--check"),
|
|
fixture.as_os_str(),
|
|
]);
|
|
assert_rejection(&format!("{} fmt --check", case.name), &fmt, case);
|
|
|
|
let project = write_project(case);
|
|
let project_check = run_glagol([OsStr::new("check"), project.as_os_str()]);
|
|
assert_rejection(
|
|
&format!("{} project check", case.name),
|
|
&project_check,
|
|
case,
|
|
);
|
|
}
|
|
}
|
|
|
|
struct ReservedCase {
|
|
name: &'static str,
|
|
source: &'static str,
|
|
code: &'static str,
|
|
message: &'static str,
|
|
}
|
|
|
|
fn assert_rejection(context: &str, output: &Output, case: &ReservedCase) {
|
|
assert_eq!(
|
|
output.status.code(),
|
|
Some(1),
|
|
"{} exit code mismatch\nstdout:\n{}\nstderr:\n{}",
|
|
context,
|
|
String::from_utf8_lossy(&output.stdout),
|
|
String::from_utf8_lossy(&output.stderr)
|
|
);
|
|
assert!(
|
|
output.stdout.is_empty(),
|
|
"{} wrote stdout:\n{}",
|
|
context,
|
|
String::from_utf8_lossy(&output.stdout)
|
|
);
|
|
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
assert!(
|
|
stderr.contains(&format!("error[{}]", case.code)),
|
|
"{} human diagnostic did not contain code `{}`:\n{}",
|
|
context,
|
|
case.code,
|
|
stderr
|
|
);
|
|
assert!(
|
|
stderr.contains(&format!(" (code {})", case.code)),
|
|
"{} machine diagnostic did not contain code `{}`:\n{}",
|
|
context,
|
|
case.code,
|
|
stderr
|
|
);
|
|
assert!(
|
|
stderr.contains(case.message),
|
|
"{} stderr did not contain message `{}`:\n{}",
|
|
context,
|
|
case.message,
|
|
stderr
|
|
);
|
|
assert!(
|
|
!stderr.contains("beta.9"),
|
|
"{} stderr still used beta.9 wording:\n{}",
|
|
context,
|
|
stderr
|
|
);
|
|
}
|
|
|
|
fn write_fixture(case: &ReservedCase) -> PathBuf {
|
|
let id = NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed);
|
|
let path = unique_base_path(&format!("file-{}-{}", id, case.name)).with_extension("slo");
|
|
fs::write(&path, case.source)
|
|
.unwrap_or_else(|err| panic!("write `{}`: {}", path.display(), err));
|
|
path
|
|
}
|
|
|
|
fn write_project(case: &ReservedCase) -> PathBuf {
|
|
let id = NEXT_FIXTURE_ID.fetch_add(1, Ordering::Relaxed);
|
|
let root = unique_base_path(&format!("project-{}-{}", id, case.name));
|
|
let src = root.join("src");
|
|
fs::create_dir_all(&src).unwrap_or_else(|err| panic!("create `{}`: {}", src.display(), err));
|
|
fs::write(
|
|
root.join("slovo.toml"),
|
|
format!(
|
|
"[project]\nname = \"reserved-beta15-{}\"\nsource_root = \"src\"\nentry = \"main\"\n",
|
|
case.name
|
|
),
|
|
)
|
|
.unwrap_or_else(|err| panic!("write project manifest for `{}`: {}", case.name, err));
|
|
fs::write(src.join("main.slo"), case.source)
|
|
.unwrap_or_else(|err| panic!("write project source for `{}`: {}", case.name, err));
|
|
root
|
|
}
|
|
|
|
fn unique_base_path(name: &str) -> PathBuf {
|
|
let nanos = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.map(|duration| duration.as_nanos())
|
|
.unwrap_or(0);
|
|
std::env::temp_dir().join(format!(
|
|
"glagol-reserved-beta15-{}-{}-{}",
|
|
std::process::id(),
|
|
nanos,
|
|
name
|
|
))
|
|
}
|
|
|
|
fn run_glagol<I, S>(args: I) -> Output
|
|
where
|
|
I: IntoIterator<Item = S>,
|
|
S: AsRef<OsStr>,
|
|
{
|
|
Command::new(env!("CARGO_BIN_EXE_glagol"))
|
|
.args(args)
|
|
.output()
|
|
.expect("run glagol")
|
|
}
|