use std::fmt::Write;
use std::mem;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use derive_more::Display;
use once_cell::sync::Lazy;
use starlark_derive::starlark_module;
use crate as starlark;
use crate::assert;
use crate::assert::Assert;
use crate::environment::GlobalsBuilder;
use crate::eval::Evaluator;
use crate::values::any::StarlarkAny;
use crate::values::FrozenHeap;
use crate::values::Heap;
#[test]
fn test_garbage_collect() {
assert::pass(
r#"
x = (100, [{"test": None}], True)
y = str(x)
garbage_collect()
assert_eq(y, str(x))
"#,
);
}
#[test]
fn test_deallocation() {
static COUNT: Lazy<AtomicUsize> = Lazy::new(|| AtomicUsize::new(0));
#[derive(Default, Debug, Display)]
struct Dealloc;
impl Drop for Dealloc {
fn drop(&mut self) {
COUNT.fetch_add(1, Ordering::SeqCst);
}
}
#[starlark_module]
fn globals(builder: &mut GlobalsBuilder) {
fn mk() -> anyhow::Result<StarlarkAny<Dealloc>> {
Ok(StarlarkAny::new(Dealloc))
}
}
COUNT.store(0, Ordering::SeqCst);
let mut a = Assert::new();
a.disable_gc();
a.globals_add(globals);
a.module("test", "x = [mk(), mk()]\ndef y(): return mk()");
a.pass(
r#"
load("test", "x", "y")
z = x[1]
q = mk()
r = [y(), mk()]
"#,
);
assert_eq!(COUNT.load(Ordering::SeqCst), 3);
mem::drop(a);
assert_eq!(COUNT.load(Ordering::SeqCst), 5);
}
#[cfg_attr(rust_nightly, cfg(not(sanitize = "address")))]
#[test]
fn test_stack_depth() {
#[starlark_module]
fn measure_stack(builder: &mut GlobalsBuilder) {
fn stack_depth() -> anyhow::Result<String> {
let mut s = std::hint::black_box(1i32);
Ok((&mut s as *mut i32 as usize).to_string())
}
}
let mut a = Assert::new();
a.globals_add(measure_stack);
let s = a.pass(
r#"
for i in range(1001):
if i == 1:
v1 = stack_depth()
if i == 100:
v100 = stack_depth()
elif i == 1000:
v1000 = stack_depth()
v1 + " " + v100 + " " + v1000
"#,
);
let s = s.unpack_str().unwrap();
let words = s
.split(' ')
.map(|x| x.parse::<isize>().unwrap())
.collect::<Vec<_>>();
let v1 = words[0];
let v100 = words[1];
let v1000 = words[2];
assert!(
(v1 - v100).abs() + 1000 >= (v1000 - v100).abs(),
"Stack change exceeded, FAILED {} + 1000 >= {} (relative to v1), 100={}, 1000={}",
(v1 - v100).abs(),
(v1000 - v100).abs(),
v100 - v1,
v1000 - v1
);
}
#[test]
fn test_garbage_collect_happens() {
#[starlark_module]
fn helpers(builder: &mut GlobalsBuilder) {
fn current_usage(heap: &Heap) -> anyhow::Result<i32> {
Ok(heap.allocated_bytes() as i32)
}
fn is_gc_disabled(eval: &mut Evaluator) -> anyhow::Result<bool> {
Ok(eval.disable_gc)
}
}
let mut a = Assert::new();
a.globals_add(helpers);
let mut code = r#"
globals = []
maximum = [0]
success = [is_gc_disabled()]
def update_maximum():
maximum[0] = max(current_usage(), maximum[0])
def expensive(n):
if success[0]:
return
now = current_usage()
if now < maximum[0]:
print("Success in " + str(n))
success[0] = True
return
update_maximum()
globals.append(str(n))
locals = []
for i in range(10 * n):
locals.append(str(i))
update_maximum()
"#
.to_owned();
for i in 0..100 {
writeln!(code, "expensive({})", i).unwrap();
}
code.push_str("assert_eq(success[0], True)\nis_gc_disabled()");
assert!(!a.pass(&code).unpack_bool().unwrap());
}
#[test]
fn test_callstack() {
let d = assert::fail(
r#"
def f():
fail("bad")
f()
"#,
"bad",
);
assert!(d.to_string().contains("fail(\"bad\")"));
}
#[test]
fn test_display_debug() {
let heap = Heap::new();
let val = heap.alloc((vec![1, 2], "test", true));
assert_eq!(format!("{}", val), "([1, 2], \"test\", True)");
assert_eq!(val.to_repr(), "([1, 2], \"test\", True)");
assert_eq!(val.to_str(), "([1, 2], \"test\", True)");
assert_eq!(
format!("{:?}", val),
"Value(TupleGen { content: [Value(ListGen(ListData { content: Cell { value: ValueTyped(Value(Array { len: 2, capacity: 4, iter_count: 0, content: [Value(1), Value(2)] })) } })), Value(\"test\"), Value(StarlarkBool(true))] })"
);
let v = heap.alloc("test");
assert_eq!(format!("{}", v), "\"test\"");
assert_eq!(v.to_repr(), "\"test\"");
assert_eq!(v.to_str(), "test");
assert_eq!(format!("{:?}", v), "Value(\"test\")");
assert_eq!(format!("{:#?}", v), "Value(\n \"test\",\n)");
let frozen_heap = FrozenHeap::new();
let v = frozen_heap.alloc("test");
assert_eq!(format!("{}", v), "\"test\"");
assert_eq!(v.to_value().to_repr(), "\"test\"");
assert_eq!(v.to_value().to_str(), "test");
assert_eq!(format!("{:?}", v), "FrozenValue(\"test\")");
assert_eq!(format!("{:#?}", v), "FrozenValue(\n \"test\",\n)");
}