You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by nw...@apache.org on 2013/05/30 22:36:44 UTC

[lucy-commits] [26/26] git commit: refs/heads/separate-clownfish-wip2 - Rework Clownfish test harness

Rework Clownfish test harness

This commit corrects some bad design decisions I made when I
reimplemented the Clownfish test harness.

The new TestBatch class that the test modules inherit from contains only
a single abstract method "Run". All the other functionality is moved to
a new class TestBatchRunner. An instance of TestBatchRunner is passed to
the "Run" method of TestBatch. This means that TestBatch doesn't have to
care about TestFormatters.

The old TestRunner is renamed to TestSuiteRunner. There's a new class
TestSuite that manages a collection of TestBatches.


Project: http://git-wip-us.apache.org/repos/asf/lucy/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucy/commit/3da6ebd3
Tree: http://git-wip-us.apache.org/repos/asf/lucy/tree/3da6ebd3
Diff: http://git-wip-us.apache.org/repos/asf/lucy/diff/3da6ebd3

Branch: refs/heads/separate-clownfish-wip2
Commit: 3da6ebd3f6fad0db4ee79db07b630a56c7dcf667
Parents: cb13877
Author: Nick Wellnhofer <we...@aevum.de>
Authored: Tue May 28 00:33:52 2013 +0200
Committer: Nick Wellnhofer <we...@aevum.de>
Committed: Thu May 30 22:32:00 2013 +0200

----------------------------------------------------------------------
 c/t/test_lucy.c                                    |   18 +-
 core/Clownfish/Test.c                              |   97 +----
 core/Clownfish/Test.cfh                            |   22 +-
 core/Clownfish/Test/TestByteBuf.c                  |   88 ++---
 core/Clownfish/Test/TestByteBuf.cfh                |    7 +-
 core/Clownfish/Test/TestCharBuf.c                  |  202 +++++-----
 core/Clownfish/Test/TestCharBuf.cfh                |    7 +-
 core/Clownfish/Test/TestErr.c                      |   22 +-
 core/Clownfish/Test/TestErr.cfh                    |    7 +-
 core/Clownfish/Test/TestHash.c                     |  100 +++---
 core/Clownfish/Test/TestHash.cfh                   |    7 +-
 core/Clownfish/Test/TestLockFreeRegistry.c         |   32 +-
 core/Clownfish/Test/TestLockFreeRegistry.cfh       |    7 +-
 core/Clownfish/Test/TestNum.c                      |  156 ++++----
 core/Clownfish/Test/TestNum.cfh                    |    7 +-
 core/Clownfish/Test/TestObj.c                      |   88 ++---
 core/Clownfish/Test/TestObj.cfh                    |    7 +-
 core/Clownfish/Test/TestVArray.c                   |  150 ++++----
 core/Clownfish/Test/TestVArray.cfh                 |    7 +-
 core/Clownfish/Test/Util/TestAtomic.c              |   32 +-
 core/Clownfish/Test/Util/TestAtomic.cfh            |    7 +-
 core/Clownfish/Test/Util/TestMemory.c              |   46 +--
 core/Clownfish/Test/Util/TestMemory.cfh            |    7 +-
 core/Clownfish/Test/Util/TestNumberUtils.c         |  116 +++---
 core/Clownfish/Test/Util/TestNumberUtils.cfh       |    7 +-
 core/Clownfish/Test/Util/TestStringHelper.c        |  140 ++++----
 core/Clownfish/Test/Util/TestStringHelper.cfh      |    7 +-
 core/Clownfish/TestHarness/TestBatch.c             |  293 --------------
 core/Clownfish/TestHarness/TestBatch.cfh           |   98 +-----
 core/Clownfish/TestHarness/TestBatchRunner.c       |  308 +++++++++++++++
 core/Clownfish/TestHarness/TestBatchRunner.cfh     |  135 +++++++
 core/Clownfish/TestHarness/TestFormatter.c         |   37 +-
 core/Clownfish/TestHarness/TestFormatter.cfh       |   38 +-
 core/Clownfish/TestHarness/TestRunner.c            |   93 -----
 core/Clownfish/TestHarness/TestRunner.cfh          |   77 ----
 core/Clownfish/TestHarness/TestSuite.c             |  109 +++++
 core/Clownfish/TestHarness/TestSuite.cfh           |   43 ++
 core/Clownfish/TestHarness/TestSuiteRunner.c       |   97 +++++
 core/Clownfish/TestHarness/TestSuiteRunner.cfh     |   76 ++++
 core/Lucy/Test.c                                   |  192 ++++------
 core/Lucy/Test.cfh                                 |   22 +-
 core/Lucy/Test/Analysis/TestAnalyzer.c             |   22 +-
 core/Lucy/Test/Analysis/TestAnalyzer.cfh           |    7 +-
 core/Lucy/Test/Analysis/TestCaseFolder.c           |   32 +-
 core/Lucy/Test/Analysis/TestCaseFolder.cfh         |    7 +-
 core/Lucy/Test/Analysis/TestNormalizer.c           |   34 +-
 core/Lucy/Test/Analysis/TestNormalizer.cfh         |    7 +-
 core/Lucy/Test/Analysis/TestPolyAnalyzer.c         |   53 ++--
 core/Lucy/Test/Analysis/TestPolyAnalyzer.cfh       |    7 +-
 core/Lucy/Test/Analysis/TestRegexTokenizer.c       |   33 +-
 core/Lucy/Test/Analysis/TestRegexTokenizer.cfh     |    7 +-
 core/Lucy/Test/Analysis/TestSnowballStemmer.c      |   32 +-
 core/Lucy/Test/Analysis/TestSnowballStemmer.cfh    |    7 +-
 core/Lucy/Test/Analysis/TestSnowballStopFilter.c   |   26 +-
 core/Lucy/Test/Analysis/TestSnowballStopFilter.cfh |    7 +-
 core/Lucy/Test/Analysis/TestStandardTokenizer.c    |   41 +--
 core/Lucy/Test/Analysis/TestStandardTokenizer.cfh  |    7 +-
 core/Lucy/Test/Highlight/TestHeatMap.c             |   50 +--
 core/Lucy/Test/Highlight/TestHeatMap.cfh           |    7 +-
 core/Lucy/Test/Highlight/TestHighlighter.c         |  114 +++---
 core/Lucy/Test/Highlight/TestHighlighter.cfh       |    7 +-
 core/Lucy/Test/Index/TestDocWriter.c               |   18 +-
 core/Lucy/Test/Index/TestDocWriter.cfh             |    7 +-
 core/Lucy/Test/Index/TestHighlightWriter.c         |   18 +-
 core/Lucy/Test/Index/TestHighlightWriter.cfh       |    7 +-
 core/Lucy/Test/Index/TestIndexManager.c            |   24 +-
 core/Lucy/Test/Index/TestIndexManager.cfh          |    7 +-
 core/Lucy/Test/Index/TestPolyReader.c              |   22 +-
 core/Lucy/Test/Index/TestPolyReader.cfh            |    7 +-
 core/Lucy/Test/Index/TestPostingListWriter.c       |   18 +-
 core/Lucy/Test/Index/TestPostingListWriter.cfh     |    7 +-
 core/Lucy/Test/Index/TestSegWriter.c               |   18 +-
 core/Lucy/Test/Index/TestSegWriter.cfh             |    7 +-
 core/Lucy/Test/Index/TestSegment.c                 |   82 ++--
 core/Lucy/Test/Index/TestSegment.cfh               |    7 +-
 core/Lucy/Test/Index/TestSnapshot.c                |   46 +--
 core/Lucy/Test/Index/TestSnapshot.cfh              |    7 +-
 core/Lucy/Test/Index/TestTermInfo.c                |   42 +--
 core/Lucy/Test/Index/TestTermInfo.cfh              |    7 +-
 core/Lucy/Test/Object/TestBitVector.c              |  140 ++++----
 core/Lucy/Test/Object/TestBitVector.cfh            |    7 +-
 core/Lucy/Test/Object/TestI32Array.c               |   28 +-
 core/Lucy/Test/Object/TestI32Array.cfh             |    7 +-
 core/Lucy/Test/Plan/TestBlobType.c                 |   24 +-
 core/Lucy/Test/Plan/TestBlobType.cfh               |    7 +-
 core/Lucy/Test/Plan/TestFieldMisc.c                |   46 +--
 core/Lucy/Test/Plan/TestFieldMisc.cfh              |    7 +-
 core/Lucy/Test/Plan/TestFieldType.c                |   42 +--
 core/Lucy/Test/Plan/TestFieldType.cfh              |    7 +-
 core/Lucy/Test/Plan/TestFullTextType.c             |   44 +--
 core/Lucy/Test/Plan/TestFullTextType.cfh           |    7 +-
 core/Lucy/Test/Plan/TestNumericType.c              |   44 +--
 core/Lucy/Test/Plan/TestNumericType.cfh            |    7 +-
 core/Lucy/Test/Search/TestLeafQuery.c              |   30 +-
 core/Lucy/Test/Search/TestLeafQuery.cfh            |    7 +-
 core/Lucy/Test/Search/TestMatchAllQuery.c          |   24 +-
 core/Lucy/Test/Search/TestMatchAllQuery.cfh        |    7 +-
 core/Lucy/Test/Search/TestNOTQuery.c               |   28 +-
 core/Lucy/Test/Search/TestNOTQuery.cfh             |    7 +-
 core/Lucy/Test/Search/TestNoMatchQuery.c           |   24 +-
 core/Lucy/Test/Search/TestNoMatchQuery.cfh         |    7 +-
 core/Lucy/Test/Search/TestPhraseQuery.c            |   22 +-
 core/Lucy/Test/Search/TestPhraseQuery.cfh          |    7 +-
 core/Lucy/Test/Search/TestPolyQuery.c              |   44 +--
 core/Lucy/Test/Search/TestPolyQuery.cfh            |   14 +-
 core/Lucy/Test/Search/TestQueryParserLogic.c       |   29 +-
 core/Lucy/Test/Search/TestQueryParserLogic.cfh     |    7 +-
 core/Lucy/Test/Search/TestQueryParserSyntax.c      |   41 +--
 core/Lucy/Test/Search/TestQueryParserSyntax.cfh    |    7 +-
 core/Lucy/Test/Search/TestRangeQuery.c             |   30 +-
 core/Lucy/Test/Search/TestRangeQuery.cfh           |    7 +-
 core/Lucy/Test/Search/TestReqOptQuery.c            |   28 +-
 core/Lucy/Test/Search/TestReqOptQuery.cfh          |    7 +-
 core/Lucy/Test/Search/TestSeriesMatcher.c          |   26 +-
 core/Lucy/Test/Search/TestSeriesMatcher.cfh        |    7 +-
 core/Lucy/Test/Search/TestSortSpec.c               |   60 ++--
 core/Lucy/Test/Search/TestSortSpec.cfh             |    7 +-
 core/Lucy/Test/Search/TestSpan.c                   |   32 +-
 core/Lucy/Test/Search/TestSpan.cfh                 |    7 +-
 core/Lucy/Test/Search/TestTermQuery.c              |   28 +-
 core/Lucy/Test/Search/TestTermQuery.cfh            |    7 +-
 core/Lucy/Test/Store/TestCompoundFileReader.c      |  140 ++++----
 core/Lucy/Test/Store/TestCompoundFileReader.cfh    |    7 +-
 core/Lucy/Test/Store/TestCompoundFileWriter.c      |   40 +--
 core/Lucy/Test/Store/TestCompoundFileWriter.cfh    |    7 +-
 core/Lucy/Test/Store/TestFSDirHandle.c             |   30 +-
 core/Lucy/Test/Store/TestFSDirHandle.cfh           |    7 +-
 core/Lucy/Test/Store/TestFSFileHandle.c            |  128 +++----
 core/Lucy/Test/Store/TestFSFileHandle.cfh          |    7 +-
 core/Lucy/Test/Store/TestFSFolder.c                |   70 ++--
 core/Lucy/Test/Store/TestFSFolder.cfh              |    7 +-
 core/Lucy/Test/Store/TestFileHandle.c              |   21 +-
 core/Lucy/Test/Store/TestFileHandle.cfh            |    7 +-
 core/Lucy/Test/Store/TestFolder.c                  |  222 +++++------
 core/Lucy/Test/Store/TestFolder.cfh                |    7 +-
 core/Lucy/Test/Store/TestFolderCommon.c            |  250 ++++++------
 core/Lucy/Test/Store/TestFolderCommon.cfh          |    2 +-
 core/Lucy/Test/Store/TestIOChunks.c                |   40 +--
 core/Lucy/Test/Store/TestIOChunks.cfh              |    7 +-
 core/Lucy/Test/Store/TestIOPrimitives.c            |   96 +++---
 core/Lucy/Test/Store/TestIOPrimitives.cfh          |    7 +-
 core/Lucy/Test/Store/TestInStream.c                |  106 +++---
 core/Lucy/Test/Store/TestInStream.cfh              |    7 +-
 core/Lucy/Test/Store/TestRAMDirHandle.c            |   32 +-
 core/Lucy/Test/Store/TestRAMDirHandle.cfh          |    7 +-
 core/Lucy/Test/Store/TestRAMFileHandle.c           |  100 +++---
 core/Lucy/Test/Store/TestRAMFileHandle.cfh         |    7 +-
 core/Lucy/Test/Store/TestRAMFolder.c               |  256 ++++++------
 core/Lucy/Test/Store/TestRAMFolder.cfh             |    7 +-
 core/Lucy/Test/TestSchema.c                        |   32 +-
 core/Lucy/Test/TestSchema.cfh                      |    7 +-
 core/Lucy/Test/TestUtils.c                         |   13 +-
 core/Lucy/Test/TestUtils.cfh                       |    2 +-
 core/Lucy/Test/Util/TestIndexFileNames.c           |   44 +--
 core/Lucy/Test/Util/TestIndexFileNames.cfh         |    7 +-
 core/Lucy/Test/Util/TestJson.c                     |  174 ++++-----
 core/Lucy/Test/Util/TestJson.cfh                   |    7 +-
 core/Lucy/Test/Util/TestMemoryPool.c               |   25 +-
 core/Lucy/Test/Util/TestMemoryPool.cfh             |    7 +-
 core/Lucy/Test/Util/TestPriorityQueue.c            |   66 ++--
 core/Lucy/Test/Util/TestPriorityQueue.cfh          |    7 +-
 perl/buildlib/Lucy/Build/Binding/Misc.pm           |    8 +-
 162 files changed, 3152 insertions(+), 3708 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/c/t/test_lucy.c
----------------------------------------------------------------------
diff --git a/c/t/test_lucy.c b/c/t/test_lucy.c
index 808d0f7..f0018b1 100644
--- a/c/t/test_lucy.c
+++ b/c/t/test_lucy.c
@@ -17,22 +17,30 @@
 #include <stdlib.h>
 
 #include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestSuite.h"
 #include "Clownfish/Test.h"
 #include "Lucy/Test.h"
 
 int
 main() {
-    cfish_TestFormatterCF *formatter;
+    cfish_TestFormatter *formatter;
+    cfish_TestSuite     *cfish_suite;
+    cfish_TestSuite     *lucy_suite;
     bool success = true;
 
     testcfish_bootstrap_parcel();
     testlucy_bootstrap_parcel();
 
-    formatter = cfish_TestFormatterCF_new();
-    success &= testcfish_Test_run_all_batches((cfish_TestFormatter*)formatter);
-    success &= testlucy_Test_run_all_batches((cfish_TestFormatter*)formatter);
-    CFISH_DECREF(formatter);
+    formatter   = (cfish_TestFormatter*)cfish_TestFormatterCF_new();
+    cfish_suite = testcfish_Test_create_test_suite();
+    lucy_suite  = testlucy_Test_create_test_suite();
+
+    success &= Cfish_TestSuite_Run_All_Batches(cfish_suite, formatter);
+    success &= Cfish_TestSuite_Run_All_Batches(lucy_suite, formatter);
 
+    CFISH_DECREF(formatter);
+    CFISH_DECREF(cfish_suite);
+    CFISH_DECREF(lucy_suite);
     return success ? EXIT_SUCCESS : EXIT_FAILURE;
 }
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test.c b/core/Clownfish/Test.c
index 4d2a9fc..546af45 100644
--- a/core/Clownfish/Test.c
+++ b/core/Clownfish/Test.c
@@ -14,19 +14,13 @@
  * limitations under the License.
  */
 
-#include <stdio.h>
-
-#define CHY_USE_SHORT_NAMES
 #define CFISH_USE_SHORT_NAMES
 #define TESTCFISH_USE_SHORT_NAMES
 
 #include "Clownfish/Test.h"
 
-#include "Clownfish/Err.h"
 #include "Clownfish/TestHarness/TestBatch.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
-#include "Clownfish/TestHarness/TestRunner.h"
-#include "Clownfish/VArray.h"
+#include "Clownfish/TestHarness/TestSuite.h"
 
 #include "Clownfish/Test/TestByteBuf.h"
 #include "Clownfish/Test/TestCharBuf.h"
@@ -41,77 +35,24 @@
 #include "Clownfish/Test/Util/TestNumberUtils.h"
 #include "Clownfish/Test/Util/TestStringHelper.h"
 
-static void
-S_unbuffer_stdout();
-
-static VArray*
-S_all_test_batches(TestFormatter *formatter) {
-    VArray *batches = VA_new(0);
-
-    VA_Push(batches, (Obj*)TestVArray_new(formatter));
-    VA_Push(batches, (Obj*)TestHash_new(formatter));
-    VA_Push(batches, (Obj*)TestObj_new(formatter));
-    VA_Push(batches, (Obj*)TestErr_new(formatter));
-    VA_Push(batches, (Obj*)TestBB_new(formatter));
-    VA_Push(batches, (Obj*)TestCB_new(formatter));
-    VA_Push(batches, (Obj*)TestNumUtil_new(formatter));
-    VA_Push(batches, (Obj*)TestNum_new(formatter));
-    VA_Push(batches, (Obj*)TestStrHelp_new(formatter));
-    VA_Push(batches, (Obj*)TestAtomic_new(formatter));
-    VA_Push(batches, (Obj*)TestLFReg_new(formatter));
-    VA_Push(batches, (Obj*)TestMemory_new(formatter));
-
-    return batches;
-}
-
-bool
-Test_run_batch(CharBuf *class_name, TestFormatter *formatter) {
-    S_unbuffer_stdout();
-
-    VArray   *batches = S_all_test_batches(formatter);
-    uint32_t  size    = VA_Get_Size(batches);
-
-    for (uint32_t i = 0; i < size; ++i) {
-        TestBatch *batch = (TestBatch*)VA_Fetch(batches, i);
-
-        if (CB_Equals(TestBatch_Get_Class_Name(batch), (Obj*)class_name)) {
-            bool result = TestBatch_Run(batch);
-            DECREF(batches);
-            return result;
-        }
-    }
-
-    DECREF(batches);
-    THROW(ERR, "Couldn't find test class '%o'", class_name);
-    UNREACHABLE_RETURN(bool);
-}
-
-bool
-Test_run_all_batches(TestFormatter *formatter) {
-    S_unbuffer_stdout();
-
-    TestRunner *runner  = TestRunner_new(formatter);
-    VArray     *batches = S_all_test_batches(formatter);
-    uint32_t    size    = VA_Get_Size(batches);
-
-    for (uint32_t i = 0; i < size; ++i) {
-        TestBatch *batch = (TestBatch*)VA_Fetch(batches, i);
-        TestRunner_Run_Batch(runner, batch);
-    }
-
-    bool result = TestRunner_Finish(runner);
-
-    DECREF(runner);
-    DECREF(batches);
-    return result;
-}
-
-static void
-S_unbuffer_stdout() {
-    int check_val = setvbuf(stdout, NULL, _IONBF, 0);
-    if (check_val != 0) {
-        fprintf(stderr, "Failed when trying to unbuffer stdout\n");
-    }
+TestSuite*
+Test_create_test_suite() {
+    TestSuite *suite = TestSuite_new();
+
+    TestSuite_Add_Batch(suite, (TestBatch*)TestVArray_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestHash_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestObj_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestErr_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestBB_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestCB_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestNumUtil_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestNum_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestStrHelp_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestAtomic_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestLFReg_new());
+    TestSuite_Add_Batch(suite, (TestBatch*)TestMemory_new());
+
+    return suite;
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test.cfh b/core/Clownfish/Test.cfh
index 4ceb429..68dc8bc 100644
--- a/core/Clownfish/Test.cfh
+++ b/core/Clownfish/Test.cfh
@@ -16,27 +16,11 @@
 
 parcel TestClownfish;
 
-/** Testing framework.
+/** Clownfish test suite.
  */
 inert class Clownfish::Test {
-    inert bool
-    run_batch(CharBuf *class_name, TestFormatter *formatter);
-
-    inert bool
-    run_all_batches(TestFormatter *formatter);
+    inert incremented TestSuite*
+    create_test_suite();
 }
 
-__C__
-#ifdef TESTCFISH_USE_SHORT_NAMES
-  #define TEST_TRUE                    cfish_TestBatch_test_true
-  #define TEST_FALSE                   cfish_TestBatch_test_false
-  #define TEST_INT_EQ                  cfish_TestBatch_test_int_equals
-  #define TEST_FLOAT_EQ                cfish_TestBatch_test_float_equals
-  #define TEST_STR_EQ                  cfish_TestBatch_test_string_equals
-  #define PASS                         cfish_TestBatch_pass
-  #define FAIL                         cfish_TestBatch_fail
-  #define SKIP                         cfish_TestBatch_skip
-#endif
-__END_C__
-
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestByteBuf.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestByteBuf.c b/core/Clownfish/Test/TestByteBuf.c
index 98da9c3..1fe8a2b 100644
--- a/core/Clownfish/Test/TestByteBuf.c
+++ b/core/Clownfish/Test/TestByteBuf.c
@@ -23,45 +23,39 @@
 
 #include "Clownfish/ByteBuf.h"
 #include "Clownfish/Test.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
 #include "Clownfish/TestHarness/TestUtils.h"
 #include "Clownfish/VTable.h"
 
 TestByteBuf*
-TestBB_new(TestFormatter *formatter) {
-    TestByteBuf *self = (TestByteBuf*)VTable_Make_Obj(TESTBYTEBUF);
-    return TestBB_init(self, formatter);
-}
-
-TestByteBuf*
-TestBB_init(TestByteBuf *self, TestFormatter *formatter) {
-    return (TestByteBuf*)TestBatch_init((TestBatch*)self, 22, formatter);
+TestBB_new() {
+    return (TestByteBuf*)VTable_Make_Obj(TESTBYTEBUF);
 }
 
 static void
-test_Equals(TestBatch *batch) {
+test_Equals(TestBatchRunner *runner) {
     ByteBuf *wanted = BB_new_bytes("foo", 4); // Include terminating NULL.
     ByteBuf *got    = BB_new_bytes("foo", 4);
 
-    TEST_TRUE(batch, BB_Equals(wanted, (Obj*)got), "Equals");
-    TEST_INT_EQ(batch, BB_Hash_Sum(got), BB_Hash_Sum(wanted), "Hash_Sum");
+    TEST_TRUE(runner, BB_Equals(wanted, (Obj*)got), "Equals");
+    TEST_INT_EQ(runner, BB_Hash_Sum(got), BB_Hash_Sum(wanted), "Hash_Sum");
 
-    TEST_TRUE(batch, BB_Equals_Bytes(got, "foo", 4), "Equals_Bytes");
-    TEST_FALSE(batch, BB_Equals_Bytes(got, "foo", 3),
+    TEST_TRUE(runner, BB_Equals_Bytes(got, "foo", 4), "Equals_Bytes");
+    TEST_FALSE(runner, BB_Equals_Bytes(got, "foo", 3),
                "Equals_Bytes spoiled by different size");
-    TEST_FALSE(batch, BB_Equals_Bytes(got, "bar", 4),
+    TEST_FALSE(runner, BB_Equals_Bytes(got, "bar", 4),
                "Equals_Bytes spoiled by different content");
 
     BB_Set_Size(got, 3);
-    TEST_FALSE(batch, BB_Equals(wanted, (Obj*)got),
+    TEST_FALSE(runner, BB_Equals(wanted, (Obj*)got),
                "Different size spoils Equals");
-    TEST_FALSE(batch, BB_Hash_Sum(got) == BB_Hash_Sum(wanted),
+    TEST_FALSE(runner, BB_Hash_Sum(got) == BB_Hash_Sum(wanted),
                "Different size spoils Hash_Sum (probably -- at least this one)");
 
     BB_Mimic_Bytes(got, "bar", 4);
-    TEST_INT_EQ(batch, BB_Get_Size(wanted), BB_Get_Size(got),
+    TEST_INT_EQ(runner, BB_Get_Size(wanted), BB_Get_Size(got),
                 "same length");
-    TEST_FALSE(batch, BB_Equals(wanted, (Obj*)got),
+    TEST_FALSE(runner, BB_Equals(wanted, (Obj*)got),
                "Different content spoils Equals");
 
     DECREF(got);
@@ -69,41 +63,41 @@ test_Equals(TestBatch *batch) {
 }
 
 static void
-test_Grow(TestBatch *batch) {
+test_Grow(TestBatchRunner *runner) {
     ByteBuf *bb = BB_new(1);
-    TEST_INT_EQ(batch, BB_Get_Capacity(bb), 8,
+    TEST_INT_EQ(runner, BB_Get_Capacity(bb), 8,
                 "Allocate in 8-byte increments");
     BB_Grow(bb, 9);
-    TEST_INT_EQ(batch, BB_Get_Capacity(bb), 16,
+    TEST_INT_EQ(runner, BB_Get_Capacity(bb), 16,
                 "Grow in 8-byte increments");
     DECREF(bb);
 }
 
 static void
-test_Clone(TestBatch *batch) {
+test_Clone(TestBatchRunner *runner) {
     ByteBuf *bb = BB_new_bytes("foo", 3);
     ByteBuf *twin = BB_Clone(bb);
-    TEST_TRUE(batch, BB_Equals(bb, (Obj*)twin), "Clone");
+    TEST_TRUE(runner, BB_Equals(bb, (Obj*)twin), "Clone");
     DECREF(bb);
     DECREF(twin);
 }
 
 static void
-test_compare(TestBatch *batch) {
+test_compare(TestBatchRunner *runner) {
     ByteBuf *a = BB_new_bytes("foo\0a", 5);
     ByteBuf *b = BB_new_bytes("foo\0b", 5);
 
     BB_Set_Size(a, 4);
     BB_Set_Size(b, 4);
-    TEST_INT_EQ(batch, BB_compare(&a, &b), 0,
+    TEST_INT_EQ(runner, BB_compare(&a, &b), 0,
                 "BB_compare returns 0 for equal ByteBufs");
 
     BB_Set_Size(a, 3);
-    TEST_TRUE(batch, BB_compare(&a, &b) < 0, "shorter ByteBuf sorts first");
+    TEST_TRUE(runner, BB_compare(&a, &b) < 0, "shorter ByteBuf sorts first");
 
     BB_Set_Size(a, 5);
     BB_Set_Size(b, 5);
-    TEST_TRUE(batch, BB_compare(&a, &b) < 0,
+    TEST_TRUE(runner, BB_compare(&a, &b) < 0,
               "NULL doesn't interfere with BB_compare");
 
     DECREF(a);
@@ -111,37 +105,37 @@ test_compare(TestBatch *batch) {
 }
 
 static void
-test_Mimic(TestBatch *batch) {
+test_Mimic(TestBatchRunner *runner) {
     ByteBuf *a = BB_new_bytes("foo", 3);
     ByteBuf *b = BB_new(0);
 
     BB_Mimic(b, (Obj*)a);
-    TEST_TRUE(batch, BB_Equals(a, (Obj*)b), "Mimic");
+    TEST_TRUE(runner, BB_Equals(a, (Obj*)b), "Mimic");
 
     BB_Mimic_Bytes(a, "bar", 4);
-    TEST_TRUE(batch, strcmp(BB_Get_Buf(a), "bar") == 0,
+    TEST_TRUE(runner, strcmp(BB_Get_Buf(a), "bar") == 0,
               "Mimic_Bytes content");
-    TEST_INT_EQ(batch, BB_Get_Size(a), 4, "Mimic_Bytes size");
+    TEST_INT_EQ(runner, BB_Get_Size(a), 4, "Mimic_Bytes size");
 
     BB_Mimic(b, (Obj*)a);
-    TEST_TRUE(batch, BB_Equals(a, (Obj*)b), "Mimic");
+    TEST_TRUE(runner, BB_Equals(a, (Obj*)b), "Mimic");
 
     DECREF(a);
     DECREF(b);
 }
 
 static void
-test_Cat(TestBatch *batch) {
+test_Cat(TestBatchRunner *runner) {
     ByteBuf *wanted  = BB_new_bytes("foobar", 6);
     ByteBuf *got     = BB_new_bytes("foo", 3);
     ByteBuf *scratch = BB_new_bytes("bar", 3);
 
     BB_Cat(got, scratch);
-    TEST_TRUE(batch, BB_Equals(wanted, (Obj*)got), "Cat");
+    TEST_TRUE(runner, BB_Equals(wanted, (Obj*)got), "Cat");
 
     BB_Mimic_Bytes(wanted, "foobarbaz", 9);
     BB_Cat_Bytes(got, "baz", 3);
-    TEST_TRUE(batch, BB_Equals(wanted, (Obj*)got), "Cat_Bytes");
+    TEST_TRUE(runner, BB_Equals(wanted, (Obj*)got), "Cat_Bytes");
 
     DECREF(scratch);
     DECREF(got);
@@ -149,25 +143,25 @@ test_Cat(TestBatch *batch) {
 }
 
 static void
-test_serialization(TestBatch *batch) {
+test_serialization(TestBatchRunner *runner) {
     ByteBuf *wanted = BB_new_bytes("foobar", 6);
     ByteBuf *got    = (ByteBuf*)TestUtils_freeze_thaw((Obj*)wanted);
-    TEST_TRUE(batch, got && BB_Equals(wanted, (Obj*)got),
+    TEST_TRUE(runner, got && BB_Equals(wanted, (Obj*)got),
               "Serialization round trip");
     DECREF(wanted);
     DECREF(got);
 }
 
 void
-TestBB_run_tests(TestByteBuf *self) {
-    TestBatch *batch = (TestBatch*)self;
-    test_Equals(batch);
-    test_Grow(batch);
-    test_Clone(batch);
-    test_compare(batch);
-    test_Mimic(batch);
-    test_Cat(batch);
-    test_serialization(batch);
+TestBB_run(TestByteBuf *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 22);
+    test_Equals(runner);
+    test_Grow(runner);
+    test_Clone(runner);
+    test_compare(runner);
+    test_Mimic(runner);
+    test_Cat(runner);
+    test_serialization(runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestByteBuf.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestByteBuf.cfh b/core/Clownfish/Test/TestByteBuf.cfh
index 6819191..3335c95 100644
--- a/core/Clownfish/Test/TestByteBuf.cfh
+++ b/core/Clownfish/Test/TestByteBuf.cfh
@@ -20,13 +20,10 @@ class Clownfish::Test::TestByteBuf cnick TestBB
     inherits Clownfish::TestHarness::TestBatch {
 
     inert incremented TestByteBuf*
-    new(TestFormatter *formatter);
-
-    inert TestByteBuf*
-    init(TestByteBuf *self, TestFormatter *formatter);
+    new();
 
     void
-    Run_Tests(TestByteBuf *self);
+    Run(TestByteBuf *self, TestBatchRunner *runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestCharBuf.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestCharBuf.c b/core/Clownfish/Test/TestCharBuf.c
index bc8a348..a75ce65 100644
--- a/core/Clownfish/Test/TestCharBuf.c
+++ b/core/Clownfish/Test/TestCharBuf.c
@@ -26,7 +26,7 @@
 #include "Clownfish/CharBuf.h"
 #include "Clownfish/Num.h"
 #include "Clownfish/Test.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
 #include "Clownfish/TestHarness/TestUtils.h"
 #include "Clownfish/VTable.h"
 
@@ -34,14 +34,8 @@ static char smiley[] = { (char)0xE2, (char)0x98, (char)0xBA, 0 };
 static uint32_t smiley_len = 3;
 
 TestCharBuf*
-TestCB_new(TestFormatter *formatter) {
-    TestCharBuf *self = (TestCharBuf*)VTable_Make_Obj(TESTCHARBUF);
-    return TestCB_init(self, formatter);
-}
-
-TestCharBuf*
-TestCB_init(TestCharBuf *self, TestFormatter *formatter) {
-    return (TestCharBuf*)TestBatch_init((TestBatch*)self, 55, formatter);
+TestCB_new() {
+    return (TestCharBuf*)VTable_Make_Obj(TESTCHARBUF);
 }
 
 static CharBuf*
@@ -50,74 +44,74 @@ S_get_cb(const char *string) {
 }
 
 static void
-test_Cat(TestBatch *batch) {
+test_Cat(TestBatchRunner *runner) {
     CharBuf *wanted = CB_newf("a%s", smiley);
     CharBuf *got    = S_get_cb("");
 
     CB_Cat(got, wanted);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Cat");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat");
     DECREF(got);
 
     got = S_get_cb("a");
     CB_Cat_Char(got, 0x263A);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Cat_Char");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat_Char");
     DECREF(got);
 
     got = S_get_cb("a");
     CB_Cat_Str(got, smiley, smiley_len);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Cat_Str");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat_Str");
     DECREF(got);
 
     got = S_get_cb("a");
     CB_Cat_Trusted_Str(got, smiley, smiley_len);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Cat_Trusted_Str");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Cat_Trusted_Str");
     DECREF(got);
 
     DECREF(wanted);
 }
 
 static void
-test_Mimic_and_Clone(TestBatch *batch) {
+test_Mimic_and_Clone(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo");
     CharBuf *got    = S_get_cb("bar");
 
     CB_Mimic(got, (Obj*)wanted);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Mimic");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Mimic");
     DECREF(got);
 
     got = S_get_cb("bar");
     CB_Mimic_Str(got, "foo", 3);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Mimic_Str");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Mimic_Str");
     DECREF(got);
 
     got = CB_Clone(wanted);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Clone");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Clone");
     DECREF(got);
 
     DECREF(wanted);
 }
 
 static void
-test_Find(TestBatch *batch) {
+test_Find(TestBatchRunner *runner) {
     CharBuf *string = CB_new(10);
     CharBuf *substring = S_get_cb("foo");
 
-    TEST_TRUE(batch, CB_Find(string, substring) == -1, "Not in empty string");
+    TEST_TRUE(runner, CB_Find(string, substring) == -1, "Not in empty string");
     CB_setf(string, "foo");
-    TEST_TRUE(batch, CB_Find(string, substring) == 0, "Find complete string");
+    TEST_TRUE(runner, CB_Find(string, substring) == 0, "Find complete string");
     CB_setf(string, "afoo");
-    TEST_TRUE(batch, CB_Find(string, substring) == 1, "Find after first");
+    TEST_TRUE(runner, CB_Find(string, substring) == 1, "Find after first");
     CB_Set_Size(string, 3);
-    TEST_TRUE(batch, CB_Find(string, substring) == -1, "Don't overrun");
+    TEST_TRUE(runner, CB_Find(string, substring) == -1, "Don't overrun");
     CB_setf(string, "afood");
-    TEST_TRUE(batch, CB_Find(string, substring) == 1, "Find in middle");
+    TEST_TRUE(runner, CB_Find(string, substring) == 1, "Find in middle");
 
     DECREF(substring);
     DECREF(string);
 }
 
 static void
-test_Code_Point_At_and_From(TestBatch *batch) {
+test_Code_Point_At_and_From(TestBatchRunner *runner) {
     uint32_t code_points[] = { 'a', 0x263A, 0x263A, 'b', 0x263A, 'c' };
     uint32_t num_code_points = sizeof(code_points) / sizeof(uint32_t);
     CharBuf *string = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
@@ -125,9 +119,9 @@ test_Code_Point_At_and_From(TestBatch *batch) {
 
     for (i = 0; i < num_code_points; i++) {
         uint32_t from = num_code_points - i - 1;
-        TEST_INT_EQ(batch, CB_Code_Point_At(string, i), code_points[i],
+        TEST_INT_EQ(runner, CB_Code_Point_At(string, i), code_points[i],
                     "Code_Point_At %ld", (long)i);
-        TEST_INT_EQ(batch, CB_Code_Point_At(string, from),
+        TEST_INT_EQ(runner, CB_Code_Point_At(string, from),
                     code_points[from], "Code_Point_From %ld", (long)from);
     }
 
@@ -135,49 +129,49 @@ test_Code_Point_At_and_From(TestBatch *batch) {
 }
 
 static void
-test_SubString(TestBatch *batch) {
+test_SubString(TestBatchRunner *runner) {
     CharBuf *string = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
     CharBuf *wanted = CB_newf("%sb%s", smiley, smiley);
     CharBuf *got = CB_SubString(string, 2, 3);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "SubString");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "SubString");
     DECREF(wanted);
     DECREF(got);
     DECREF(string);
 }
 
 static void
-test_Nip_and_Chop(TestBatch *batch) {
+test_Nip_and_Chop(TestBatchRunner *runner) {
     CharBuf *wanted;
     CharBuf *got;
 
     wanted = CB_newf("%sb%sc", smiley, smiley);
     got    = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
     CB_Nip(got, 2);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Nip");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Nip");
     DECREF(wanted);
     DECREF(got);
 
     wanted = CB_newf("a%s%s", smiley, smiley);
     got    = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
     CB_Chop(got, 3);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Chop");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Chop");
     DECREF(wanted);
     DECREF(got);
 }
 
 
 static void
-test_Truncate(TestBatch *batch) {
+test_Truncate(TestBatchRunner *runner) {
     CharBuf *wanted = CB_newf("a%s", smiley, smiley);
     CharBuf *got    = CB_newf("a%s%sb%sc", smiley, smiley, smiley);
     CB_Truncate(got, 2);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Truncate");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "Truncate");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_Trim(TestBatch *batch) {
+test_Trim(TestBatchRunner *runner) {
     uint32_t spaces[] = {
         ' ',    '\t',   '\r',   '\n',   0x000B, 0x000C, 0x000D, 0x0085,
         0x00A0, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004,
@@ -193,13 +187,13 @@ test_Trim(TestBatch *batch) {
     CB_Cat_Char(got, 0x263A);
     for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
 
-    TEST_TRUE(batch, CB_Trim_Top(got), "Trim_Top returns true on success");
-    TEST_FALSE(batch, CB_Trim_Top(got),
+    TEST_TRUE(runner, CB_Trim_Top(got), "Trim_Top returns true on success");
+    TEST_FALSE(runner, CB_Trim_Top(got),
                "Trim_Top returns false on failure");
-    TEST_TRUE(batch, CB_Trim_Tail(got), "Trim_Tail returns true on success");
-    TEST_FALSE(batch, CB_Trim_Tail(got),
+    TEST_TRUE(runner, CB_Trim_Tail(got), "Trim_Tail returns true on success");
+    TEST_FALSE(runner, CB_Trim_Tail(got),
                "Trim_Tail returns false on failure");
-    TEST_TRUE(batch, CB_Equals_Str(got, smiley, smiley_len),
+    TEST_TRUE(runner, CB_Equals_Str(got, smiley, smiley_len),
               "Trim_Top and Trim_Tail worked");
 
     // Build the spacey smiley again.
@@ -208,168 +202,168 @@ test_Trim(TestBatch *batch) {
     CB_Cat_Char(got, 0x263A);
     for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
 
-    TEST_TRUE(batch, CB_Trim(got), "Trim returns true on success");
-    TEST_FALSE(batch, CB_Trim(got), "Trim returns false on failure");
-    TEST_TRUE(batch, CB_Equals_Str(got, smiley, smiley_len),
+    TEST_TRUE(runner, CB_Trim(got), "Trim returns true on success");
+    TEST_FALSE(runner, CB_Trim(got), "Trim returns false on failure");
+    TEST_TRUE(runner, CB_Equals_Str(got, smiley, smiley_len),
               "Trim worked");
 
     DECREF(got);
 }
 
 static void
-test_To_F64(TestBatch *batch) {
+test_To_F64(TestBatchRunner *runner) {
     CharBuf *charbuf = S_get_cb("1.5");
     double difference = 1.5 - CB_To_F64(charbuf);
     if (difference < 0) { difference = 0 - difference; }
-    TEST_TRUE(batch, difference < 0.001, "To_F64");
+    TEST_TRUE(runner, difference < 0.001, "To_F64");
 
     CB_setf(charbuf, "-1.5");
     difference = 1.5 + CB_To_F64(charbuf);
     if (difference < 0) { difference = 0 - difference; }
-    TEST_TRUE(batch, difference < 0.001, "To_F64 negative");
+    TEST_TRUE(runner, difference < 0.001, "To_F64 negative");
 
     CB_setf(charbuf, "1.59");
     double value_full = CB_To_F64(charbuf);
     CB_Set_Size(charbuf, 3);
     double value_short = CB_To_F64(charbuf);
-    TEST_TRUE(batch, value_short < value_full,
+    TEST_TRUE(runner, value_short < value_full,
               "TO_F64 doesn't run past end of string");
 
     DECREF(charbuf);
 }
 
 static void
-test_To_I64(TestBatch *batch) {
+test_To_I64(TestBatchRunner *runner) {
     CharBuf *charbuf = S_get_cb("10");
-    TEST_TRUE(batch, CB_To_I64(charbuf) == 10, "To_I64");
+    TEST_TRUE(runner, CB_To_I64(charbuf) == 10, "To_I64");
     CB_setf(charbuf, "-10");
-    TEST_TRUE(batch, CB_To_I64(charbuf) == -10, "To_I64 negative");
+    TEST_TRUE(runner, CB_To_I64(charbuf) == -10, "To_I64 negative");
     DECREF(charbuf);
 }
 
 
 static void
-test_vcatf_s(TestBatch *batch) {
+test_vcatf_s(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar bizzle baz");
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %s baz", "bizzle");
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%s");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%s");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_null_string(TestBatch *batch) {
+test_vcatf_null_string(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar [NULL] baz");
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %s baz", NULL);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%s NULL");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%s NULL");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_cb(TestBatch *batch) {
+test_vcatf_cb(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar ZEKE baz");
     CharBuf *catworthy = S_get_cb("ZEKE");
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %o baz", catworthy);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%o CharBuf");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%o CharBuf");
     DECREF(catworthy);
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_obj(TestBatch *batch) {
+test_vcatf_obj(TestBatchRunner *runner) {
     CharBuf   *wanted = S_get_cb("ooga 20 booga");
     Integer32 *i32 = Int32_new(20);
     CharBuf   *got = S_get_cb("ooga");
     CB_catf(got, " %o booga", i32);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%o Obj");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%o Obj");
     DECREF(i32);
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_null_obj(TestBatch *batch) {
+test_vcatf_null_obj(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar [NULL] baz");
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %o baz", NULL);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%o NULL");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%o NULL");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_i8(TestBatch *batch) {
+test_vcatf_i8(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar -3 baz");
     int8_t num = -3;
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %i8 baz", num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%i8");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%i8");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_i32(TestBatch *batch) {
+test_vcatf_i32(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar -100000 baz");
     int32_t num = -100000;
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %i32 baz", num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%i32");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%i32");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_i64(TestBatch *batch) {
+test_vcatf_i64(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar -5000000000 baz");
     int64_t num = INT64_C(-5000000000);
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %i64 baz", num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%i64");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%i64");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_u8(TestBatch *batch) {
+test_vcatf_u8(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar 3 baz");
     uint8_t num = 3;
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %u8 baz", num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%u8");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%u8");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_u32(TestBatch *batch) {
+test_vcatf_u32(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar 100000 baz");
     uint32_t num = 100000;
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %u32 baz", num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%u32");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%u32");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_u64(TestBatch *batch) {
+test_vcatf_u64(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo bar 5000000000 baz");
     uint64_t num = UINT64_C(5000000000);
     CharBuf *got = S_get_cb("foo ");
     CB_catf(got, "bar %u64 baz", num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%u64");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%u64");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_f64(TestBatch *batch) {
+test_vcatf_f64(TestBatchRunner *runner) {
     CharBuf *wanted;
     char buf[64];
     float num = 1.3f;
@@ -377,13 +371,13 @@ test_vcatf_f64(TestBatch *batch) {
     sprintf(buf, "foo bar %g baz", num);
     wanted = CB_new_from_trusted_utf8(buf, strlen(buf));
     CB_catf(got, "bar %f64 baz", num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%f64");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%f64");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_vcatf_x32(TestBatch *batch) {
+test_vcatf_x32(TestBatchRunner *runner) {
     CharBuf *wanted;
     char buf[64];
     unsigned long num = INT32_MAX;
@@ -395,48 +389,48 @@ test_vcatf_x32(TestBatch *batch) {
 #endif
     wanted = CB_new_from_trusted_utf8(buf, strlen(buf));
     CB_catf(got, "bar %x32 baz", (uint32_t)num);
-    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "%%x32");
+    TEST_TRUE(runner, CB_Equals(wanted, (Obj*)got), "%%x32");
     DECREF(wanted);
     DECREF(got);
 }
 
 static void
-test_serialization(TestBatch *batch) {
+test_serialization(TestBatchRunner *runner) {
     CharBuf *wanted = S_get_cb("foo");
     CharBuf *got    = (CharBuf*)TestUtils_freeze_thaw((Obj*)wanted);
-    TEST_TRUE(batch, got && CB_Equals(wanted, (Obj*)got),
+    TEST_TRUE(runner, got && CB_Equals(wanted, (Obj*)got),
               "Round trip through FREEZE/THAW");
     DECREF(got);
     DECREF(wanted);
 }
 
 void
-TestCB_run_tests(TestCharBuf *self) {
-    TestBatch *batch = (TestBatch*)self;
-    test_vcatf_s(batch);
-    test_vcatf_null_string(batch);
-    test_vcatf_cb(batch);
-    test_vcatf_obj(batch);
-    test_vcatf_null_obj(batch);
-    test_vcatf_i8(batch);
-    test_vcatf_i32(batch);
-    test_vcatf_i64(batch);
-    test_vcatf_u8(batch);
-    test_vcatf_u32(batch);
-    test_vcatf_u64(batch);
-    test_vcatf_f64(batch);
-    test_vcatf_x32(batch);
-    test_Cat(batch);
-    test_Mimic_and_Clone(batch);
-    test_Code_Point_At_and_From(batch);
-    test_Find(batch);
-    test_SubString(batch);
-    test_Nip_and_Chop(batch);
-    test_Truncate(batch);
-    test_Trim(batch);
-    test_To_F64(batch);
-    test_To_I64(batch);
-    test_serialization(batch);
+TestCB_run(TestCharBuf *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 55);
+    test_vcatf_s(runner);
+    test_vcatf_null_string(runner);
+    test_vcatf_cb(runner);
+    test_vcatf_obj(runner);
+    test_vcatf_null_obj(runner);
+    test_vcatf_i8(runner);
+    test_vcatf_i32(runner);
+    test_vcatf_i64(runner);
+    test_vcatf_u8(runner);
+    test_vcatf_u32(runner);
+    test_vcatf_u64(runner);
+    test_vcatf_f64(runner);
+    test_vcatf_x32(runner);
+    test_Cat(runner);
+    test_Mimic_and_Clone(runner);
+    test_Code_Point_At_and_From(runner);
+    test_Find(runner);
+    test_SubString(runner);
+    test_Nip_and_Chop(runner);
+    test_Truncate(runner);
+    test_Trim(runner);
+    test_To_F64(runner);
+    test_To_I64(runner);
+    test_serialization(runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestCharBuf.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestCharBuf.cfh b/core/Clownfish/Test/TestCharBuf.cfh
index ba27bdb..3cc5d43 100644
--- a/core/Clownfish/Test/TestCharBuf.cfh
+++ b/core/Clownfish/Test/TestCharBuf.cfh
@@ -20,13 +20,10 @@ class Clownfish::Test::TestCharBuf cnick TestCB
     inherits Clownfish::TestHarness::TestBatch {
 
     inert incremented TestCharBuf*
-    new(TestFormatter *formatter);
-
-    inert TestCharBuf*
-    init(TestCharBuf *self, TestFormatter *formatter);
+    new();
 
     void
-    Run_Tests(TestCharBuf *self);
+    Run(TestCharBuf *self, TestBatchRunner *runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestErr.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestErr.c b/core/Clownfish/Test/TestErr.c
index 7839de5..ddf0457 100644
--- a/core/Clownfish/Test/TestErr.c
+++ b/core/Clownfish/Test/TestErr.c
@@ -22,35 +22,29 @@
 #include "Clownfish/CharBuf.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/Test.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
 #include "Clownfish/VTable.h"
 
 TestErr*
-TestErr_new(TestFormatter *formatter) {
-    TestErr *self = (TestErr*)VTable_Make_Obj(TESTERR);
-    return TestErr_init(self, formatter);
-}
-
-TestErr*
-TestErr_init(TestErr *self, TestFormatter *formatter) {
-    return (TestErr*)TestBatch_init((TestBatch*)self, 1, formatter);
+TestErr_new() {
+    return (TestErr*)VTable_Make_Obj(TESTERR);
 }
 
 static void
-test_To_String(TestBatch *batch) {
+test_To_String(TestBatchRunner *runner) {
     CharBuf *message = CB_newf("oops");
     Err *error = Err_new(message);
     CharBuf *string = Err_To_String(error);
-    TEST_TRUE(batch, CB_Equals(message, (Obj*)string),
+    TEST_TRUE(runner, CB_Equals(message, (Obj*)string),
               "Stringifies as message");
     DECREF(string);
     DECREF(error);
 }
 
 void
-TestErr_run_tests(TestErr *self) {
-    TestBatch *batch = (TestBatch*)self;
-    test_To_String(batch);
+TestErr_run(TestErr *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 1);
+    test_To_String(runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestErr.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestErr.cfh b/core/Clownfish/Test/TestErr.cfh
index 4efd467..178017c 100644
--- a/core/Clownfish/Test/TestErr.cfh
+++ b/core/Clownfish/Test/TestErr.cfh
@@ -20,12 +20,9 @@ class Clownfish::Test::TestErr
     inherits Clownfish::TestHarness::TestBatch {
 
     inert incremented TestErr*
-    new(TestFormatter *formatter);
-
-    inert TestErr*
-    init(TestErr *self, TestFormatter *formatter);
+    new();
 
     void
-    Run_Tests(TestErr *self);
+    Run(TestErr *self, TestBatchRunner *runner);
 }
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestHash.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestHash.c b/core/Clownfish/Test/TestHash.c
index 6b72ec7..c5e899a 100644
--- a/core/Clownfish/Test/TestHash.c
+++ b/core/Clownfish/Test/TestHash.c
@@ -26,41 +26,35 @@
 #include "Clownfish/Hash.h"
 #include "Clownfish/Num.h"
 #include "Clownfish/Test.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
 #include "Clownfish/TestHarness/TestUtils.h"
 #include "Clownfish/VArray.h"
 #include "Clownfish/VTable.h"
 
 TestHash*
-TestHash_new(TestFormatter *formatter) {
-    TestHash *self = (TestHash*)VTable_Make_Obj(TESTHASH);
-    return TestHash_init(self, formatter);
-}
-
-TestHash*
-TestHash_init(TestHash *self, TestFormatter *formatter) {
-    return (TestHash*)TestBatch_init((TestBatch*)self, 29, formatter);
+TestHash_new() {
+    return (TestHash*)VTable_Make_Obj(TESTHASH);
 }
 
 static void
-test_Equals(TestBatch *batch) {
+test_Equals(TestBatchRunner *runner) {
     Hash *hash  = Hash_new(0);
     Hash *other = Hash_new(0);
     ZombieCharBuf *stuff = ZCB_WRAP_STR("stuff", 5);
 
-    TEST_TRUE(batch, Hash_Equals(hash, (Obj*)other),
+    TEST_TRUE(runner, Hash_Equals(hash, (Obj*)other),
               "Empty hashes are equal");
 
     Hash_Store_Str(hash, "foo", 3, (Obj*)CFISH_TRUE);
-    TEST_FALSE(batch, Hash_Equals(hash, (Obj*)other),
+    TEST_FALSE(runner, Hash_Equals(hash, (Obj*)other),
                "Add one pair and Equals returns false");
 
     Hash_Store_Str(other, "foo", 3, (Obj*)CFISH_TRUE);
-    TEST_TRUE(batch, Hash_Equals(hash, (Obj*)other),
+    TEST_TRUE(runner, Hash_Equals(hash, (Obj*)other),
               "Add a matching pair and Equals returns true");
 
     Hash_Store_Str(other, "foo", 3, INCREF(stuff));
-    TEST_FALSE(batch, Hash_Equals(hash, (Obj*)other),
+    TEST_FALSE(runner, Hash_Equals(hash, (Obj*)other),
                "Non-matching value spoils Equals");
 
     DECREF(hash);
@@ -68,7 +62,7 @@ test_Equals(TestBatch *batch) {
 }
 
 static void
-test_Store_and_Fetch(TestBatch *batch) {
+test_Store_and_Fetch(TestBatchRunner *runner) {
     Hash          *hash         = Hash_new(100);
     Hash          *dupe         = Hash_new(100);
     const uint32_t starting_cap = Hash_Get_Capacity(hash);
@@ -84,9 +78,9 @@ test_Store_and_Fetch(TestBatch *batch) {
         Hash_Store(dupe, (Obj*)cb, INCREF(cb));
         VA_Push(expected, INCREF(cb));
     }
-    TEST_TRUE(batch, Hash_Equals(hash, (Obj*)dupe), "Equals");
+    TEST_TRUE(runner, Hash_Equals(hash, (Obj*)dupe), "Equals");
 
-    TEST_INT_EQ(batch, Hash_Get_Capacity(hash), starting_cap,
+    TEST_INT_EQ(runner, Hash_Get_Capacity(hash), starting_cap,
                 "Initial capacity sufficient (no rebuilds)");
 
     for (int32_t i = 0; i < 100; i++) {
@@ -95,37 +89,37 @@ test_Store_and_Fetch(TestBatch *batch) {
         VA_Push(got, (Obj*)INCREF(elem));
     }
 
-    TEST_TRUE(batch, VA_Equals(got, (Obj*)expected),
+    TEST_TRUE(runner, VA_Equals(got, (Obj*)expected),
               "basic Store and Fetch");
-    TEST_INT_EQ(batch, Hash_Get_Size(hash), 100,
+    TEST_INT_EQ(runner, Hash_Get_Size(hash), 100,
                 "size incremented properly by Hash_Store");
 
-    TEST_TRUE(batch, Hash_Fetch(hash, (Obj*)foo) == NULL,
+    TEST_TRUE(runner, Hash_Fetch(hash, (Obj*)foo) == NULL,
               "Fetch against non-existent key returns NULL");
 
     Hash_Store(hash, (Obj*)forty, INCREF(foo));
-    TEST_TRUE(batch, ZCB_Equals(foo, Hash_Fetch(hash, (Obj*)forty)),
+    TEST_TRUE(runner, ZCB_Equals(foo, Hash_Fetch(hash, (Obj*)forty)),
               "Hash_Store replaces existing value");
-    TEST_FALSE(batch, Hash_Equals(hash, (Obj*)dupe),
+    TEST_FALSE(runner, Hash_Equals(hash, (Obj*)dupe),
                "replacement value spoils equals");
-    TEST_INT_EQ(batch, Hash_Get_Size(hash), 100,
+    TEST_INT_EQ(runner, Hash_Get_Size(hash), 100,
                 "size unaffected after value replaced");
 
-    TEST_TRUE(batch, Hash_Delete(hash, (Obj*)forty) == (Obj*)foo,
+    TEST_TRUE(runner, Hash_Delete(hash, (Obj*)forty) == (Obj*)foo,
               "Delete returns value");
     DECREF(foo);
-    TEST_INT_EQ(batch, Hash_Get_Size(hash), 99,
+    TEST_INT_EQ(runner, Hash_Get_Size(hash), 99,
                 "size decremented by successful Delete");
-    TEST_TRUE(batch, Hash_Delete(hash, (Obj*)forty) == NULL,
+    TEST_TRUE(runner, Hash_Delete(hash, (Obj*)forty) == NULL,
               "Delete returns NULL when key not found");
-    TEST_INT_EQ(batch, Hash_Get_Size(hash), 99,
+    TEST_INT_EQ(runner, Hash_Get_Size(hash), 99,
                 "size not decremented by unsuccessful Delete");
     DECREF(Hash_Delete(dupe, (Obj*)forty));
-    TEST_TRUE(batch, VA_Equals(got, (Obj*)expected), "Equals after Delete");
+    TEST_TRUE(runner, VA_Equals(got, (Obj*)expected), "Equals after Delete");
 
     Hash_Clear(hash);
-    TEST_TRUE(batch, Hash_Fetch(hash, (Obj*)twenty) == NULL, "Clear");
-    TEST_TRUE(batch, Hash_Get_Size(hash) == 0, "size is 0 after Clear");
+    TEST_TRUE(runner, Hash_Fetch(hash, (Obj*)twenty) == NULL, "Clear");
+    TEST_TRUE(runner, Hash_Get_Size(hash) == 0, "size is 0 after Clear");
 
     DECREF(hash);
     DECREF(dupe);
@@ -134,7 +128,7 @@ test_Store_and_Fetch(TestBatch *batch) {
 }
 
 static void
-test_Keys_Values_Iter(TestBatch *batch) {
+test_Keys_Values_Iter(TestBatchRunner *runner) {
     Hash     *hash     = Hash_new(0); // trigger multiple rebuilds.
     VArray   *expected = VA_new(100);
     VArray   *keys;
@@ -152,8 +146,8 @@ test_Keys_Values_Iter(TestBatch *batch) {
     values = Hash_Values(hash);
     VA_Sort(keys, NULL, NULL);
     VA_Sort(values, NULL, NULL);
-    TEST_TRUE(batch, VA_Equals(keys, (Obj*)expected), "Keys");
-    TEST_TRUE(batch, VA_Equals(values, (Obj*)expected), "Values");
+    TEST_TRUE(runner, VA_Equals(keys, (Obj*)expected), "Keys");
+    TEST_TRUE(runner, VA_Equals(values, (Obj*)expected), "Values");
     VA_Clear(keys);
     VA_Clear(values);
 
@@ -169,16 +163,16 @@ test_Keys_Values_Iter(TestBatch *batch) {
 
     VA_Sort(keys, NULL, NULL);
     VA_Sort(values, NULL, NULL);
-    TEST_TRUE(batch, VA_Equals(keys, (Obj*)expected), "Keys from Iter");
-    TEST_TRUE(batch, VA_Equals(values, (Obj*)expected), "Values from Iter");
+    TEST_TRUE(runner, VA_Equals(keys, (Obj*)expected), "Keys from Iter");
+    TEST_TRUE(runner, VA_Equals(values, (Obj*)expected), "Values from Iter");
 
     {
         ZombieCharBuf *forty = ZCB_WRAP_STR("40", 2);
         ZombieCharBuf *nope  = ZCB_WRAP_STR("nope", 4);
         Obj *key = Hash_Find_Key(hash, (Obj*)forty, ZCB_Hash_Sum(forty));
-        TEST_TRUE(batch, Obj_Equals(key, (Obj*)forty), "Find_Key");
+        TEST_TRUE(runner, Obj_Equals(key, (Obj*)forty), "Find_Key");
         key = Hash_Find_Key(hash, (Obj*)nope, ZCB_Hash_Sum(nope)),
-        TEST_TRUE(batch, key == NULL,
+        TEST_TRUE(runner, key == NULL,
                   "Find_Key returns NULL for non-existent key");
     }
 
@@ -189,7 +183,7 @@ test_Keys_Values_Iter(TestBatch *batch) {
 }
 
 static void
-test_Dump_and_Load(TestBatch *batch) {
+test_Dump_and_Load(TestBatchRunner *runner) {
     Hash *hash = Hash_new(0);
     Obj  *dump;
     Hash *loaded;
@@ -198,7 +192,7 @@ test_Dump_and_Load(TestBatch *batch) {
                    (Obj*)CB_new_from_trusted_utf8("foo", 3));
     dump = (Obj*)Hash_Dump(hash);
     loaded = (Hash*)Obj_Load(dump, dump);
-    TEST_TRUE(batch, Hash_Equals(hash, (Obj*)loaded),
+    TEST_TRUE(runner, Hash_Equals(hash, (Obj*)loaded),
               "Dump => Load round trip");
     DECREF(dump);
     DECREF(loaded);
@@ -210,7 +204,7 @@ test_Dump_and_Load(TestBatch *batch) {
     dump = (Obj*)Hash_Dump(hash);
     loaded = (Hash*)Obj_Load(dump, dump);
 
-    TEST_TRUE(batch, Hash_Equals(hash, (Obj*)loaded),
+    TEST_TRUE(runner, Hash_Equals(hash, (Obj*)loaded),
               "Load still works with _class if it's not a real class");
     DECREF(dump);
     DECREF(loaded);
@@ -221,7 +215,7 @@ test_Dump_and_Load(TestBatch *batch) {
 }
 
 static void
-test_serialization(TestBatch *batch) {
+test_serialization(TestBatchRunner *runner) {
     Hash  *wanted = Hash_new(0);
     Hash  *got;
 
@@ -233,7 +227,7 @@ test_serialization(TestBatch *batch) {
     }
 
     got = (Hash*)TestUtils_freeze_thaw((Obj*)wanted);
-    TEST_TRUE(batch, got && Hash_Equals(wanted, (Obj*)got),
+    TEST_TRUE(runner, got && Hash_Equals(wanted, (Obj*)got),
               "Round trip through serialization.");
 
     DECREF(got);
@@ -241,7 +235,7 @@ test_serialization(TestBatch *batch) {
 }
 
 static void
-test_stress(TestBatch *batch) {
+test_stress(TestBatchRunner *runner) {
     Hash     *hash     = Hash_new(0); // trigger multiple rebuilds.
     VArray   *expected = VA_new(1000);
     VArray   *keys;
@@ -269,8 +263,8 @@ test_stress(TestBatch *batch) {
     values = Hash_Values(hash);
     VA_Sort(keys, NULL, NULL);
     VA_Sort(values, NULL, NULL);
-    TEST_TRUE(batch, VA_Equals(keys, (Obj*)expected), "stress Keys");
-    TEST_TRUE(batch, VA_Equals(values, (Obj*)expected), "stress Values");
+    TEST_TRUE(runner, VA_Equals(keys, (Obj*)expected), "stress Keys");
+    TEST_TRUE(runner, VA_Equals(values, (Obj*)expected), "stress Values");
 
     DECREF(keys);
     DECREF(values);
@@ -279,15 +273,15 @@ test_stress(TestBatch *batch) {
 }
 
 void
-TestHash_run_tests(TestHash *self) {
-    TestBatch *batch = (TestBatch*)self;
+TestHash_run(TestHash *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 29);
     srand((unsigned int)time((time_t*)NULL));
-    test_Equals(batch);
-    test_Store_and_Fetch(batch);
-    test_Keys_Values_Iter(batch);
-    test_Dump_and_Load(batch);
-    test_serialization(batch);
-    test_stress(batch);
+    test_Equals(runner);
+    test_Store_and_Fetch(runner);
+    test_Keys_Values_Iter(runner);
+    test_Dump_and_Load(runner);
+    test_serialization(runner);
+    test_stress(runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestHash.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestHash.cfh b/core/Clownfish/Test/TestHash.cfh
index cc74687..a730105 100644
--- a/core/Clownfish/Test/TestHash.cfh
+++ b/core/Clownfish/Test/TestHash.cfh
@@ -20,13 +20,10 @@ class Clownfish::Test::TestHash
     inherits Clownfish::TestHarness::TestBatch {
 
     inert incremented TestHash*
-    new(TestFormatter *formatter);
-
-    inert TestHash*
-    init(TestHash *self, TestFormatter *formatter);
+    new();
 
     void
-    Run_Tests(TestHash *self);
+    Run(TestHash *self, TestBatchRunner *runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestLockFreeRegistry.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestLockFreeRegistry.c b/core/Clownfish/Test/TestLockFreeRegistry.c
index dcd5e8b..d895595 100644
--- a/core/Clownfish/Test/TestLockFreeRegistry.c
+++ b/core/Clownfish/Test/TestLockFreeRegistry.c
@@ -24,18 +24,12 @@
 
 #include "Clownfish/LockFreeRegistry.h"
 #include "Clownfish/Test.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
 #include "Clownfish/VTable.h"
 
 TestLockFreeRegistry*
-TestLFReg_new(TestFormatter *formatter) {
-    TestLockFreeRegistry *self = (TestLockFreeRegistry*)VTable_Make_Obj(TESTLOCKFREEREGISTRY);
-    return TestLFReg_init(self, formatter);
-}
-
-TestLockFreeRegistry*
-TestLFReg_init(TestLockFreeRegistry *self, TestFormatter *formatter) {
-    return (TestLockFreeRegistry*)TestBatch_init((TestBatch*)self, 6, formatter);
+TestLFReg_new() {
+    return (TestLockFreeRegistry*)VTable_Make_Obj(TESTLOCKFREEREGISTRY);
 }
 
 StupidHashCharBuf*
@@ -50,27 +44,27 @@ StupidHashCharBuf_hash_sum(StupidHashCharBuf *self) {
 }
 
 static void
-test_all(TestBatch *batch) {
+test_all(TestBatchRunner *runner) {
     LockFreeRegistry *registry = LFReg_new(10);
     StupidHashCharBuf *foo = StupidHashCharBuf_new("foo");
     StupidHashCharBuf *bar = StupidHashCharBuf_new("bar");
     StupidHashCharBuf *baz = StupidHashCharBuf_new("baz");
     StupidHashCharBuf *foo_dupe = StupidHashCharBuf_new("foo");
 
-    TEST_TRUE(batch, LFReg_Register(registry, (Obj*)foo, (Obj*)foo),
+    TEST_TRUE(runner, LFReg_Register(registry, (Obj*)foo, (Obj*)foo),
               "Register() returns true on success");
-    TEST_FALSE(batch,
+    TEST_FALSE(runner,
                LFReg_Register(registry, (Obj*)foo_dupe, (Obj*)foo_dupe),
                "Can't Register() keys that test equal");
 
-    TEST_TRUE(batch, LFReg_Register(registry, (Obj*)bar, (Obj*)bar),
+    TEST_TRUE(runner, LFReg_Register(registry, (Obj*)bar, (Obj*)bar),
               "Register() key with the same Hash_Sum but that isn't Equal");
 
-    TEST_TRUE(batch, LFReg_Fetch(registry, (Obj*)foo_dupe) == (Obj*)foo,
+    TEST_TRUE(runner, LFReg_Fetch(registry, (Obj*)foo_dupe) == (Obj*)foo,
               "Fetch()");
-    TEST_TRUE(batch, LFReg_Fetch(registry, (Obj*)bar) == (Obj*)bar,
+    TEST_TRUE(runner, LFReg_Fetch(registry, (Obj*)bar) == (Obj*)bar,
               "Fetch() again");
-    TEST_TRUE(batch, LFReg_Fetch(registry, (Obj*)baz) == NULL,
+    TEST_TRUE(runner, LFReg_Fetch(registry, (Obj*)baz) == NULL,
               "Fetch() non-existent key returns NULL");
 
     DECREF(foo_dupe);
@@ -81,9 +75,9 @@ test_all(TestBatch *batch) {
 }
 
 void
-TestLFReg_run_tests(TestLockFreeRegistry *self) {
-    TestBatch *batch = (TestBatch*)self;
-    test_all(batch);
+TestLFReg_run(TestLockFreeRegistry *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 6);
+    test_all(runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestLockFreeRegistry.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestLockFreeRegistry.cfh b/core/Clownfish/Test/TestLockFreeRegistry.cfh
index 376fbe2..a3a3675 100644
--- a/core/Clownfish/Test/TestLockFreeRegistry.cfh
+++ b/core/Clownfish/Test/TestLockFreeRegistry.cfh
@@ -20,13 +20,10 @@ class Clownfish::Test::TestLockFreeRegistry cnick TestLFReg
     inherits Clownfish::TestHarness::TestBatch {
 
     inert incremented TestLockFreeRegistry*
-    new(TestFormatter *formatter);
-
-    inert TestLockFreeRegistry*
-    init(TestLockFreeRegistry *self, TestFormatter *formatter);
+    new();
 
     void
-    Run_Tests(TestLockFreeRegistry *self);
+    Run(TestLockFreeRegistry *self, TestBatchRunner *runner);
 }
 
 /** Private test-only class for stressing LockFreeRegistry.

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestNum.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestNum.c b/core/Clownfish/Test/TestNum.c
index add9a65..2b05659 100644
--- a/core/Clownfish/Test/TestNum.c
+++ b/core/Clownfish/Test/TestNum.c
@@ -22,23 +22,17 @@
 #include "Clownfish/CharBuf.h"
 #include "Clownfish/Num.h"
 #include "Clownfish/Test.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
 #include "Clownfish/TestHarness/TestUtils.h"
 #include "Clownfish/VTable.h"
 
 TestNum*
-TestNum_new(TestFormatter *formatter) {
-    TestNum *self = (TestNum*)VTable_Make_Obj(TESTNUM);
-    return TestNum_init(self, formatter);
-}
-
-TestNum*
-TestNum_init(TestNum *self, TestFormatter *formatter) {
-    return (TestNum*)TestBatch_init((TestBatch*)self, 58, formatter);
+TestNum_new() {
+    return (TestNum*)VTable_Make_Obj(TESTNUM);
 }
 
 static void
-test_To_String(TestBatch *batch) {
+test_To_String(TestBatchRunner *runner) {
     Float32   *f32 = Float32_new(1.33f);
     Float64   *f64 = Float64_new(1.33);
     Integer32 *i32 = Int32_new(INT32_MAX);
@@ -50,17 +44,17 @@ test_To_String(TestBatch *batch) {
     CharBuf *true_string  = Bool_To_String(CFISH_TRUE);
     CharBuf *false_string = Bool_To_String(CFISH_FALSE);
 
-    TEST_TRUE(batch, CB_Starts_With_Str(f32_string, "1.3", 3),
+    TEST_TRUE(runner, CB_Starts_With_Str(f32_string, "1.3", 3),
               "Float32_To_String");
-    TEST_TRUE(batch, CB_Starts_With_Str(f64_string, "1.3", 3),
+    TEST_TRUE(runner, CB_Starts_With_Str(f64_string, "1.3", 3),
               "Float64_To_String");
-    TEST_TRUE(batch, CB_Equals_Str(i32_string, "2147483647", 10),
+    TEST_TRUE(runner, CB_Equals_Str(i32_string, "2147483647", 10),
               "Int32_To_String");
-    TEST_TRUE(batch, CB_Equals_Str(i64_string, "9223372036854775807", 19),
+    TEST_TRUE(runner, CB_Equals_Str(i64_string, "9223372036854775807", 19),
               "Int64_To_String");
-    TEST_TRUE(batch, CB_Equals_Str(true_string, "true", 4),
+    TEST_TRUE(runner, CB_Equals_Str(true_string, "true", 4),
               "Bool_To_String [true]");
-    TEST_TRUE(batch, CB_Equals_Str(false_string, "false", 5),
+    TEST_TRUE(runner, CB_Equals_Str(false_string, "false", 5),
               "Bool_To_String [false]");
 
     DECREF(false_string);
@@ -76,7 +70,7 @@ test_To_String(TestBatch *batch) {
 }
 
 static void
-test_accessors(TestBatch *batch) {
+test_accessors(TestBatchRunner *runner) {
     Float32   *f32 = Float32_new(1.0);
     Float64   *f64 = Float64_new(1.0);
     Integer32 *i32 = Int32_new(1);
@@ -87,49 +81,49 @@ test_accessors(TestBatch *batch) {
     double got64;
 
     Float32_Set_Value(f32, 1.33f);
-    TEST_FLOAT_EQ(batch, Float32_Get_Value(f32), 1.33f,
+    TEST_FLOAT_EQ(runner, Float32_Get_Value(f32), 1.33f,
                   "F32 Set_Value Get_Value");
 
     Float64_Set_Value(f64, 1.33);
     got64 = Float64_Get_Value(f64);
-    TEST_TRUE(batch, *(int64_t*)&got64 == *(int64_t*)&wanted64,
+    TEST_TRUE(runner, *(int64_t*)&got64 == *(int64_t*)&wanted64,
               "F64 Set_Value Get_Value");
 
-    TEST_TRUE(batch, Float32_To_I64(f32) == 1, "Float32_To_I64");
-    TEST_TRUE(batch, Float64_To_I64(f64) == 1, "Float64_To_I64");
+    TEST_TRUE(runner, Float32_To_I64(f32) == 1, "Float32_To_I64");
+    TEST_TRUE(runner, Float64_To_I64(f64) == 1, "Float64_To_I64");
 
     got32 = (float)Float32_To_F64(f32);
-    TEST_TRUE(batch, *(int32_t*)&got32 == *(int32_t*)&wanted32,
+    TEST_TRUE(runner, *(int32_t*)&got32 == *(int32_t*)&wanted32,
               "Float32_To_F64");
 
     got64 = Float64_To_F64(f64);
-    TEST_TRUE(batch, *(int64_t*)&got64 == *(int64_t*)&wanted64,
+    TEST_TRUE(runner, *(int64_t*)&got64 == *(int64_t*)&wanted64,
               "Float64_To_F64");
 
     Int32_Set_Value(i32, INT32_MIN);
-    TEST_INT_EQ(batch, Int32_Get_Value(i32), INT32_MIN,
+    TEST_INT_EQ(runner, Int32_Get_Value(i32), INT32_MIN,
                 "I32 Set_Value Get_Value");
 
     Int64_Set_Value(i64, INT64_MIN);
-    TEST_TRUE(batch, Int64_Get_Value(i64) == INT64_MIN,
+    TEST_TRUE(runner, Int64_Get_Value(i64) == INT64_MIN,
               "I64 Set_Value Get_Value");
 
     Int32_Set_Value(i32, -1);
     Int64_Set_Value(i64, -1);
-    TEST_TRUE(batch, Int32_To_F64(i32) == -1, "Int32_To_F64");
-    TEST_TRUE(batch, Int64_To_F64(i64) == -1, "Int64_To_F64");
+    TEST_TRUE(runner, Int32_To_F64(i32) == -1, "Int32_To_F64");
+    TEST_TRUE(runner, Int64_To_F64(i64) == -1, "Int64_To_F64");
 
-    TEST_INT_EQ(batch, Bool_Get_Value(CFISH_TRUE), true,
+    TEST_INT_EQ(runner, Bool_Get_Value(CFISH_TRUE), true,
                 "Bool_Get_Value [true]");
-    TEST_INT_EQ(batch, Bool_Get_Value(CFISH_FALSE), false,
+    TEST_INT_EQ(runner, Bool_Get_Value(CFISH_FALSE), false,
                 "Bool_Get_Value [false]");
-    TEST_TRUE(batch, Bool_To_I64(CFISH_TRUE) == true,
+    TEST_TRUE(runner, Bool_To_I64(CFISH_TRUE) == true,
               "Bool_To_I64 [true]");
-    TEST_TRUE(batch, Bool_To_I64(CFISH_FALSE) == false,
+    TEST_TRUE(runner, Bool_To_I64(CFISH_FALSE) == false,
               "Bool_To_I64 [false]");
-    TEST_TRUE(batch, Bool_To_F64(CFISH_TRUE) == 1.0,
+    TEST_TRUE(runner, Bool_To_F64(CFISH_TRUE) == 1.0,
               "Bool_To_F64 [true]");
-    TEST_TRUE(batch, Bool_To_F64(CFISH_FALSE) == 0.0,
+    TEST_TRUE(runner, Bool_To_F64(CFISH_FALSE) == 0.0,
               "Bool_To_F64 [false]");
 
     DECREF(i64);
@@ -139,74 +133,74 @@ test_accessors(TestBatch *batch) {
 }
 
 static void
-test_Equals_and_Compare_To(TestBatch *batch) {
+test_Equals_and_Compare_To(TestBatchRunner *runner) {
     Float32   *f32 = Float32_new(1.0);
     Float64   *f64 = Float64_new(1.0);
     Integer32 *i32 = Int32_new(INT32_MAX);
     Integer64 *i64 = Int64_new(INT64_MAX);
 
-    TEST_TRUE(batch, Float32_Compare_To(f32, (Obj*)f64) == 0,
+    TEST_TRUE(runner, Float32_Compare_To(f32, (Obj*)f64) == 0,
               "F32_Compare_To equal");
-    TEST_TRUE(batch, Float32_Equals(f32, (Obj*)f64),
+    TEST_TRUE(runner, Float32_Equals(f32, (Obj*)f64),
               "F32_Equals equal");
 
     Float64_Set_Value(f64, 2.0);
-    TEST_TRUE(batch, Float32_Compare_To(f32, (Obj*)f64) < 0,
+    TEST_TRUE(runner, Float32_Compare_To(f32, (Obj*)f64) < 0,
               "F32_Compare_To less than");
-    TEST_FALSE(batch, Float32_Equals(f32, (Obj*)f64),
+    TEST_FALSE(runner, Float32_Equals(f32, (Obj*)f64),
                "F32_Equals less than");
 
     Float64_Set_Value(f64, 0.0);
-    TEST_TRUE(batch, Float32_Compare_To(f32, (Obj*)f64) > 0,
+    TEST_TRUE(runner, Float32_Compare_To(f32, (Obj*)f64) > 0,
               "F32_Compare_To greater than");
-    TEST_FALSE(batch, Float32_Equals(f32, (Obj*)f64),
+    TEST_FALSE(runner, Float32_Equals(f32, (Obj*)f64),
                "F32_Equals greater than");
 
     Float64_Set_Value(f64, 1.0);
     Float32_Set_Value(f32, 1.0);
-    TEST_TRUE(batch, Float64_Compare_To(f64, (Obj*)f32) == 0,
+    TEST_TRUE(runner, Float64_Compare_To(f64, (Obj*)f32) == 0,
               "F64_Compare_To equal");
-    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f32),
+    TEST_TRUE(runner, Float64_Equals(f64, (Obj*)f32),
               "F64_Equals equal");
 
     Float32_Set_Value(f32, 2.0);
-    TEST_TRUE(batch, Float64_Compare_To(f64, (Obj*)f32) < 0,
+    TEST_TRUE(runner, Float64_Compare_To(f64, (Obj*)f32) < 0,
               "F64_Compare_To less than");
-    TEST_FALSE(batch, Float64_Equals(f64, (Obj*)f32),
+    TEST_FALSE(runner, Float64_Equals(f64, (Obj*)f32),
                "F64_Equals less than");
 
     Float32_Set_Value(f32, 0.0);
-    TEST_TRUE(batch, Float64_Compare_To(f64, (Obj*)f32) > 0,
+    TEST_TRUE(runner, Float64_Compare_To(f64, (Obj*)f32) > 0,
               "F64_Compare_To greater than");
-    TEST_FALSE(batch, Float64_Equals(f64, (Obj*)f32),
+    TEST_FALSE(runner, Float64_Equals(f64, (Obj*)f32),
                "F64_Equals greater than");
 
     Float64_Set_Value(f64, INT64_MAX * 2.0);
-    TEST_TRUE(batch, Float64_Compare_To(f64, (Obj*)i64) > 0,
+    TEST_TRUE(runner, Float64_Compare_To(f64, (Obj*)i64) > 0,
               "Float64 comparison to Integer64");
-    TEST_TRUE(batch, Int64_Compare_To(i64, (Obj*)f64) < 0,
+    TEST_TRUE(runner, Int64_Compare_To(i64, (Obj*)f64) < 0,
               "Integer64 comparison to Float64");
 
     Float32_Set_Value(f32, INT32_MAX * 2.0f);
-    TEST_TRUE(batch, Float32_Compare_To(f32, (Obj*)i32) > 0,
+    TEST_TRUE(runner, Float32_Compare_To(f32, (Obj*)i32) > 0,
               "Float32 comparison to Integer32");
-    TEST_TRUE(batch, Int32_Compare_To(i32, (Obj*)f32) < 0,
+    TEST_TRUE(runner, Int32_Compare_To(i32, (Obj*)f32) < 0,
               "Integer32 comparison to Float32");
 
     Int64_Set_Value(i64, INT64_C(0x6666666666666666));
     Integer64 *i64_copy = Int64_new(INT64_C(0x6666666666666666));
-    TEST_TRUE(batch, Int64_Compare_To(i64, (Obj*)i64_copy) == 0,
+    TEST_TRUE(runner, Int64_Compare_To(i64, (Obj*)i64_copy) == 0,
               "Integer64 comparison to same number");
 
-    TEST_TRUE(batch, Bool_Equals(CFISH_TRUE, (Obj*)CFISH_TRUE),
+    TEST_TRUE(runner, Bool_Equals(CFISH_TRUE, (Obj*)CFISH_TRUE),
               "CFISH_TRUE Equals itself");
-    TEST_TRUE(batch, Bool_Equals(CFISH_FALSE, (Obj*)CFISH_FALSE),
+    TEST_TRUE(runner, Bool_Equals(CFISH_FALSE, (Obj*)CFISH_FALSE),
               "CFISH_FALSE Equals itself");
-    TEST_FALSE(batch, Bool_Equals(CFISH_FALSE, (Obj*)CFISH_TRUE),
+    TEST_FALSE(runner, Bool_Equals(CFISH_FALSE, (Obj*)CFISH_TRUE),
                "CFISH_FALSE not Equals CFISH_TRUE ");
-    TEST_FALSE(batch, Bool_Equals(CFISH_TRUE, (Obj*)CFISH_FALSE),
+    TEST_FALSE(runner, Bool_Equals(CFISH_TRUE, (Obj*)CFISH_FALSE),
                "CFISH_TRUE not Equals CFISH_FALSE ");
-    TEST_FALSE(batch, Bool_Equals(CFISH_TRUE, (Obj*)CHARBUF),
+    TEST_FALSE(runner, Bool_Equals(CFISH_TRUE, (Obj*)CHARBUF),
                "CFISH_TRUE not Equals random other object ");
 
     DECREF(i64_copy);
@@ -217,7 +211,7 @@ test_Equals_and_Compare_To(TestBatch *batch) {
 }
 
 static void
-test_Clone(TestBatch *batch) {
+test_Clone(TestBatchRunner *runner) {
     Float32   *f32 = Float32_new(1.33f);
     Float64   *f64 = Float64_new(1.33);
     Integer32 *i32 = Int32_new(INT32_MAX);
@@ -226,15 +220,15 @@ test_Clone(TestBatch *batch) {
     Float64   *f64_dupe = Float64_Clone(f64);
     Integer32 *i32_dupe = Int32_Clone(i32);
     Integer64 *i64_dupe = Int64_Clone(i64);
-    TEST_TRUE(batch, Float32_Equals(f32, (Obj*)f32_dupe),
+    TEST_TRUE(runner, Float32_Equals(f32, (Obj*)f32_dupe),
               "Float32 Clone");
-    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f64_dupe),
+    TEST_TRUE(runner, Float64_Equals(f64, (Obj*)f64_dupe),
               "Float64 Clone");
-    TEST_TRUE(batch, Int32_Equals(i32, (Obj*)i32_dupe),
+    TEST_TRUE(runner, Int32_Equals(i32, (Obj*)i32_dupe),
               "Integer32 Clone");
-    TEST_TRUE(batch, Int64_Equals(i64, (Obj*)i64_dupe),
+    TEST_TRUE(runner, Int64_Equals(i64, (Obj*)i64_dupe),
               "Integer64 Clone");
-    TEST_TRUE(batch, Bool_Equals(CFISH_TRUE, (Obj*)Bool_Clone(CFISH_TRUE)),
+    TEST_TRUE(runner, Bool_Equals(CFISH_TRUE, (Obj*)Bool_Clone(CFISH_TRUE)),
               "BoolNum Clone");
     DECREF(i64_dupe);
     DECREF(i32_dupe);
@@ -247,7 +241,7 @@ test_Clone(TestBatch *batch) {
 }
 
 static void
-test_Mimic(TestBatch *batch) {
+test_Mimic(TestBatchRunner *runner) {
     Float32   *f32 = Float32_new(1.33f);
     Float64   *f64 = Float64_new(1.33);
     Integer32 *i32 = Int32_new(INT32_MAX);
@@ -260,13 +254,13 @@ test_Mimic(TestBatch *batch) {
     Float64_Mimic(f64_dupe, (Obj*)f64);
     Int32_Mimic(i32_dupe, (Obj*)i32);
     Int64_Mimic(i64_dupe, (Obj*)i64);
-    TEST_TRUE(batch, Float32_Equals(f32, (Obj*)f32_dupe),
+    TEST_TRUE(runner, Float32_Equals(f32, (Obj*)f32_dupe),
               "Float32 Mimic");
-    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f64_dupe),
+    TEST_TRUE(runner, Float64_Equals(f64, (Obj*)f64_dupe),
               "Float64 Mimic");
-    TEST_TRUE(batch, Int32_Equals(i32, (Obj*)i32_dupe),
+    TEST_TRUE(runner, Int32_Equals(i32, (Obj*)i32_dupe),
               "Integer32 Mimic");
-    TEST_TRUE(batch, Int64_Equals(i64, (Obj*)i64_dupe),
+    TEST_TRUE(runner, Int64_Equals(i64, (Obj*)i64_dupe),
               "Integer64 Mimic");
     DECREF(i64_dupe);
     DECREF(i32_dupe);
@@ -279,7 +273,7 @@ test_Mimic(TestBatch *batch) {
 }
 
 static void
-test_serialization(TestBatch *batch) {
+test_serialization(TestBatchRunner *runner) {
     Float32   *f32 = Float32_new(1.33f);
     Float64   *f64 = Float64_new(1.33);
     Integer32 *i32 = Int32_new(-1);
@@ -290,15 +284,15 @@ test_serialization(TestBatch *batch) {
     Integer64 *i64_thaw = (Integer64*)TestUtils_freeze_thaw((Obj*)i64);
     BoolNum   *true_thaw = (BoolNum*)TestUtils_freeze_thaw((Obj*)CFISH_TRUE);
 
-    TEST_TRUE(batch, Float32_Equals(f32, (Obj*)f32_thaw),
+    TEST_TRUE(runner, Float32_Equals(f32, (Obj*)f32_thaw),
               "Float32 freeze/thaw");
-    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f64_thaw),
+    TEST_TRUE(runner, Float64_Equals(f64, (Obj*)f64_thaw),
               "Float64 freeze/thaw");
-    TEST_TRUE(batch, Int32_Equals(i32, (Obj*)i32_thaw),
+    TEST_TRUE(runner, Int32_Equals(i32, (Obj*)i32_thaw),
               "Integer32 freeze/thaw");
-    TEST_TRUE(batch, Int64_Equals(i64, (Obj*)i64_thaw),
+    TEST_TRUE(runner, Int64_Equals(i64, (Obj*)i64_thaw),
               "Integer64 freeze/thaw");
-    TEST_TRUE(batch, Bool_Equals(CFISH_TRUE, (Obj*)true_thaw),
+    TEST_TRUE(runner, Bool_Equals(CFISH_TRUE, (Obj*)true_thaw),
               "BoolNum freeze/thaw");
 
     DECREF(i64_thaw);
@@ -312,14 +306,14 @@ test_serialization(TestBatch *batch) {
 }
 
 void
-TestNum_run_tests(TestNum *self) {
-    TestBatch *batch = (TestBatch*)self;
-    test_To_String(batch);
-    test_accessors(batch);
-    test_Equals_and_Compare_To(batch);
-    test_Clone(batch);
-    test_Mimic(batch);
-    test_serialization(batch);
+TestNum_run(TestNum *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 58);
+    test_To_String(runner);
+    test_accessors(runner);
+    test_Equals_and_Compare_To(runner);
+    test_Clone(runner);
+    test_Mimic(runner);
+    test_serialization(runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestNum.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestNum.cfh b/core/Clownfish/Test/TestNum.cfh
index 9176210..6d1f663 100644
--- a/core/Clownfish/Test/TestNum.cfh
+++ b/core/Clownfish/Test/TestNum.cfh
@@ -20,13 +20,10 @@ class Clownfish::Test::TestNum
     inherits Clownfish::TestHarness::TestBatch {
 
     inert incremented TestNum*
-    new(TestFormatter *formatter);
-
-    inert TestNum*
-    init(TestNum *self, TestFormatter *formatter);
+    new();
 
     void
-    Run_Tests(TestNum *self);
+    Run(TestNum *self, TestBatchRunner *runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestObj.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestObj.c b/core/Clownfish/Test/TestObj.c
index 63d81f7..f1d6d90 100644
--- a/core/Clownfish/Test/TestObj.c
+++ b/core/Clownfish/Test/TestObj.c
@@ -25,18 +25,12 @@
 #include "Clownfish/CharBuf.h"
 #include "Clownfish/Err.h"
 #include "Clownfish/Test.h"
-#include "Clownfish/TestHarness/TestFormatter.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
 #include "Clownfish/VTable.h"
 
 TestObj*
-TestObj_new(TestFormatter *formatter) {
-    TestObj *self = (TestObj*)VTable_Make_Obj(TESTOBJ);
-    return TestObj_init(self, formatter);
-}
-
-TestObj*
-TestObj_init(TestObj *self, TestFormatter *formatter) {
-    return (TestObj*)TestBatch_init((TestBatch*)self, 20, formatter);
+TestObj_new() {
+    return (TestObj*)VTable_Make_Obj(TESTOBJ);
 }
 
 static Obj*
@@ -52,23 +46,23 @@ S_new_testobj() {
 }
 
 static void
-test_refcounts(TestBatch *batch) {
+test_refcounts(TestBatchRunner *runner) {
     Obj *obj = S_new_testobj();
 
-    TEST_INT_EQ(batch, Obj_Get_RefCount(obj), 1,
+    TEST_INT_EQ(runner, Obj_Get_RefCount(obj), 1,
                 "Correct starting refcount");
 
     Obj_Inc_RefCount(obj);
-    TEST_INT_EQ(batch, Obj_Get_RefCount(obj), 2, "Inc_RefCount");
+    TEST_INT_EQ(runner, Obj_Get_RefCount(obj), 2, "Inc_RefCount");
 
     Obj_Dec_RefCount(obj);
-    TEST_INT_EQ(batch, Obj_Get_RefCount(obj), 1, "Dec_RefCount");
+    TEST_INT_EQ(runner, Obj_Get_RefCount(obj), 1, "Dec_RefCount");
 
     DECREF(obj);
 }
 
 static void
-test_To_String(TestBatch *batch) {
+test_To_String(TestBatchRunner *runner) {
     Obj *testobj = S_new_testobj();
     CharBuf *string = Obj_To_String(testobj);
     ZombieCharBuf *temp = ZCB_WRAP(string);
@@ -76,17 +70,17 @@ test_To_String(TestBatch *batch) {
         if (ZCB_Starts_With_Str(temp, "TestObj", 7)) { break; }
         ZCB_Nip_One(temp);
     }
-    TEST_TRUE(batch, ZCB_Starts_With_Str(temp, "TestObj", 7), "To_String");
+    TEST_TRUE(runner, ZCB_Starts_With_Str(temp, "TestObj", 7), "To_String");
     DECREF(string);
     DECREF(testobj);
 }
 
 static void
-test_Dump(TestBatch *batch) {
+test_Dump(TestBatchRunner *runner) {
     Obj *testobj = S_new_testobj();
     CharBuf *string = Obj_To_String(testobj);
     Obj *dump = Obj_Dump(testobj);
-    TEST_TRUE(batch, Obj_Equals(dump, (Obj*)string),
+    TEST_TRUE(runner, Obj_Equals(dump, (Obj*)string),
               "Default Dump returns To_String");
     DECREF(dump);
     DECREF(string);
@@ -94,13 +88,13 @@ test_Dump(TestBatch *batch) {
 }
 
 static void
-test_Equals(TestBatch *batch) {
+test_Equals(TestBatchRunner *runner) {
     Obj *testobj = S_new_testobj();
     Obj *other   = S_new_testobj();
 
-    TEST_TRUE(batch, Obj_Equals(testobj, testobj),
+    TEST_TRUE(runner, Obj_Equals(testobj, testobj),
               "Equals is true for the same object");
-    TEST_FALSE(batch, Obj_Equals(testobj, other),
+    TEST_FALSE(runner, Obj_Equals(testobj, other),
                "Distinct objects are not equal");
 
     DECREF(testobj);
@@ -108,25 +102,25 @@ test_Equals(TestBatch *batch) {
 }
 
 static void
-test_Hash_Sum(TestBatch *batch) {
+test_Hash_Sum(TestBatchRunner *runner) {
     Obj *testobj = S_new_testobj();
     int64_t address64 = PTR_TO_I64(testobj);
     int32_t address32 = (int32_t)address64;
-    TEST_TRUE(batch, (Obj_Hash_Sum(testobj) == address32),
+    TEST_TRUE(runner, (Obj_Hash_Sum(testobj) == address32),
               "Hash_Sum uses memory address");
     DECREF(testobj);
 }
 
 static void
-test_Is_A(TestBatch *batch) {
+test_Is_A(TestBatchRunner *runner) {
     CharBuf *charbuf   = CB_new(0);
     VTable  *bb_vtable = CB_Get_VTable(charbuf);
     CharBuf *klass     = CB_Get_Class_Name(charbuf);
 
-    TEST_TRUE(batch, CB_Is_A(charbuf, CHARBUF), "CharBuf Is_A CharBuf.");
-    TEST_TRUE(batch, CB_Is_A(charbuf, OBJ), "CharBuf Is_A Obj.");
-    TEST_TRUE(batch, bb_vtable == CHARBUF, "Get_VTable");
-    TEST_TRUE(batch, CB_Equals(VTable_Get_Name(CHARBUF), (Obj*)klass),
+    TEST_TRUE(runner, CB_Is_A(charbuf, CHARBUF), "CharBuf Is_A CharBuf.");
+    TEST_TRUE(runner, CB_Is_A(charbuf, OBJ), "CharBuf Is_A Obj.");
+    TEST_TRUE(runner, bb_vtable == CHARBUF, "Get_VTable");
+    TEST_TRUE(runner, CB_Equals(VTable_Get_Name(CHARBUF), (Obj*)klass),
               "Get_Class_Name");
 
     DECREF(charbuf);
@@ -173,12 +167,12 @@ S_attempt_Mimic(void *context) {
 }
 
 static void
-S_verify_abstract_error(TestBatch *batch, Err_Attempt_t routine,
+S_verify_abstract_error(TestBatchRunner *runner, Err_Attempt_t routine,
                         void *context, const char *name) {
     char message[100];
     sprintf(message, "%s() is abstract", name);
     Err *error = Err_trap(routine, context);
-    TEST_TRUE(batch, error != NULL
+    TEST_TRUE(runner, error != NULL
               && Err_Is_A(error, ERR) 
               && CB_Find_Str(Err_Get_Mess(error), "bstract", 7) != -1,
               message);
@@ -186,31 +180,31 @@ S_verify_abstract_error(TestBatch *batch, Err_Attempt_t routine,
 }
 
 static void
-test_abstract_routines(TestBatch *batch) {
+test_abstract_routines(TestBatchRunner *runner) {
     Obj *blank = VTable_Make_Obj(OBJ);
-    S_verify_abstract_error(batch, S_attempt_init, blank, "init");
+    S_verify_abstract_error(runner, S_attempt_init, blank, "init");
 
     Obj *obj = S_new_testobj();
-    S_verify_abstract_error(batch, S_attempt_Clone,      obj, "Clone");
-    S_verify_abstract_error(batch, S_attempt_Make,       obj, "Make");
-    S_verify_abstract_error(batch, S_attempt_Compare_To, obj, "Compare_To");
-    S_verify_abstract_error(batch, S_attempt_To_I64,     obj, "To_I64");
-    S_verify_abstract_error(batch, S_attempt_To_F64,     obj, "To_F64");
-    S_verify_abstract_error(batch, S_attempt_Load,       obj, "Load");
-    S_verify_abstract_error(batch, S_attempt_Mimic,      obj, "Mimic");
+    S_verify_abstract_error(runner, S_attempt_Clone,      obj, "Clone");
+    S_verify_abstract_error(runner, S_attempt_Make,       obj, "Make");
+    S_verify_abstract_error(runner, S_attempt_Compare_To, obj, "Compare_To");
+    S_verify_abstract_error(runner, S_attempt_To_I64,     obj, "To_I64");
+    S_verify_abstract_error(runner, S_attempt_To_F64,     obj, "To_F64");
+    S_verify_abstract_error(runner, S_attempt_Load,       obj, "Load");
+    S_verify_abstract_error(runner, S_attempt_Mimic,      obj, "Mimic");
     DECREF(obj);
 }
 
 void
-TestObj_run_tests(TestObj *self) {
-    TestBatch *batch = (TestBatch*)self;
-    test_refcounts(batch);
-    test_To_String(batch);
-    test_Dump(batch);
-    test_Equals(batch);
-    test_Hash_Sum(batch);
-    test_Is_A(batch);
-    test_abstract_routines(batch);
+TestObj_run(TestObj *self, TestBatchRunner *runner) {
+    TestBatchRunner_Plan(runner, (TestBatch*)self, 20);
+    test_refcounts(runner);
+    test_To_String(runner);
+    test_Dump(runner);
+    test_Equals(runner);
+    test_Hash_Sum(runner);
+    test_Is_A(runner);
+    test_abstract_routines(runner);
 }
 
 

http://git-wip-us.apache.org/repos/asf/lucy/blob/3da6ebd3/core/Clownfish/Test/TestObj.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestObj.cfh b/core/Clownfish/Test/TestObj.cfh
index 3cca164..eb4a0b6 100644
--- a/core/Clownfish/Test/TestObj.cfh
+++ b/core/Clownfish/Test/TestObj.cfh
@@ -20,13 +20,10 @@ class Clownfish::Test::TestObj
     inherits Clownfish::TestHarness::TestBatch {
 
     inert incremented TestObj*
-    new(TestFormatter *formatter);
-
-    inert TestObj*
-    init(TestObj *self, TestFormatter *formatter);
+    new();
 
     void
-    Run_Tests(TestObj *self);
+    Run(TestObj *self, TestBatchRunner *runner);
 }