void test_lazy() { #ifdef DEFERRED_CALLS_FIRST_DRAFT Branch branch; Term* a = branch.compile("a = add(1 2)"); set_lazy_call(a, true); Stack context; push_frame(&context, &branch); run_interpreter(&context); test_equals(get_register(&context, a), ":Unevaluated"); Frame* frame = push_frame(&context, &branch); frame->pc = a->index; frame->startPc = frame->pc; frame->endPc = frame->pc + 1; frame->strategy = ByDemand; run_interpreter(&context); test_equals(get_register(&context, a), "3"); reset_stack(&context); Term* b = branch.compile("b = add(a a)"); push_frame(&context, &branch); run_interpreter(&context); test_equals(get_register(&context, a), "3"); test_equals(get_register(&context, b), "6"); #endif }
void repro_source_after_rename() { Branch branch; // Simple test Term* a = branch.compile("a = 1"); rename(a, "apple"); test_equals(get_term_source_text(a), "apple = 1"); // Rename a function argument Term* b = branch.compile("b = 5"); Term* add = branch.compile("add( b , 10)"); rename(b, "banana"); test_equals(get_term_source_text(add), "add( banana , 10)"); // Rename a function Term* myfunc = import_function(&branch, NULL, "def myfunc(int)"); Term* myfunc_call = branch.compile("myfunc(555)"); rename(myfunc, "my_renamed_func"); test_equals(get_term_source_text(myfunc_call), "my_renamed_func(555)"); }
void test_trimmed_state(std::string const& source, std::string const& dest, std::string const& expectedTrash) { Branch sourceBranch; sourceBranch.compile(source); if (test_fail_on_static_error(&sourceBranch)) return; Stack context; evaluate_branch(&context, &sourceBranch); if (test_fail_on_runtime_error(context)) return; Branch destBranch; destBranch.compile(dest); if (test_fail_on_static_error(&destBranch)) return; caValue trash; strip_orphaned_state(&destBranch, &context.state, &trash); if (expectedTrash != trash.toString()) { declare_current_test_failed(); std::cout << "In test " << get_current_test_name() << std::endl; std::cout << expectedTrash << " != " << trash.toString() << std::endl; } }
void test_state_is_reset_when_if_fails() { Branch branch; Stack context; Term* c = branch.compile("c = true"); branch.compile("if c { state i = 0; i += 1 } else { 'hi' }"); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [{i: 1}]}"); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [{i: 2}]}"); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [{i: 3}]}"); set_bool(c, false); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [null, null]}"); set_bool(c, true); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [{i: 1}]}"); }
void assign_output_type() { Branch branch; branch.compile("a = [1]"); Term* assign = branch.compile("a[0] = 2"); test_equals(assign->type->name, "List"); }
void test_state_is_reset_when_if_fails2() { // Similar to test_state_is_reset_when_if_fails, but this one doesn't // have an 'else' block and it uses test_oracle. internal_debug_function::oracle_clear(); Branch branch; Term* a = branch.compile("a = true"); branch.compile("if a { state s = test_oracle() }"); internal_debug_function::oracle_send(1); internal_debug_function::oracle_send(2); internal_debug_function::oracle_send(3); Stack context; evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [{s: 1}]}"); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [{s: 1}]}"); set_bool(a, false); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [null, null]}"); set_bool(a, true); evaluate_branch(&context, &branch); test_equals(&context.state, "{_if_block: [{s: 2}]}"); }
void for_loop_output_type() { Branch branch; branch.compile("a = 1"); branch.compile("for i in [1] { a = 2 }"); test_equals(branch["a"]->type->name, "int"); }
void infer_type_of_append() { Branch branch; branch.compile("a = []"); branch.compile("a.append(1)"); Term* b = branch.compile("a[0]"); test_equals(b->type->name, "int"); }
void compare_builtin_types() { Branch branch; Term* aFloat = branch.compile("1.2"); test_assert(term_output_always_satisfies_type(aFloat, unbox_type(FLOAT_TYPE))); test_assert(term_output_never_satisfies_type(aFloat, unbox_type(INT_TYPE))); Term* anInt = branch.compile("1"); test_assert(term_output_always_satisfies_type(anInt, unbox_type(FLOAT_TYPE))); test_assert(term_output_always_satisfies_type(anInt, unbox_type(INT_TYPE))); }
void name_visible_iterator_1() { Branch branch; branch.compile("a = 1"); Term* b = branch.compile("b = 1"); branch.compile("c = 1; d = 1; e = 1"); branch.compile("b = 2; f = 1; g = 1"); NameVisibleIterator it(b); test_equals(it.current()->name,"c"); test_assert(!it.finished()); ++it; test_equals(it.current()->name,"d"); test_assert(!it.finished()); ++it; test_equals(it.current()->name,"e"); test_assert(!it.finished()); ++it; test_assert(it.finished()); }
void test_execution_with_elif() { Branch branch; branch.compile("x = 5"); branch.compile("if x > 5 { test_spy('Fail') } " "elif x < 5 { test_spy('Fail')} " "elif x == 5 { test_spy('Success')} " "else { test_spy('Fail') }"); internal_debug_function::spy_clear(); evaluate_branch(&branch); test_assert(&branch); test_equals(internal_debug_function::spy_results(), "['Success']"); }
void test_snippet(std::string const& source) { Branch branch; branch.compile(source); if (test_fail_on_static_error(&branch)) return; Stack context; evaluate_branch(&context, &branch); if (test_fail_on_runtime_error(context)) return; // Try stripping orphaned state, this should not have an effect. caValue trash; strip_orphaned_state(&branch, &context.state, &trash); if (!is_null(&trash)) { std::cout << "Falsely orphaned state in " << get_current_test_name() << std::endl; std::cout << "Code = " << source << std::endl; std::cout << "Trash = " << trash.toString() << std::endl; declare_current_test_failed(); return; } }
void bug_reproducing_list_after_eval() { // There once was a bug where source repro would fail when using a list // in a vectorized function, but only after that piece of code had been // evaluated. This was because vectorize_vv was calling apply() which // was changing the 'statement' property of its inputs. Branch branch; Term* sum = branch.compile("[1 1] + [1 1]"); Term* in0 = sum->input(0); Term* in1 = sum->input(0); test_equals(get_term_source_text(in0), "[1 1]"); test_equals(get_term_source_text(in1), "[1 1]"); test_equals(get_input_source_text(sum, 0), "[1 1] "); test_equals(get_input_source_text(sum, 1), " [1 1]"); test_equals(get_branch_source_text(&branch), "[1 1] + [1 1]"); evaluate_branch(&branch); test_equals(get_term_source_text(in0), "[1 1]"); test_equals(get_term_source_text(in1), "[1 1]"); test_equals(get_input_source_text(sum, 0), "[1 1] "); test_equals(get_input_source_text(sum, 1), " [1 1]"); test_equals(get_branch_source_text(&branch), "[1 1] + [1 1]"); }
void test_custom_object() { g_currentlyAllocated = 0; g_totalAllocated = 0; Branch branch; branch.compile( "type MyType; \n" "def create_object() -> MyType\n" "def check_object(MyType t)\n" "s = create_object()\n" "check_object(s)\n" ); circa_install_function(&branch, "create_object", create_object); circa_install_function(&branch, "check_object", check_object); circa_setup_object_type(circa_find_type(&branch, "MyType"), sizeof(CustomObject), CustomObjectRelease); // Shouldn't allocate any objects before running. test_equals(g_currentlyAllocated, 0); test_equals(g_totalAllocated, 0); Stack stack; push_frame(&stack, &branch); run_interpreter(&stack); test_assert(&stack); circa_clear_stack(&stack); // Running the script should only cause 1 object allocation. test_equals(g_currentlyAllocated, 0); test_equals(g_totalAllocated, 1); }
void test_type_not_prematurely_used() { // Verify that a circa-defined type is not used until interpreter time. Modifying // a type's release() handler after there are already instances of it, is not good. Branch branch; branch.compile( "type MyType; \n" "def f() -> MyType\n" "def g(MyType t)\n" "s = f()\n" "g(s)\n" "l = [s s s]\n" "type MyCompoundType {\n" " MyType t\n" "}\n" "state MyType st\n" "state MyType st2 = create(MyType)\n" ); Type* myType = (Type*) circa_find_type(&branch, "MyType"); Type* myCompoundType = (Type*) circa_find_type(&branch, "MyCompoundType"); test_assert(!myType->inUse); test_assert(!myCompoundType->inUse); circa::Value value1, value2; create(myType, &value1); create(myCompoundType, &value2); test_assert(myType->inUse); test_assert(myCompoundType->inUse); }
void test_state_simple() { Branch branch; Stack context; // Simple test, condition never changes Term* block = branch.compile("if true { state i = 0; i += 1 }"); evaluate_branch(&context, &branch); caValue *i = context.state.getField("_if_block")->getIndex(0)->getField("i"); test_assert(i != NULL); test_assert(as_int(i) == 1); evaluate_branch(&context, &branch); i = context.state.getField("_if_block")->getIndex(0)->getField("i"); test_assert(as_int(i) == 2); evaluate_branch(&context, &branch); i = context.state.getField("_if_block")->getIndex(0)->getField("i"); test_assert(as_int(i) == 3); // Same test with elif branch.clear(); block = branch.compile("if false {} elif true { state i = 0; i += 1 }"); evaluate_branch(&context, &branch); i = context.state.getField("_if_block")->getIndex(1)->getField("i"); test_assert(as_int(i) == 1); evaluate_branch(&context, &branch); i = context.state.getField("_if_block")->getIndex(1)->getField("i"); test_assert(as_int(i) == 2); evaluate_branch(&context, &branch); i = context.state.getField("_if_block")->getIndex(1)->getField("i"); test_assert(as_int(i) == 3); // Same test with else branch.clear(); Stack context2; block = branch.compile("if false {} else { state i = 0; i += 1 }"); evaluate_branch(&context2, &branch); i = context2.state.getField("_if_block")->getIndex(1)->getField("i"); test_assert(as_int(i) == 1); evaluate_branch(&context2, &branch); i = context2.state.getField("_if_block")->getIndex(1)->getField("i"); test_assert(as_int(i) == 2); evaluate_branch(&context2, &branch); i = context2.state.getField("_if_block")->getIndex(1)->getField("i"); test_assert(as_int(i) == 3); }
void infer_length() { Branch branch; caValue result; statically_infer_result(branch.compile("length([])"), &result); test_equals(&result, "0"); statically_infer_result(branch.compile("length([1])"), &result); test_equals(&result, "1"); statically_infer_result(branch.compile("length([1 2 3])"), &result); test_equals(&result, "3"); return; // TEST_DISABLED statically_infer_result(branch.compile("length([1 2 3].append(3))"), &result); test_equals(&result, "4"); }
void test_that_stripping_state_is_recursive() { Branch branch; setup(branch); branch.compile("if true { state a = 1; state s; s = alloc_handle(s) }"); Stack context; evaluate_branch(&context, &branch); test_equals(&g_slots, "[true, false, false]"); clear_branch(&branch); branch.compile("if true { state a = 1 }"); strip_orphaned_state(&branch, &context.state); test_equals(&g_slots, "[false, false, false]"); }
void test_state_inside_if_block() { Branch branch; setup(branch); branch.compile("state s = null"); branch.compile("if is_null(s) { s = alloc_handle(s) }"); Stack context; evaluate_branch(&context, &branch); test_equals(&g_slots, "[true, false, false]"); clear_branch(&branch); strip_orphaned_state(&branch, &context.state); test_equals(&g_slots, "[false, false, false]"); }
void test_if_elif_else() { Branch branch; branch.compile("if true { a = 1 } elif true { a = 2 } else { a = 3 } a=a"); evaluate_branch(&branch); test_assert(branch.contains("a")); test_equals(branch["a"]->asInt(), 1); branch.compile( "if false { b = 'apple' } elif false { b = 'orange' } else { b = 'pineapple' } b=b"); evaluate_branch(&branch); test_assert(branch.contains("b")); test_assert(branch["b"]->asString() == "pineapple"); // try one without 'else' branch.clear(); branch.compile("c = 0"); branch.compile("if false { c = 7 } elif true { c = 8 }; c=c"); evaluate_branch(&branch); test_assert(branch.contains("c")); test_assert(branch["c"]->asInt() == 8); // try with some more complex conditions branch.clear(); branch.compile("x = 5"); branch.compile("if x > 6 { compare = 1 } elif x < 6 { compare = -1 } else { compare = 0}"); branch.compile("compare=compare"); evaluate_branch(&branch); test_assert(branch.contains("compare")); test_assert(branch["compare"]->asInt() == -1); }
void test_in_subroutine_state() { Branch branch; setup(branch); branch.compile("def hi(any input) { state s = input }"); branch.compile("state s"); branch.compile("alloc_handle(@s)"); branch.compile("hi(s)"); Stack context; evaluate_branch(&context, &branch); test_equals(&g_slots, "[true, false, false]"); set_null(&context.state); test_equals(&g_slots, "[false, false, false]"); }
void test_dont_always_rebind_inner_names() { Branch branch; branch.compile("if false { b = 1 } elif false { c = 1 } elif false { d = 1 } else { e = 1 }"); evaluate_branch(&branch); test_assert(!branch.contains("b")); test_assert(!branch.contains("c")); test_assert(!branch.contains("d")); test_assert(!branch.contains("e")); }
void test_refs_are_destruction_safe() { Branch branch; Term* a = branch.compile("a = 1"); caValue ref; set_ref(&ref, a); test_assert(as_ref(&ref) == a); clear_branch(&branch); test_assert(as_ref(&ref) == NULL); }
void test_cast_first_inputs() { // Pass an input of [1] to a branch that expects a compound type. // The function will need to cast the [1] to T in order for it to work. Branch branch; branch.compile("type T { int i }"); Term* f = branch.compile("def f(T t) -> int { return t.i }"); Stack context; push_frame(&context, function_contents(f)); caValue* in = circa_input((caStack*) &context, 0); circa_set_list(in, 1); circa_set_int(circa_index(in, 0), 5); run_interpreter(&context); test_assert(circa_int(circa_output((caStack*) &context, 0)) == 5); }
void repro_source_after_append_code() { Branch branch; Term* target = branch.compile("target = {}"); branch.eval("bm = branch_ref(target)"); branch.eval("bm.append_code({ 1 + 1 })"); test_equals(get_branch_source_text(nested_contents(target)), " 1 + 1 "); }
void setup(Branch& branch) { g_slots.resize(numSlots); for (int i=0; i < numSlots; i++) set_bool(g_slots[i], false); handle_t::setup_type(&handle_type); set_opaque_pointer(&handle_type.parameter, (void*) on_release_func); branch.compile("def alloc_handle(any s) -> any;"); install_function(branch["alloc_handle"], alloc_handle); }
void test_with_state() { Branch branch; setup(branch); test_equals(&g_slots, "[false, false, false]"); branch.compile("state s"); branch.compile("alloc_handle(@s)"); set_branch_in_progress(&branch, false); Stack context; evaluate_branch(&context, &branch); test_equals(&g_slots, "[true, false, false]"); evaluate_branch(&context, &branch); test_equals(&g_slots, "[true, false, false]"); reset(&context.state); test_equals(&g_slots, "[false, false, false]"); }
void test_deleted_state() { Branch branch; setup(branch); branch.compile("state s"); branch.compile("alloc_handle(@s)"); branch.compile("state t"); branch.compile("alloc_handle(@t)"); Stack context; evaluate_branch(&context, &branch); test_equals(&g_slots, "[true, true, false]"); clear_branch(&branch); branch.compile("state t"); strip_orphaned_state(&branch, &context.state); test_equals(&g_slots, "[false, true, false]"); }
void test_change_function() { Branch branch; // simple test Term* a = branch.compile("add(3,3)"); evaluate_branch(&branch); test_assert(as_int(a) == 6); change_function(a, KERNEL->get("mult_i")); evaluate_branch(&branch); test_assert(as_int(a) == 9); }
void branch_iterator_2_2() { Branch branch; branch.compile("a = { b = { c = { d = 3; e = 4; }}}"); BranchIterator2 it(&branch); test_assert(it.current()->name == "a"); test_assert(!it.finished()); ++it; test_assert(it.current()->name == "b"); test_assert(!it.finished()); ++it; test_assert(it.current()->name == "c"); test_assert(!it.finished()); ++it; test_assert(it.current()->name == "d"); test_assert(!it.finished()); ++it; test_assert(it.current()->name == "e"); test_assert(!it.finished()); ++it; test_assert(it.finished()); }