From 8aa8cc9c12fe18887eb13a08962bde982dad996d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 16 Sep 2012 01:00:04 -0400 Subject: [PATCH] Add unit test framework and sample script. --- errcheck.m | 7 +++++ erreval.m | 17 ++++++++++++ unit-tests | 11 ++++++++ unit_init.m | 41 ++++++++++++++++++++++++++++ unit_results.m | 20 ++++++++++++++ unit_test.m | 67 ++++++++++++++++++++++++++++++++++++++++++++++ unit_test_equals.m | 5 ++++ unit_test_err.m | 3 +++ 8 files changed, 171 insertions(+) create mode 100644 errcheck.m create mode 100644 erreval.m create mode 100755 unit-tests create mode 100644 unit_init.m create mode 100644 unit_results.m create mode 100644 unit_test.m create mode 100644 unit_test_equals.m create mode 100644 unit_test_err.m diff --git a/errcheck.m b/errcheck.m new file mode 100644 index 0000000..fc3d2f2 --- /dev/null +++ b/errcheck.m @@ -0,0 +1,7 @@ +function e = errcheck(error_prefix) + e = 1; + if (index(__error_text__, error_prefix) != 1) + e = 0; + printf("\nUNEXPECTED ERROR: %s", __error_text__); + endif +end diff --git a/erreval.m b/erreval.m new file mode 100644 index 0000000..995d854 --- /dev/null +++ b/erreval.m @@ -0,0 +1,17 @@ +function rv = erreval(error_prefix, try_str, catch_str) + ## erreval() extends the built-in function eval(). Return 0 if + ## try_str does not raise the error of type error_prefix, return 1 + ## otherwise. + + global unittest_results; + for k = 1:length(unittest_results.eval_globals) + eval(unittest_results.eval_globals{k}); + end + + rv = 0; + try + eval(try_str); + catch + rv = errcheck(error_prefix); + end +endfunction diff --git a/unit-tests b/unit-tests new file mode 100755 index 0000000..c0737ba --- /dev/null +++ b/unit-tests @@ -0,0 +1,11 @@ +#!/usr/bin/octave --silent + +unit_init(1, {}); + +unit_test_equals("sin[0] == 0", ... + 0, ... + divided_difference(@sin, 0)); + +unit_test_equals("sin[0, pi] == 0", ... + 0, ... + divided_difference(@sin, [0,pi])); diff --git a/unit_init.m b/unit_init.m new file mode 100644 index 0000000..b692a80 --- /dev/null +++ b/unit_init.m @@ -0,0 +1,41 @@ +function unit_init(verbosity, global_vars) + ## Initialize the global structure unittest_results, which is needed + ## in all functions of the *unittest module. Debugging information + ## is printed if verbosity==1. global_vars is a cell array of the + ## names of the global variables used in the tests. + ## + ## e.g. unit_init(1, {"g", "a", "x"}) + + global unittest_results; + + unittest_results.verbose = 0; + unittest_results.eval_globals = {}; + if (nargin > 0) + if (!isscalar(verbosity) || verbosity < 0 || verbosity > 1) + warning("unit_init: verbose must be 0 or 1"); + else + unittest_results.verbose = verbosity; + endif + + if (nargin == 2 && iscell(global_vars)) + for i = 1:length(global_vars) + unittest_results.eval_globals{i} = strcat("global ", global_vars{i}, ";"); + endfor + else + error("global_vars must be a cell array"); + endif + + if (nargin > 2) + usage("expecting 2 arguments only"); + end + endif + + unittest_results.total = 0; # number of testcases attempted + unittest_results.pass = 0; # number of expected passed + unittest_results.fail = 0; # number of unexpected failures + unittest_results.upass = 0; # number of unexpected passes + unittest_results.xfail = 0; # number of expected failures + unittest_results.unresolved = 0; # number of unresolved testcases + + default_eval_print_flag = 0; +endfunction diff --git a/unit_results.m b/unit_results.m new file mode 100644 index 0000000..f6485ba --- /dev/null +++ b/unit_results.m @@ -0,0 +1,20 @@ +function unit_results() + ## Print the results from previous unittest calls in pretty format. + + global unittest_results; + + printf("\n"); + printf("# of testcases attempted %d\n", unittest_results.total); + printf("# of expected passes %d\n", unittest_results.pass); + printf("# of expected failures %d\n", unittest_results.xfail); + printf("# of unexpected passes %d\n", unittest_results.upass); + printf("# of unexpected failures %d\n", unittest_results.fail); + printf("# of unresolved testcases %d\n", unittest_results.unresolved); + printf("\n"); + + if (unittest_results.total == unittest_results.pass + unittest_results.xfail) + printf("Unit testing completed successfully!\n"); + else + printf("One or more tests failed!\n"); + endif +endfunction diff --git a/unit_test.m b/unit_test.m new file mode 100644 index 0000000..e21739d --- /dev/null +++ b/unit_test.m @@ -0,0 +1,67 @@ +function result = unit_test(test_title, expect_pass, actual_result) + ## Function unittest compares the ACTUAL_RESULT of running + ## a test (either 0 for failure, or 1 for success) with the + ## expected outcome of the test EXPECT_PASS (either 0 for expecting + ## a failure, or 1 for expecting pass). TEST_TITLE is the name of + ## the test. All test results will be accompanied by the test's + ## title. + ## + ## The result of unit_test is on of the following: UNRESOLVED: The + ## test did neither return 0 nor 1. PASS: expected pass, got pass. + ## FAIL: expected pass, got fail. UPASS: expected fail, got pass. + ## XFAIL: expected fail, got fail. + ## + ## A call to unit_test typically looks like this: + ## + ## unit_test("scalar integer addition", 1, eval("1 + 1 == 2;")); + + global unittest_results; + + ## Sanity check input parameters + if ( nargin < 3 || nargin > 4 ) + error("Function run_rest expects 3 or 4 parameters."); + endif + + if (!ischar(test_title)) + error("Expecting TEST_TITLE (arg 1) to be a string."); + endif + + if (expect_pass != 0 && expect_pass != 1) + error("Expecting EXPECT_PASS (arg 2) to be 0 or 1."); + endif + + unittest_results.total++; + + ## Take actions depending on what test result we expect + ## (expect_pass), and what we actually got (actual_result). + if (actual_result != 0 && actual_result != 1) + result = "UNRESOLVED"; + unittest_results.unresolved++; + if (actual_result == 2) + printf("SYNTAX ERROR: %s\n", test_title); + endif + return; + endif + + if (expect_pass == 1 && actual_result == 1) + result = "PASS"; + if (unittest_results.verbose != 0) + printf("PASS: %s\n", test_title); + else + printf('.'); + endif + unittest_results.pass++; + elseif (expect_pass == 1 && actual_result == 0) + result = "FAIL"; + printf("FAIL: %s\n\n", test_title); + unittest_results.fail++; + elseif (expect_pass == 0 && actual_result == 0) + result = "XFAIL"; + printf("XFAIL: %s\n", test_title); + unittest_results.xfail++; + elseif (expect_pass == 0 && actual_result == 1) + result = "UPASS"; + printf("UPASS: %s\n", test_title); + unittest_results.upass++; + endif +endfunction diff --git a/unit_test_equals.m b/unit_test_equals.m new file mode 100644 index 0000000..e8b00d6 --- /dev/null +++ b/unit_test_equals.m @@ -0,0 +1,5 @@ +function unit_test_equals(test_title, result_A, result_B) + tol = 0.0001; + err = abs(result_A - result_B); + unit_test(test_title, 1, all(err < tol)); +endfunction diff --git a/unit_test_err.m b/unit_test_err.m new file mode 100644 index 0000000..cfecd87 --- /dev/null +++ b/unit_test_err.m @@ -0,0 +1,3 @@ +function unit_test_err(test_title, expected_error_prefix, evaluated_string) + unit_test(test_title, 1, erreval(expected_error_prefix, evaluated_string)); +endfunction -- 2.44.2