use crate::assert;
use crate::assert::Assert;
#[test]
fn funcall_test() {
fn f(x: &str) -> String {
format!(
"
def f1():
return 1
def f2(a): return a
def f3(a, b, c):
return a + b + c
def f4(a, *args):
r = a
for i in args:
r += i
return r
def f5(a, **kwargs): return kwargs
def f6(*args): return args
def rec1(): rec1()
def rec2(): rec3()
def rec3(): rec4()
def rec4(): rec5()
def rec5(): rec6()
def rec6(): rec2()
{}",
x
)
}
assert::is_true(&f("(f1() == 1)"));
assert::is_true(&f("(f2(2) == 2)"));
assert::is_true(&f("(f3(1, 2, 3) == 6)"));
assert::is_true(&f("(f4(1, 2, 3) == 6)"));
assert::is_true(&f("(f5(2) == {})"));
assert::is_true(&f("(f5(a=2) == {})"));
assert::is_true(&f("(f5(1, b=2) == {'b': 2})"));
assert::is_true(&f("(f6(1, 2, 3) == (1, 2, 3))"));
assert::fail(&f("rec1()"), "Starlark call stack overflow");
assert::fail(&f("rec2()"), "Starlark call stack overflow");
assert::fail("def f(a, a=2): pass", "duplicated parameter");
assert::is_true("def f(a, *args, b): return b\nf(1, b=True)");
assert::is_true("def f(a, *args, b=True): return b\nf(1)");
assert::is_true("NAME=True\ndef f(*args, pkg=NAME, **kwargs): return pkg\nf()");
assert::is_true("def f(*args, pkg=False, **kwargs): return pkg\nf(pkg=True)");
assert::is_true("def f(a, b=1, *args, c=False): return c\nf(a=1,c=True)");
assert::fail("def f(a, **kwargs, b=1): pass", "Parameter after kwargs");
assert::fail(
"def f(a, b=1, **kwargs, c=1): pass",
"Parameter after kwargs",
);
assert::fail("def f(a, **kwargs, *args): pass", "parameter after another");
}
#[test]
fn funcall_extra_args_def() {
fn f(x: &str) -> String {
format!(
"
def f3(a, b, c):
return a + b + c
{}",
x
)
}
assert::fail(&f("noop(f3)(1,2,3,4)"), "extra positional");
assert::fail(&f("noop(f3)(1,2)"), "Missing parameter");
assert::fail(&f("noop(f3)(a=1, b=2)"), "Missing parameter");
assert::fail(&f("noop(f3)(a=1, b=2, c=3, d=4)"), "extra named");
}
#[test]
fn test_repeated_parameters() {
assert::fail("def f(x,x): pass", "duplicated parameter");
assert::fail("def f(): pass\ndef g(): f(x=1,x=1)", "repeated named");
}
#[test]
fn test_bad_application() {
assert::fail("noop(['1'])(2)", "not supported");
assert::fail("noop('test')(2)", "not supported");
assert::fail("noop(1 == 1)(2)", "not supported");
}
#[test]
fn test_extra_args_native() {
assert::is_true(r#"("bonbon".find("on") == 1)"#);
assert::fail(r#"("bonbon".find(needle = "on") == 1)"#, "extra named");
assert::fail(r#""bonbon".find("on", 2, 3, 4)"#, "Wrong number of");
assert::fail(r#""bonbon".find("on", needless="on")"#, "extra named");
assert::fail(r#""bonbon".find()"#, "Wrong number of");
}
#[test]
fn test_insufficient_args_native() {
assert::fails(
"noop(filter)([])",
&["Wrong number of positional", "expected 2", "got 1"],
);
}
#[test]
fn test_parameter_defaults() {
assert::is_true(
"
def f(x=[x for x in [1]]):
return x
f() == [1]",
);
assert::is_true(
"
y = 7
def f(x=y):
y = 1
return x
f() == 7",
);
assert::is_true(
"
def f(x, xs = []):
xs.append(x)
return xs
pre = str(f(8, [6,7]))
f(1)
post = str(f(2))
pre == '[6, 7, 8]' and post == '[1, 2]'",
);
}
#[test]
fn test_parameter_defaults_frozen() {
let mut a = Assert::new();
a.module("f.bzl", "def f(x, xs = []):\n xs.append(x)\n return xs");
a.is_true("load('f.bzl', 'f')\nf(1, [2]) == [2, 1]");
a.fail("load('f.bzl', 'f')\nf(1) == [1]", "Immutable");
}
#[test]
fn test_arguments() {
fn f(x: &str) -> String {
format!(
"
def f(a, b, c=5):
return a * b + c
def g(a=1, b=2):
return a+b
def h(a=1, *, b=2):
return a+b
{}",
x
)
}
assert::is_true(&f("f(*[2, 3]) == 11"));
assert::is_true(&f("f(*[2, 3, 7]) == 13"));
assert::fail(&f("f(*[2])"), "Missing parameter");
assert::is_true(&f("f(**{'b':3, 'a':2}) == 11"));
assert::is_true(&f("f(**{'c':7, 'a':2, 'b':3}) == 13"));
assert::fail(&f("f(**{'a':2})"), "Missing parameter");
assert::fail(&f("f(**{'c':7, 'a':2, 'b':3, 'd':5})"), "extra named");
assert::fail(&f("noop(f)(1, a=1, b=2)"), "occurs more");
assert::fail(&f("noop(g)(a=1,*[2])"), "occurs more");
assert::fail(&f("noop(h)(1, 2)"), "extra positional");
assert::is_true(&f("h(2, b=3) == 5"));
assert::is_true(&f("h(a=2, b=3) == 5"));
assert::fail(
&f("def bad(x, *, *args):\n pass"),
"parameter after another",
);
}
#[test]
fn test_argument_evaluation_order() {
assert::pass(
r#"
r = []
def id(x):
r.append(x)
return x
def f(*args, **kwargs):
return (args, kwargs)
y = f(id(1), id(2), x=id(3), *[id(4)], **dict(z=id(5)))
assert_eq(y, ((1, 2, 4), dict(x=3, z=5)))
assert_eq(r, [1,2,3,4,5])
"#,
);
}
#[test]
fn test_empty_args_kwargs() {
assert::pass(
r#"
def f(x, *args, **kwargs):
assert_eq(args, ())
assert_eq(kwargs, {})
f(1)
"#,
);
assert::fail(
r#"
def f(x, *, y):
pass
noop(f)(1)
"#,
"Missing named-only parameter `y`",
);
}
#[test]
fn test_non_optional_after_optional() {
assert::pass(
r#"
def f(*args, x, y = 42, z):
return (args, x, y, z)
assert_eq(f(x = 1, z = 3), ((), 1, 42, 3))
assert_eq(f(2, 4, y = 7, x = 1, z = 3), ((2, 4), 1, 7, 3))
"#,
);
}
#[test]
fn test_pos_only_pass() {
assert::pass(
r#"
def f(x, /, y):
return x, y
assert_eq((1, 2), f(1, y=2))
"#,
);
}
#[test]
fn test_pos_only_fail() {
assert::fail(
r#"
def f(x, /, y):
return x, y
g = noop(f) # Hide from static type checker.
g(x=1, y=2)
"#,
"Missing positional-only parameter `x` for call",
);
}
#[cfg_attr(rust_nightly, cfg(not(sanitize = "address")))]
#[test]
fn test_frame_size() {
use starlark::values::list_or_tuple::UnpackListOrTuple;
use starlark_derive::starlark_module;
use crate as starlark;
use crate::environment::GlobalsBuilder;
use crate::values::UnpackValue;
use crate::values::Value;
#[starlark_module]
fn natives(builder: &mut GlobalsBuilder) {
fn stack_ptr(args: UnpackListOrTuple<Value>) -> anyhow::Result<usize> {
drop(args);
let x = std::hint::black_box(1);
let ptr = &x;
Ok(ptr as *const i32 as usize)
}
}
let program = r#"
def f(x):
return stack_ptr(x)
def g(x):
noop(x)
return f(x)
F_PTR = f([])
G_F_PTR = g([])
"#;
let mut a = Assert::new();
a.globals_add(natives);
let module = a.pass_module(program);
let one = usize::unpack_value(module.get("F_PTR").unwrap().value())
.unwrap()
.unwrap();
let two = usize::unpack_value(module.get("G_F_PTR").unwrap().value())
.unwrap()
.unwrap();
assert!(
two < one,
"stack grows down everywhere we support starlark-rust"
);
let frame_native_size = one - two;
assert!(frame_native_size > 20, "sanity check");
assert!(
frame_native_size < 20000,
"native frame size is too large: {}, evaluation may result in native stack overflow",
frame_native_size,
);
}