prelude!();
macro_rules! test_op {
($ty:ty => $lhs:literal $op:tt $rhs:literal = $result:literal) => {{
let program = format!(
r#"const A = {lhs}; const B = {rhs}; const VALUE = A {op} B; VALUE"#,
lhs = $lhs,
rhs = $rhs,
op = stringify!($op),
);
let out: $ty = eval(&program);
assert_eq!(
out, $result,
concat!("expected ", stringify!($result), " out of program `{}`"),
program
);
}};
}
#[test]
fn test_const_values() {
let out: bool = rune!(const VALUE = true; VALUE);
assert_eq!(out, true);
let out: String = rune!(const VALUE = "Hello World"; VALUE);
assert_eq!(out, "Hello World");
let out: String = eval(
r#"
const VALUE = `Hello ${WORLD} ${A} ${B} ${C}`;
const WORLD = "World";
const A = 1;
const B = 1.0;
const C = true;
VALUE
"#,
);
assert_eq!(out, "Hello World 1 1.0 true");
}
#[test]
fn test_integer_ops() {
test_op!(i64 => 1 + 2 = 3);
test_op!(i64 => 2 - 1 = 1);
test_op!(i64 => 8 / 2 = 4);
test_op!(i64 => 8 * 2 = 16);
test_op!(i64 => 0b1010 << 2 = 0b101000);
test_op!(i64 => 0b1010 >> 2 = 0b10);
test_op!(bool => 1 < 2 = true);
test_op!(bool => 2 < 2 = false);
test_op!(bool => 1 <= 1 = true);
test_op!(bool => 2 <= 1 = false);
test_op!(bool => 3 > 2 = true);
test_op!(bool => 2 > 2 = false);
test_op!(bool => 1 >= 1 = true);
test_op!(bool => 0 >= 2 = false);
}
macro_rules! test_float_op {
($ty:ty => $lhs:literal $op:tt $rhs:literal = $result:literal) => {{
let program = format!(
r#"const A = {lhs}.0; const B = {rhs}.0; const VALUE = A {op} B; VALUE"#,
lhs = $lhs,
rhs = $rhs,
op = stringify!($op),
);
let out: $ty = eval(&program);
assert_eq!(
out, $result,
concat!("expected ", stringify!($result), " out of program `{}`"),
program
);
}};
}
#[test]
fn test_float_ops() {
test_float_op!(f64 => 1 + 2 = 3f64);
test_float_op!(f64 => 2 - 1 = 1f64);
test_float_op!(f64 => 8 / 2 = 4f64);
test_float_op!(f64 => 8 * 2 = 16f64);
test_float_op!(bool => 1 < 2 = true);
test_float_op!(bool => 2 < 2 = false);
test_float_op!(bool => 1 <= 1 = true);
test_float_op!(bool => 2 <= 1 = false);
test_float_op!(bool => 3 > 2 = true);
test_float_op!(bool => 2 > 2 = false);
test_float_op!(bool => 1 >= 1 = true);
test_float_op!(bool => 0 >= 2 = false);
}
#[test]
fn test_const_collections() {
let object: Object = rune!(fn main() { VALUE } const VALUE = #{}; main());
assert!(object.is_empty());
let tuple: Box<Tuple> = rune!(fn main() { VALUE } const VALUE = (); main());
assert!(tuple.is_empty());
let tuple: Box<Tuple> = rune!(fn main() { VALUE } const VALUE = ("Hello World",); main());
assert_eq!(
Some("Hello World"),
tuple.get_value::<String>(0).unwrap().as_deref()
);
let vec: runtime::Vec = rune!(fn main() { VALUE } const VALUE = []; main());
assert!(vec.is_empty());
let vec: runtime::Vec = rune!(fn main() { VALUE } const VALUE = ["Hello World"]; main());
assert_eq!(
Some("Hello World"),
vec.get_value::<String>(0).unwrap().as_deref()
);
}
#[test]
fn test_more_complexity() {
let result: i64 = rune! {
const BASE = 10;
const LIMIT = 0b1 << 10;
const VALUE = {
let timeout = BASE;
while timeout < LIMIT {
timeout *= 2;
}
timeout
};
VALUE
};
assert_eq!(result, 1280);
}
#[test]
fn test_if_else() {
let result: i64 = rune! {
const VALUE = { if true { 1 } else if true { 2 } else { 3 } };
VALUE
};
assert_eq!(result, 1);
let result: i64 = rune! {
const VALUE = { if false { 1 } else if true { 2 } else { 3 } };
VALUE
};
assert_eq!(result, 2);
let result: i64 = rune! {
const VALUE = { if false { 1 } else if false { 2 } else { 3 } };
VALUE
};
assert_eq!(result, 3);
}
#[test]
fn test_const_fn() {
let result: i64 = rune! {
const VALUE = 2;
const fn foo(n) { n + VALUE }
fn bar() {
const VALUE = 1;
foo(1 + 4 / 2 - VALUE) + foo(VALUE - 1)
}
bar()
};
assert_eq!(result, 6);
let result: String = eval(
r#"
const VALUE = "baz";
const fn foo(n) {
`foo ${n}`
}
foo(`bar ${VALUE}`)
"#,
);
assert_eq!(result, "foo bar baz");
let result: String = eval(
r#"
const VALUE = foo("bar", "baz");
const fn foo(a, b) {
`foo ${a} ${b} ${bar("biz")}`
}
const fn bar(c) {
c
}
VALUE
"#,
);
assert_eq!(result, "foo bar baz biz");
}
#[test]
fn test_const_fn_visibility() {
let result: i64 = rune! {
pub mod a {
pub mod b {
pub const fn out(n) {
n + A
}
const A = 1;
}
}
mod b {
pub(super) fn out() {
crate::a::b::out(B)
}
const B = 2;
}
b::out()
};
assert_eq!(result, 3);
}
#[test]
fn test_const_block() {
let result: i64 = rune! {
let u = 2;
let value = const { 1 << test() };
return value - u;
const fn test() { 32 }
};
assert_eq!(result, (1i64 << 32) - 2);
let result: String = rune! {
let var = "World";
format!(const { "Hello {}" }, var)
};
assert_eq!(result, "Hello World");
let result: String = rune! {
let var = "World";
return format!(const { FORMAT }, var);
const FORMAT = "Hello {}";
};
assert_eq!(result, "Hello World");
}