slovo/compiler/tests/reserved_generic_collection_beta15.rs

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")
}