Add unit test framework and sample script.
authorMichael Orlitzky <michael@orlitzky.com>
Sun, 16 Sep 2012 05:00:04 +0000 (01:00 -0400)
committerMichael Orlitzky <michael@orlitzky.com>
Sun, 16 Sep 2012 05:00:04 +0000 (01:00 -0400)
errcheck.m [new file with mode: 0644]
erreval.m [new file with mode: 0644]
unit-tests [new file with mode: 0755]
unit_init.m [new file with mode: 0644]
unit_results.m [new file with mode: 0644]
unit_test.m [new file with mode: 0644]
unit_test_equals.m [new file with mode: 0644]
unit_test_err.m [new file with mode: 0644]

diff --git a/errcheck.m b/errcheck.m
new file mode 100644 (file)
index 0000000..fc3d2f2
--- /dev/null
@@ -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 (file)
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 (executable)
index 0000000..c0737ba
--- /dev/null
@@ -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 (file)
index 0000000..b692a80
--- /dev/null
@@ -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 (file)
index 0000000..f6485ba
--- /dev/null
@@ -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 (file)
index 0000000..e21739d
--- /dev/null
@@ -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 (file)
index 0000000..e8b00d6
--- /dev/null
@@ -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 (file)
index 0000000..cfecd87
--- /dev/null
@@ -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