mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
If not supplied with a fault, `test::@error` checks if a fault of any type/value was returned
358 lines
7.7 KiB
Plaintext
358 lines
7.7 KiB
Plaintext
module test::std::core::test @test;
|
|
import std::core::runtime @public;
|
|
import std::core::builtin;
|
|
import std::io;
|
|
|
|
struct TestState
|
|
{
|
|
int n_runs;
|
|
int n_fails;
|
|
bool expected_fail;
|
|
|
|
// NOTE: we must wrap setup/teardown functions to hide them from module @test runner
|
|
TestFn setup_fn;
|
|
TestFn teardown_fn;
|
|
PanicFn old_panic; // original test panic, use it when it's really fails
|
|
PanicFn panic_mock_fn; // mock panic, for testing the test:: failed
|
|
void* buf;
|
|
}
|
|
|
|
TestState state =
|
|
{
|
|
.setup_fn = fn void()
|
|
{
|
|
state.n_runs++;
|
|
state.n_fails = 0;
|
|
|
|
assert (runtime::test_context.assert_print_backtrace);
|
|
assert (builtin::panic != state.panic_mock_fn, "missing finalization of panic");
|
|
state.buf = mem::alloc(int);
|
|
|
|
state.old_panic = builtin::panic;
|
|
builtin::panic = state.panic_mock_fn;
|
|
},
|
|
.teardown_fn = fn void()
|
|
{
|
|
builtin::panic = state.old_panic;
|
|
|
|
assert(state.n_runs > 0);
|
|
|
|
if (state.expected_fail)
|
|
{
|
|
assert(state.n_fails > 0, "test case expected to fail, but it's not");
|
|
}
|
|
state.n_fails = 0;
|
|
state.expected_fail = false;
|
|
state.n_runs = 0;
|
|
mem::free(state.buf);
|
|
},
|
|
.panic_mock_fn = fn void (String message, String file, String function, uint line)
|
|
{
|
|
if (runtime::test_context.is_in_panic) return;
|
|
if (runtime::test_context.assert_print_backtrace)
|
|
{
|
|
$if env::NATIVE_STACKTRACE:
|
|
builtin::print_backtrace(message, 0);
|
|
$else
|
|
io::printfn("No print_backtrace() supported by this platform");
|
|
$endif
|
|
}
|
|
runtime::test_context.assert_print_backtrace = true;
|
|
if (state.expected_fail)
|
|
{
|
|
state.n_fails++;
|
|
}
|
|
else
|
|
{
|
|
builtin::panic = state.old_panic;
|
|
state.old_panic(message, file, function, line);
|
|
}
|
|
runtime::test_context.is_in_panic = false;
|
|
}
|
|
};
|
|
|
|
|
|
fn void test_eq()
|
|
{
|
|
test::eq(1, 1);
|
|
test::eq(true, true);
|
|
test::eq(1.31, 1.31);
|
|
test::eq("foo", "foo");
|
|
}
|
|
|
|
fn void test_almost_equal()
|
|
{
|
|
test::eq_approx(1, 1);
|
|
test::eq_approx(1.31, 1.31);
|
|
test::eq_approx(1.31f, 1.31f);
|
|
test::eq_approx(double.nan, double.nan);
|
|
test::eq_approx(float.nan, float.nan);
|
|
test::eq_approx(1.31, 1.31, delta: 0.01);
|
|
test::eq_approx(1.311, 1.312, delta: 0.01);
|
|
test::eq_approx(1.311, 1.312, places: 2);
|
|
// 7 decimal places are default
|
|
test::eq_approx(1.00000001, 1.00000000);
|
|
}
|
|
|
|
|
|
fn void test_almost_equal_fails()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
// 7 decimal places are default
|
|
test::eq_approx(1.0000001, 1.00000000);
|
|
}
|
|
|
|
fn void test_almost_equal_fails_nan()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::eq_approx(1.0000001, double.nan);
|
|
}
|
|
|
|
fn void test_almost_equal_fails_nan2()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::eq_approx(double.nan, 1);
|
|
}
|
|
|
|
fn void test_almost_equal_fails_equal_nan_false()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::eq_approx(double.nan, double.nan, equal_nan: false);
|
|
}
|
|
|
|
fn void setup_teardown()
|
|
{
|
|
state.n_runs = 0; // just in case of previous test failed
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
|
|
test::eq(state.n_runs, 1);
|
|
test::eq(state.n_fails, 0);
|
|
test::eq(state.expected_fail, false);
|
|
}
|
|
|
|
fn void setup_no_teardown()
|
|
{
|
|
test::@setup(state.setup_fn);
|
|
|
|
test::eq(state.n_runs, 1);
|
|
test::eq(state.n_fails, 0);
|
|
test::eq(state.expected_fail, false);
|
|
|
|
mem::free(state.buf);
|
|
|
|
// WARNING: reverting back original panic func
|
|
builtin::panic = state.old_panic;
|
|
}
|
|
|
|
fn void expected_fail()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::eq(state.n_fails, 0);
|
|
test::eq(2, 1); // this fails, and we test it
|
|
test::eq(state.n_fails, 1);
|
|
}
|
|
|
|
fn void test_neq()
|
|
{
|
|
test::ne(2, 1);
|
|
test::ne(false, true);
|
|
test::ne(1.32, 1.31);
|
|
test::ne("foo", "bar");
|
|
}
|
|
|
|
fn void test_neq_fails()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::ne(1, 1);
|
|
}
|
|
|
|
fn void test_gt()
|
|
{
|
|
test::gt(2, 1);
|
|
test::gt(true, false);
|
|
test::gt(1.32, 1.31);
|
|
}
|
|
|
|
fn void test_gt_fails_when_equal()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::gt(2, 2);
|
|
}
|
|
|
|
fn void test_gt_fails_when_less()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::gt(1, 2);
|
|
}
|
|
|
|
|
|
fn void test_gte()
|
|
{
|
|
test::ge(2, 1);
|
|
test::ge(true, false);
|
|
test::ge(1.32, 1.31);
|
|
test::ge(2, 2);
|
|
test::ge(true, true);
|
|
test::ge(1.32, 1.32);
|
|
}
|
|
|
|
fn void test_gte_fails_when_less()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::ge(1, 2);
|
|
}
|
|
|
|
fn void test_lt()
|
|
{
|
|
test::lt(1, 2);
|
|
test::lt(false, true);
|
|
test::lt(1.31, 1.32);
|
|
}
|
|
|
|
fn void test_lt_fails_when_equal()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::lt(2, 2);
|
|
}
|
|
|
|
fn void test_lt_fails_when_greater()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::lt(2, 1);
|
|
}
|
|
|
|
fn void test_lte()
|
|
{
|
|
test::le(1, 2);
|
|
test::le(false, true);
|
|
test::le(1.31, 1.32);
|
|
test::le(2, 2);
|
|
test::le(true, true);
|
|
test::le(1.32, 1.32);
|
|
}
|
|
|
|
fn void test_lte_fails_when_greater()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::le(2, 1);
|
|
}
|
|
|
|
fn void test_check(){
|
|
test::@check(1 == 1);
|
|
test::@check(1.2 == 1.2, "1 == 1");
|
|
test::@check(true == true, "1 == 1");
|
|
test::@check("foo" == "foo", "2 == %d", 1 );
|
|
}
|
|
|
|
fn void test_check_fails()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::@check(2 == 1, "2 == %d", 1 );
|
|
}
|
|
|
|
fn void test_check_fails_no_info()
|
|
{
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::@check(2 == 1);
|
|
}
|
|
|
|
alias TestIntFn = fn int? (int a, int b);
|
|
alias TestFailFn = fn void? (bool to_fail);
|
|
|
|
faultdef FOO;
|
|
|
|
fn void test_error()
|
|
{
|
|
TestFailFn ffail_void = fn void?(bool to_fail)
|
|
{
|
|
if (to_fail) return io::FILE_NOT_FOUND?;
|
|
};
|
|
TestIntFn ffail_int = fn int?(int a, int b)
|
|
{
|
|
if (b == 0) return io::FILE_NOT_FOUND?;
|
|
return a / b;
|
|
};
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
|
|
test::@error(ffail_void(true), io::FILE_NOT_FOUND);
|
|
test::@error(ffail_int(1, 0), io::FILE_NOT_FOUND);
|
|
}
|
|
|
|
fn void test_error_not_raised()
|
|
{
|
|
TestIntFn ffail_int = fn int?(int a, int b) {
|
|
if (b == 0) return io::FILE_NOT_FOUND?;
|
|
return a / b;
|
|
};
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::@error(ffail_int(1, 1), io::FILE_NOT_FOUND);
|
|
}
|
|
|
|
fn void test_error_nofault()
|
|
{
|
|
TestFailFn ffail_void = fn void?(bool to_fail)
|
|
{
|
|
if (to_fail) return io::FILE_NOT_FOUND?;
|
|
};
|
|
TestIntFn ffail_int = fn int?(int a, int b)
|
|
{
|
|
if (b == 0) return io::FILE_NOT_FOUND?;
|
|
return a / b;
|
|
};
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
|
|
test::@error(ffail_void(true));
|
|
test::@error(ffail_int(1, 0));
|
|
}
|
|
|
|
fn void test_error_nofault_not_raised()
|
|
{
|
|
TestIntFn ffail_int = fn int?(int a, int b) {
|
|
if (b == 0) return io::FILE_NOT_FOUND?;
|
|
return a / b;
|
|
};
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
test::@error(ffail_int(1, 1));
|
|
}
|
|
|
|
fn void test_error_wrong_error_expected()
|
|
{
|
|
TestIntFn ffail_int = fn int?(int a, int b) {
|
|
if (b == 0) return io::BUSY?;
|
|
return a / b;
|
|
};
|
|
test::@setup(state.setup_fn, state.teardown_fn);
|
|
state.expected_fail = true;
|
|
|
|
test::@error(ffail_int(1, 0), io::FILE_NOT_FOUND);
|
|
}
|
|
|
|
fn void test_std_out_hijack()
|
|
{
|
|
io::print("print: aldsjalsdjlasjdlja\n");
|
|
io::printf("printf: aldsjalsdjlasjdlja\n");
|
|
io::eprint("eprint: aldsjalsdjlasjdlja\n");
|
|
io::eprintfn("eprintfn: aldsjalsdjlasjdlja\n");
|
|
io::fprint(io::stdout(), "fprint: stdout aldsjalsdjlasjdlja\n")!!;
|
|
io::fprint(io::stderr(), "fprint: stderr aldsjalsdjlasjdlja\n")!!;
|
|
io::fprintf(io::stderr(), "fprintf: stderr aldsjalsdjlasjdlja\n")!!;
|
|
io::fprintf(io::stderr(), "fprintfn: stderr aldsjalsdjlasjdlja\n")!!;
|
|
test::eq(true, true);
|
|
}
|