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/28 19:47:49 UTC

[lucy-commits] [16/19] git commit: refs/heads/separate-clownfish-wip1 - Move Clownfish runtime tests to a separate parcel

Move Clownfish runtime tests to a separate parcel

Parcel "TestClownfish", cnick "TestCfish".

There's some code duplication in Clownfish::Test and
Clownfish::TestUtils which can be addressed later.


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

Branch: refs/heads/separate-clownfish-wip1
Commit: a51ff172681ed183673c3173f56ad48ff14e082f
Parents: c2eee72
Author: Nick Wellnhofer <we...@aevum.de>
Authored: Mon May 27 19:40:10 2013 +0200
Committer: Nick Wellnhofer <we...@aevum.de>
Committed: Tue May 28 17:07:28 2013 +0200

----------------------------------------------------------------------
 c/t/test_lucy.c                                |    7 +-
 common/charmonizer.c                           |    8 +
 common/charmonizer.main                        |    8 +
 core/Clownfish/Test.c                          |  117 +++++
 core/Clownfish/Test.cfh                        |   42 ++
 core/Clownfish/Test/TestByteBuf.c              |  173 ++++++++
 core/Clownfish/Test/TestByteBuf.cfh            |   32 ++
 core/Clownfish/Test/TestCharBuf.c              |  442 +++++++++++++++++++
 core/Clownfish/Test/TestCharBuf.cfh            |   32 ++
 core/Clownfish/Test/TestErr.c                  |   56 +++
 core/Clownfish/Test/TestErr.cfh                |   31 ++
 core/Clownfish/Test/TestHash.c                 |  293 ++++++++++++
 core/Clownfish/Test/TestHash.cfh               |   32 ++
 core/Clownfish/Test/TestLockFreeRegistry.c     |   89 ++++
 core/Clownfish/Test/TestLockFreeRegistry.cfh   |   43 ++
 core/Clownfish/Test/TestNum.c                  |  325 ++++++++++++++
 core/Clownfish/Test/TestNum.cfh                |   32 ++
 core/Clownfish/Test/TestObj.c                  |  216 +++++++++
 core/Clownfish/Test/TestObj.cfh                |   32 ++
 core/Clownfish/Test/TestUtils.c                |  140 ++++++
 core/Clownfish/Test/TestUtils.cfh              |   71 +++
 core/Clownfish/Test/TestVArray.c               |  353 +++++++++++++++
 core/Clownfish/Test/TestVArray.cfh             |   32 ++
 core/Clownfish/Test/Util/TestAtomic.c          |   71 +++
 core/Clownfish/Test/Util/TestAtomic.cfh        |   32 ++
 core/Clownfish/Test/Util/TestMemory.c          |  124 ++++++
 core/Clownfish/Test/Util/TestMemory.cfh        |   32 ++
 core/Clownfish/Test/Util/TestNumberUtils.c     |  381 ++++++++++++++++
 core/Clownfish/Test/Util/TestNumberUtils.cfh   |   32 ++
 core/Clownfish/Test/Util/TestStringHelper.c    |  361 +++++++++++++++
 core/Clownfish/Test/Util/TestStringHelper.cfh  |   32 ++
 core/Lucy/Test.c                               |   24 -
 core/Lucy/Test/Object/TestByteBuf.c            |  168 -------
 core/Lucy/Test/Object/TestByteBuf.cfh          |   33 --
 core/Lucy/Test/Object/TestCharBuf.c            |  436 ------------------
 core/Lucy/Test/Object/TestCharBuf.cfh          |   33 --
 core/Lucy/Test/Object/TestErr.c                |   53 ---
 core/Lucy/Test/Object/TestErr.cfh              |   31 --
 core/Lucy/Test/Object/TestHash.c               |  286 ------------
 core/Lucy/Test/Object/TestHash.cfh             |   32 --
 core/Lucy/Test/Object/TestLockFreeRegistry.c   |   86 ----
 core/Lucy/Test/Object/TestLockFreeRegistry.cfh |   43 --
 core/Lucy/Test/Object/TestNum.c                |  321 --------------
 core/Lucy/Test/Object/TestNum.cfh              |   32 --
 core/Lucy/Test/Object/TestObj.c                |  211 ---------
 core/Lucy/Test/Object/TestObj.cfh              |   32 --
 core/Lucy/Test/Object/TestVArray.c             |  345 ---------------
 core/Lucy/Test/Object/TestVArray.cfh           |   32 --
 core/Lucy/Test/TestUtils.c                     |    2 +-
 core/Lucy/Test/Util/TestAtomic.c               |   68 ---
 core/Lucy/Test/Util/TestAtomic.cfh             |   32 --
 core/Lucy/Test/Util/TestMemory.c               |  121 -----
 core/Lucy/Test/Util/TestMemory.cfh             |   32 --
 core/Lucy/Test/Util/TestNumberUtils.c          |  376 ----------------
 core/Lucy/Test/Util/TestNumberUtils.cfh        |   32 --
 core/Lucy/Test/Util/TestStringHelper.c         |  354 ---------------
 core/Lucy/Test/Util/TestStringHelper.cfh       |   32 --
 core/TestClownfish.c                           |   20 +
 core/TestClownfish.cfp                         |    5 +
 perl/Build.PL                                  |    2 +-
 perl/buildlib/Lucy/Build/Binding/Misc.pm       |   33 ++-
 perl/lib/Clownfish/Test.pm                     |   25 +
 perl/t/core/016-varray.t                       |    4 +-
 perl/t/core/017-hash.t                         |    4 +-
 perl/t/core/019-obj.t                          |    4 +-
 perl/t/core/020-err.t                          |    4 +-
 perl/t/core/022-bytebuf.t                      |    4 +-
 perl/t/core/029-charbuf.t                      |    4 +-
 perl/t/core/030-number_utils.t                 |    6 +-
 perl/t/core/031-num.t                          |    4 +-
 perl/t/core/032-string_helper.t                |    6 +-
 perl/t/core/037-atomic.t                       |    4 +-
 perl/t/core/038-lock_free_registry.t           |    6 +-
 perl/t/core/039-memory.t                       |    4 +-
 74 files changed, 3782 insertions(+), 3275 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/c/t/test_lucy.c
----------------------------------------------------------------------
diff --git a/c/t/test_lucy.c b/c/t/test_lucy.c
index 709b5d1..d7ba0bb 100644
--- a/c/t/test_lucy.c
+++ b/c/t/test_lucy.c
@@ -17,17 +17,20 @@
 #include <stdlib.h>
 
 #include "Clownfish/Test/TestFormatter.h"
+#include "Clownfish/Test.h"
 #include "Lucy/Test.h"
 
 int
 main() {
     cfish_TestFormatterCF *formatter;
-    bool success;
+    bool success = true;
 
+    testcfish_bootstrap_parcel();
     lucy_bootstrap_parcel();
 
     formatter = cfish_TestFormatterCF_new();
-    success = lucy_Test_run_all_batches((cfish_TestFormatter*)formatter);
+    success &= testcfish_Test_run_all_batches((cfish_TestFormatter*)formatter);
+    success &= lucy_Test_run_all_batches((cfish_TestFormatter*)formatter);
     CFISH_DECREF(formatter);
 
     return success ? EXIT_SUCCESS : EXIT_FAILURE;

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/common/charmonizer.c
----------------------------------------------------------------------
diff --git a/common/charmonizer.c b/common/charmonizer.c
index 21cc5b3..200f570 100644
--- a/common/charmonizer.c
+++ b/common/charmonizer.c
@@ -6803,6 +6803,7 @@ S_add_compiler_flags(struct chaz_CLIArgs *args) {
     }
 
     chaz_CFlags_add_define(extra_cflags, "CFP_CFISH", NULL);
+    chaz_CFlags_add_define(extra_cflags, "CFP_TESTCFISH", NULL);
     chaz_CFlags_add_define(extra_cflags, "CFP_LUCY", NULL);
 }
 
@@ -6959,6 +6960,10 @@ S_write_makefile(struct chaz_CLIArgs *args) {
     chaz_MakeVar_append(var, scratch);
     free(scratch);
     scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
+                             "testcfish_parcel", obj_ext, NULL);
+    chaz_MakeVar_append(var, scratch);
+    free(scratch);
+    scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
                              "lucy_parcel", obj_ext, NULL);
     chaz_MakeVar_append(var, scratch);
     free(scratch);
@@ -7001,6 +7006,9 @@ S_write_makefile(struct chaz_CLIArgs *args) {
     scratch = chaz_Util_join(dir_sep, "autogen", "source", "cfish_parcel.c",
                              NULL);
     rule = chaz_MakeFile_add_rule(makefile, scratch, "autogen");
+    scratch = chaz_Util_join(dir_sep, "autogen", "source",
+                             "testcfish_parcel.c", NULL);
+    rule = chaz_MakeFile_add_rule(makefile, scratch, "autogen");
     free(scratch);
     scratch = chaz_Util_join(dir_sep, "autogen", "source", "lucy_parcel.c",
                              NULL);

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/common/charmonizer.main
----------------------------------------------------------------------
diff --git a/common/charmonizer.main b/common/charmonizer.main
index d7236fb..539d0fb 100644
--- a/common/charmonizer.main
+++ b/common/charmonizer.main
@@ -91,6 +91,7 @@ S_add_compiler_flags(struct chaz_CLIArgs *args) {
     }
 
     chaz_CFlags_add_define(extra_cflags, "CFP_CFISH", NULL);
+    chaz_CFlags_add_define(extra_cflags, "CFP_TESTCFISH", NULL);
     chaz_CFlags_add_define(extra_cflags, "CFP_LUCY", NULL);
 }
 
@@ -247,6 +248,10 @@ S_write_makefile(struct chaz_CLIArgs *args) {
     chaz_MakeVar_append(var, scratch);
     free(scratch);
     scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
+                             "testcfish_parcel", obj_ext, NULL);
+    chaz_MakeVar_append(var, scratch);
+    free(scratch);
+    scratch = chaz_Util_join("", "autogen", dir_sep, "source", dir_sep,
                              "lucy_parcel", obj_ext, NULL);
     chaz_MakeVar_append(var, scratch);
     free(scratch);
@@ -289,6 +294,9 @@ S_write_makefile(struct chaz_CLIArgs *args) {
     scratch = chaz_Util_join(dir_sep, "autogen", "source", "cfish_parcel.c",
                              NULL);
     rule = chaz_MakeFile_add_rule(makefile, scratch, "autogen");
+    scratch = chaz_Util_join(dir_sep, "autogen", "source",
+                             "testcfish_parcel.c", NULL);
+    rule = chaz_MakeFile_add_rule(makefile, scratch, "autogen");
     free(scratch);
     scratch = chaz_Util_join(dir_sep, "autogen", "source", "lucy_parcel.c",
                              NULL);

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test.c b/core/Clownfish/Test.c
new file mode 100644
index 0000000..d569e55
--- /dev/null
+++ b/core/Clownfish/Test.c
@@ -0,0 +1,117 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * 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/Test/TestBatch.h"
+#include "Clownfish/Test/TestFormatter.h"
+#include "Clownfish/Test/TestRunner.h"
+#include "Clownfish/VArray.h"
+
+#include "Clownfish/Test/TestByteBuf.h"
+#include "Clownfish/Test/TestCharBuf.h"
+#include "Clownfish/Test/TestErr.h"
+#include "Clownfish/Test/TestHash.h"
+#include "Clownfish/Test/TestLockFreeRegistry.h"
+#include "Clownfish/Test/TestNum.h"
+#include "Clownfish/Test/TestObj.h"
+#include "Clownfish/Test/TestVArray.h"
+#include "Clownfish/Test/Util/TestAtomic.h"
+#include "Clownfish/Test/Util/TestMemory.h"
+#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");
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test.cfh b/core/Clownfish/Test.cfh
new file mode 100644
index 0000000..4ceb429
--- /dev/null
+++ b/core/Clownfish/Test.cfh
@@ -0,0 +1,42 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+/** Testing framework.
+ */
+inert class Clownfish::Test {
+    inert bool
+    run_batch(CharBuf *class_name, TestFormatter *formatter);
+
+    inert bool
+    run_all_batches(TestFormatter *formatter);
+}
+
+__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/a51ff172/core/Clownfish/Test/TestByteBuf.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestByteBuf.c b/core/Clownfish/Test/TestByteBuf.c
new file mode 100644
index 0000000..71329d7
--- /dev/null
+++ b/core/Clownfish/Test/TestByteBuf.c
@@ -0,0 +1,173 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestByteBuf.h"
+
+#include "Clownfish/ByteBuf.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Test/TestFormatter.h"
+#include "Clownfish/Test/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);
+}
+
+static void
+test_Equals(TestBatch *batch) {
+    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(batch, BB_Equals_Bytes(got, "foo", 4), "Equals_Bytes");
+    TEST_FALSE(batch, BB_Equals_Bytes(got, "foo", 3),
+               "Equals_Bytes spoiled by different size");
+    TEST_FALSE(batch, 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),
+               "Different size spoils Equals");
+    TEST_FALSE(batch, 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),
+                "same length");
+    TEST_FALSE(batch, BB_Equals(wanted, (Obj*)got),
+               "Different content spoils Equals");
+
+    DECREF(got);
+    DECREF(wanted);
+}
+
+static void
+test_Grow(TestBatch *batch) {
+    ByteBuf *bb = BB_new(1);
+    TEST_INT_EQ(batch, BB_Get_Capacity(bb), 8,
+                "Allocate in 8-byte increments");
+    BB_Grow(bb, 9);
+    TEST_INT_EQ(batch, BB_Get_Capacity(bb), 16,
+                "Grow in 8-byte increments");
+    DECREF(bb);
+}
+
+static void
+test_Clone(TestBatch *batch) {
+    ByteBuf *bb = BB_new_bytes("foo", 3);
+    ByteBuf *twin = BB_Clone(bb);
+    TEST_TRUE(batch, BB_Equals(bb, (Obj*)twin), "Clone");
+    DECREF(bb);
+    DECREF(twin);
+}
+
+static void
+test_compare(TestBatch *batch) {
+    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,
+                "BB_compare returns 0 for equal ByteBufs");
+
+    BB_Set_Size(a, 3);
+    TEST_TRUE(batch, 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,
+              "NULL doesn't interfere with BB_compare");
+
+    DECREF(a);
+    DECREF(b);
+}
+
+static void
+test_Mimic(TestBatch *batch) {
+    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");
+
+    BB_Mimic_Bytes(a, "bar", 4);
+    TEST_TRUE(batch, strcmp(BB_Get_Buf(a), "bar") == 0,
+              "Mimic_Bytes content");
+    TEST_INT_EQ(batch, BB_Get_Size(a), 4, "Mimic_Bytes size");
+
+    BB_Mimic(b, (Obj*)a);
+    TEST_TRUE(batch, BB_Equals(a, (Obj*)b), "Mimic");
+
+    DECREF(a);
+    DECREF(b);
+}
+
+static void
+test_Cat(TestBatch *batch) {
+    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");
+
+    BB_Mimic_Bytes(wanted, "foobarbaz", 9);
+    BB_Cat_Bytes(got, "baz", 3);
+    TEST_TRUE(batch, BB_Equals(wanted, (Obj*)got), "Cat_Bytes");
+
+    DECREF(scratch);
+    DECREF(got);
+    DECREF(wanted);
+}
+
+static void
+test_serialization(TestBatch *batch) {
+    ByteBuf *wanted = BB_new_bytes("foobar", 6);
+    ByteBuf *got    = (ByteBuf*)TestUtils_freeze_thaw((Obj*)wanted);
+    TEST_TRUE(batch, 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);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestByteBuf.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestByteBuf.cfh b/core/Clownfish/Test/TestByteBuf.cfh
new file mode 100644
index 0000000..fba607b
--- /dev/null
+++ b/core/Clownfish/Test/TestByteBuf.cfh
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestByteBuf cnick TestBB
+    inherits Clownfish::Test::TestBatch {
+
+    inert incremented TestByteBuf*
+    new(TestFormatter *formatter);
+
+    inert TestByteBuf*
+    init(TestByteBuf *self, TestFormatter *formatter);
+
+    void
+    Run_Tests(TestByteBuf *self);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestCharBuf.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestCharBuf.c b/core/Clownfish/Test/TestCharBuf.c
new file mode 100644
index 0000000..891731c
--- /dev/null
+++ b/core/Clownfish/Test/TestCharBuf.c
@@ -0,0 +1,442 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#define CHY_USE_SHORT_NAMES
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestCharBuf.h"
+
+#include "Clownfish/CharBuf.h"
+#include "Clownfish/Num.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Test/TestFormatter.h"
+#include "Clownfish/Test/TestUtils.h"
+#include "Clownfish/VTable.h"
+
+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);
+}
+
+static CharBuf*
+S_get_cb(const char *string) {
+    return CB_new_from_utf8(string, strlen(string));
+}
+
+static void
+test_Cat(TestBatch *batch) {
+    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");
+    DECREF(got);
+
+    got = S_get_cb("a");
+    CB_Cat_Char(got, 0x263A);
+    TEST_TRUE(batch, 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");
+    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");
+    DECREF(got);
+
+    DECREF(wanted);
+}
+
+static void
+test_Mimic_and_Clone(TestBatch *batch) {
+    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");
+    DECREF(got);
+
+    got = S_get_cb("bar");
+    CB_Mimic_Str(got, "foo", 3);
+    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Mimic_Str");
+    DECREF(got);
+
+    got = CB_Clone(wanted);
+    TEST_TRUE(batch, CB_Equals(wanted, (Obj*)got), "Clone");
+    DECREF(got);
+
+    DECREF(wanted);
+}
+
+static void
+test_Find(TestBatch *batch) {
+    CharBuf *string = CB_new(10);
+    CharBuf *substring = S_get_cb("foo");
+
+    TEST_TRUE(batch, CB_Find(string, substring) == -1, "Not in empty string");
+    CB_setf(string, "foo");
+    TEST_TRUE(batch, CB_Find(string, substring) == 0, "Find complete string");
+    CB_setf(string, "afoo");
+    TEST_TRUE(batch, CB_Find(string, substring) == 1, "Find after first");
+    CB_Set_Size(string, 3);
+    TEST_TRUE(batch, CB_Find(string, substring) == -1, "Don't overrun");
+    CB_setf(string, "afood");
+    TEST_TRUE(batch, CB_Find(string, substring) == 1, "Find in middle");
+
+    DECREF(substring);
+    DECREF(string);
+}
+
+static void
+test_Code_Point_At_and_From(TestBatch *batch) {
+    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);
+    uint32_t i;
+
+    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],
+                    "Code_Point_At %ld", (long)i);
+        TEST_INT_EQ(batch, CB_Code_Point_At(string, from),
+                    code_points[from], "Code_Point_From %ld", (long)from);
+    }
+
+    DECREF(string);
+}
+
+static void
+test_SubString(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+    DECREF(string);
+}
+
+static void
+test_Nip_and_Chop(TestBatch *batch) {
+    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");
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+
+static void
+test_Truncate(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_Trim(TestBatch *batch) {
+    uint32_t spaces[] = {
+        ' ',    '\t',   '\r',   '\n',   0x000B, 0x000C, 0x000D, 0x0085,
+        0x00A0, 0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004,
+        0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x2028, 0x2029,
+        0x202F, 0x205F, 0x3000
+    };
+    uint32_t num_spaces = sizeof(spaces) / sizeof(uint32_t);
+    uint32_t i;
+    CharBuf *got = CB_new(0);
+
+    // Surround a smiley with lots of whitespace.
+    for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
+    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),
+               "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),
+               "Trim_Tail returns false on failure");
+    TEST_TRUE(batch, CB_Equals_Str(got, smiley, smiley_len),
+              "Trim_Top and Trim_Tail worked");
+
+    // Build the spacey smiley again.
+    CB_Truncate(got, 0);
+    for (i = 0; i < num_spaces; i++) { CB_Cat_Char(got, spaces[i]); }
+    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),
+              "Trim worked");
+
+    DECREF(got);
+}
+
+static void
+test_To_F64(TestBatch *batch) {
+    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");
+
+    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");
+
+    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,
+              "TO_F64 doesn't run past end of string");
+
+    DECREF(charbuf);
+}
+
+static void
+test_To_I64(TestBatch *batch) {
+    CharBuf *charbuf = S_get_cb("10");
+    TEST_TRUE(batch, CB_To_I64(charbuf) == 10, "To_I64");
+    CB_setf(charbuf, "-10");
+    TEST_TRUE(batch, CB_To_I64(charbuf) == -10, "To_I64 negative");
+    DECREF(charbuf);
+}
+
+
+static void
+test_vcatf_s(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_null_string(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_cb(TestBatch *batch) {
+    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");
+    DECREF(catworthy);
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_obj(TestBatch *batch) {
+    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");
+    DECREF(i32);
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_null_obj(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_i8(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_i32(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_i64(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_u8(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_u32(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_u64(TestBatch *batch) {
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_f64(TestBatch *batch) {
+    CharBuf *wanted;
+    char buf[64];
+    float num = 1.3f;
+    CharBuf *got = S_get_cb("foo ");
+    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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_vcatf_x32(TestBatch *batch) {
+    CharBuf *wanted;
+    char buf[64];
+    unsigned long num = INT32_MAX;
+    CharBuf *got = S_get_cb("foo ");
+#if (SIZEOF_LONG == 4)
+    sprintf(buf, "foo bar %.8lx baz", num);
+#elif (SIZEOF_INT == 4)
+    sprintf(buf, "foo bar %.8x baz", (unsigned)num);
+#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");
+    DECREF(wanted);
+    DECREF(got);
+}
+
+static void
+test_serialization(TestBatch *batch) {
+    CharBuf *wanted = S_get_cb("foo");
+    CharBuf *got    = (CharBuf*)TestUtils_freeze_thaw((Obj*)wanted);
+    TEST_TRUE(batch, 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);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestCharBuf.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestCharBuf.cfh b/core/Clownfish/Test/TestCharBuf.cfh
new file mode 100644
index 0000000..1ecedcc
--- /dev/null
+++ b/core/Clownfish/Test/TestCharBuf.cfh
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestCharBuf cnick TestCB
+    inherits Clownfish::Test::TestBatch {
+
+    inert incremented TestCharBuf*
+    new(TestFormatter *formatter);
+
+    inert TestCharBuf*
+    init(TestCharBuf *self, TestFormatter *formatter);
+
+    void
+    Run_Tests(TestCharBuf *self);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestErr.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestErr.c b/core/Clownfish/Test/TestErr.c
new file mode 100644
index 0000000..c6b0869
--- /dev/null
+++ b/core/Clownfish/Test/TestErr.c
@@ -0,0 +1,56 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestErr.h"
+
+#include "Clownfish/CharBuf.h"
+#include "Clownfish/Err.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Test/TestFormatter.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);
+}
+
+static void
+test_To_String(TestBatch *batch) {
+    CharBuf *message = CB_newf("oops");
+    Err *error = Err_new(message);
+    CharBuf *string = Err_To_String(error);
+    TEST_TRUE(batch, 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);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestErr.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestErr.cfh b/core/Clownfish/Test/TestErr.cfh
new file mode 100644
index 0000000..1366e7e
--- /dev/null
+++ b/core/Clownfish/Test/TestErr.cfh
@@ -0,0 +1,31 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestErr
+    inherits Clownfish::Test::TestBatch {
+
+    inert incremented TestErr*
+    new(TestFormatter *formatter);
+
+    inert TestErr*
+    init(TestErr *self, TestFormatter *formatter);
+
+    void
+    Run_Tests(TestErr *self);
+}
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestHash.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestHash.c b/core/Clownfish/Test/TestHash.c
new file mode 100644
index 0000000..773a6f8
--- /dev/null
+++ b/core/Clownfish/Test/TestHash.c
@@ -0,0 +1,293 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <time.h>
+
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestHash.h"
+
+#include "Clownfish/CharBuf.h"
+#include "Clownfish/Hash.h"
+#include "Clownfish/Num.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Test/TestFormatter.h"
+#include "Clownfish/Test/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);
+}
+
+static void
+test_Equals(TestBatch *batch) {
+    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),
+              "Empty hashes are equal");
+
+    Hash_Store_Str(hash, "foo", 3, (Obj*)CFISH_TRUE);
+    TEST_FALSE(batch, 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),
+              "Add a matching pair and Equals returns true");
+
+    Hash_Store_Str(other, "foo", 3, INCREF(stuff));
+    TEST_FALSE(batch, Hash_Equals(hash, (Obj*)other),
+               "Non-matching value spoils Equals");
+
+    DECREF(hash);
+    DECREF(other);
+}
+
+static void
+test_Store_and_Fetch(TestBatch *batch) {
+    Hash          *hash         = Hash_new(100);
+    Hash          *dupe         = Hash_new(100);
+    const uint32_t starting_cap = Hash_Get_Capacity(hash);
+    VArray        *expected     = VA_new(100);
+    VArray        *got          = VA_new(100);
+    ZombieCharBuf *twenty       = ZCB_WRAP_STR("20", 2);
+    ZombieCharBuf *forty        = ZCB_WRAP_STR("40", 2);
+    ZombieCharBuf *foo          = ZCB_WRAP_STR("foo", 3);
+
+    for (int32_t i = 0; i < 100; i++) {
+        CharBuf *cb = CB_newf("%i32", i);
+        Hash_Store(hash, (Obj*)cb, (Obj*)cb);
+        Hash_Store(dupe, (Obj*)cb, INCREF(cb));
+        VA_Push(expected, INCREF(cb));
+    }
+    TEST_TRUE(batch, Hash_Equals(hash, (Obj*)dupe), "Equals");
+
+    TEST_INT_EQ(batch, Hash_Get_Capacity(hash), starting_cap,
+                "Initial capacity sufficient (no rebuilds)");
+
+    for (int32_t i = 0; i < 100; i++) {
+        Obj *key  = VA_Fetch(expected, i);
+        Obj *elem = Hash_Fetch(hash, key);
+        VA_Push(got, (Obj*)INCREF(elem));
+    }
+
+    TEST_TRUE(batch, VA_Equals(got, (Obj*)expected),
+              "basic Store and Fetch");
+    TEST_INT_EQ(batch, Hash_Get_Size(hash), 100,
+                "size incremented properly by Hash_Store");
+
+    TEST_TRUE(batch, 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)),
+              "Hash_Store replaces existing value");
+    TEST_FALSE(batch, Hash_Equals(hash, (Obj*)dupe),
+               "replacement value spoils equals");
+    TEST_INT_EQ(batch, Hash_Get_Size(hash), 100,
+                "size unaffected after value replaced");
+
+    TEST_TRUE(batch, Hash_Delete(hash, (Obj*)forty) == (Obj*)foo,
+              "Delete returns value");
+    DECREF(foo);
+    TEST_INT_EQ(batch, Hash_Get_Size(hash), 99,
+                "size decremented by successful Delete");
+    TEST_TRUE(batch, Hash_Delete(hash, (Obj*)forty) == NULL,
+              "Delete returns NULL when key not found");
+    TEST_INT_EQ(batch, 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");
+
+    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");
+
+    DECREF(hash);
+    DECREF(dupe);
+    DECREF(got);
+    DECREF(expected);
+}
+
+static void
+test_Keys_Values_Iter(TestBatch *batch) {
+    Hash     *hash     = Hash_new(0); // trigger multiple rebuilds.
+    VArray   *expected = VA_new(100);
+    VArray   *keys;
+    VArray   *values;
+
+    for (uint32_t i = 0; i < 500; i++) {
+        CharBuf *cb = CB_newf("%u32", i);
+        Hash_Store(hash, (Obj*)cb, (Obj*)cb);
+        VA_Push(expected, INCREF(cb));
+    }
+
+    VA_Sort(expected, NULL, NULL);
+
+    keys   = Hash_Keys(hash);
+    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");
+    VA_Clear(keys);
+    VA_Clear(values);
+
+    {
+        Obj *key;
+        Obj *value;
+        Hash_Iterate(hash);
+        while (Hash_Next(hash, &key, &value)) {
+            VA_Push(keys, INCREF(key));
+            VA_Push(values, INCREF(value));
+        }
+    }
+
+    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");
+
+    {
+        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");
+        key = Hash_Find_Key(hash, (Obj*)nope, ZCB_Hash_Sum(nope)),
+        TEST_TRUE(batch, key == NULL,
+                  "Find_Key returns NULL for non-existent key");
+    }
+
+    DECREF(hash);
+    DECREF(expected);
+    DECREF(keys);
+    DECREF(values);
+}
+
+static void
+test_Dump_and_Load(TestBatch *batch) {
+    Hash *hash = Hash_new(0);
+    Obj  *dump;
+    Hash *loaded;
+
+    Hash_Store_Str(hash, "foo", 3,
+                   (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),
+              "Dump => Load round trip");
+    DECREF(dump);
+    DECREF(loaded);
+
+    /* TODO: Fix Hash_Load().
+
+    Hash_Store_Str(hash, "_class", 6,
+        (Obj*)CB_new_from_trusted_utf8("not_a_class", 11));
+    dump = (Obj*)Hash_Dump(hash);
+    loaded = (Hash*)Obj_Load(dump, dump);
+
+    TEST_TRUE(batch, Hash_Equals(hash, (Obj*)loaded),
+              "Load still works with _class if it's not a real class");
+    DECREF(dump);
+    DECREF(loaded);
+
+    */
+
+    DECREF(hash);
+}
+
+static void
+test_serialization(TestBatch *batch) {
+    Hash  *wanted = Hash_new(0);
+    Hash  *got;
+
+    for (uint32_t i = 0; i < 10; i++) {
+        CharBuf *cb = TestUtils_random_string(rand() % 1200);
+        Integer32 *num = Int32_new(i);
+        Hash_Store(wanted, (Obj*)cb, (Obj*)num);
+        Hash_Store(wanted, (Obj*)num, (Obj*)cb);
+    }
+
+    got = (Hash*)TestUtils_freeze_thaw((Obj*)wanted);
+    TEST_TRUE(batch, got && Hash_Equals(wanted, (Obj*)got),
+              "Round trip through serialization.");
+
+    DECREF(got);
+    DECREF(wanted);
+}
+
+static void
+test_stress(TestBatch *batch) {
+    Hash     *hash     = Hash_new(0); // trigger multiple rebuilds.
+    VArray   *expected = VA_new(1000);
+    VArray   *keys;
+    VArray   *values;
+
+    for (uint32_t i = 0; i < 1000; i++) {
+        CharBuf *cb = TestUtils_random_string(rand() % 1200);
+        while (Hash_Fetch(hash, (Obj*)cb)) {
+            DECREF(cb);
+            cb = TestUtils_random_string(rand() % 1200);
+        }
+        Hash_Store(hash, (Obj*)cb, (Obj*)cb);
+        VA_Push(expected, INCREF(cb));
+    }
+
+    VA_Sort(expected, NULL, NULL);
+
+    // Overwrite for good measure.
+    for (uint32_t i = 0; i < 1000; i++) {
+        CharBuf *cb = (CharBuf*)VA_Fetch(expected, i);
+        Hash_Store(hash, (Obj*)cb, INCREF(cb));
+    }
+
+    keys   = Hash_Keys(hash);
+    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");
+
+    DECREF(keys);
+    DECREF(values);
+    DECREF(expected);
+    DECREF(hash);
+}
+
+void
+TestHash_run_tests(TestHash *self) {
+    TestBatch *batch = (TestBatch*)self;
+    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);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestHash.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestHash.cfh b/core/Clownfish/Test/TestHash.cfh
new file mode 100644
index 0000000..ed5e548
--- /dev/null
+++ b/core/Clownfish/Test/TestHash.cfh
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestHash
+    inherits Clownfish::Test::TestBatch {
+
+    inert incremented TestHash*
+    new(TestFormatter *formatter);
+
+    inert TestHash*
+    init(TestHash *self, TestFormatter *formatter);
+
+    void
+    Run_Tests(TestHash *self);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestLockFreeRegistry.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestLockFreeRegistry.c b/core/Clownfish/Test/TestLockFreeRegistry.c
new file mode 100644
index 0000000..035bd77
--- /dev/null
+++ b/core/Clownfish/Test/TestLockFreeRegistry.c
@@ -0,0 +1,89 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string.h>
+
+#define CHY_USE_SHORT_NAMES
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestLockFreeRegistry.h"
+
+#include "Clownfish/LockFreeRegistry.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Test/TestFormatter.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);
+}
+
+StupidHashCharBuf*
+StupidHashCharBuf_new(const char *text) {
+    return (StupidHashCharBuf*)CB_new_from_utf8(text, strlen(text));
+}
+
+int32_t
+StupidHashCharBuf_hash_sum(StupidHashCharBuf *self) {
+    UNUSED_VAR(self);
+    return 1;
+}
+
+static void
+test_all(TestBatch *batch) {
+    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),
+              "Register() returns true on success");
+    TEST_FALSE(batch,
+               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),
+              "Register() key with the same Hash_Sum but that isn't Equal");
+
+    TEST_TRUE(batch, LFReg_Fetch(registry, (Obj*)foo_dupe) == (Obj*)foo,
+              "Fetch()");
+    TEST_TRUE(batch, LFReg_Fetch(registry, (Obj*)bar) == (Obj*)bar,
+              "Fetch() again");
+    TEST_TRUE(batch, LFReg_Fetch(registry, (Obj*)baz) == NULL,
+              "Fetch() non-existent key returns NULL");
+
+    DECREF(foo_dupe);
+    DECREF(baz);
+    DECREF(bar);
+    DECREF(foo);
+    DECREF(registry);
+}
+
+void
+TestLFReg_run_tests(TestLockFreeRegistry *self) {
+    TestBatch *batch = (TestBatch*)self;
+    test_all(batch);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestLockFreeRegistry.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestLockFreeRegistry.cfh b/core/Clownfish/Test/TestLockFreeRegistry.cfh
new file mode 100644
index 0000000..b2c7d36
--- /dev/null
+++ b/core/Clownfish/Test/TestLockFreeRegistry.cfh
@@ -0,0 +1,43 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestLockFreeRegistry cnick TestLFReg
+    inherits Clownfish::Test::TestBatch {
+
+    inert incremented TestLockFreeRegistry*
+    new(TestFormatter *formatter);
+
+    inert TestLockFreeRegistry*
+    init(TestLockFreeRegistry *self, TestFormatter *formatter);
+
+    void
+    Run_Tests(TestLockFreeRegistry *self);
+}
+
+/** Private test-only class for stressing LockFreeRegistry.
+ */
+class Lucy::Test::Object::StupidHashCharBuf inherits Clownfish::CharBuf {
+    inert incremented StupidHashCharBuf*
+    new(const char *text);
+
+    /** Always returns 1, guaranteeing collisions. */
+    public int32_t
+    Hash_Sum(StupidHashCharBuf *self);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestNum.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestNum.c b/core/Clownfish/Test/TestNum.c
new file mode 100644
index 0000000..1f01a61
--- /dev/null
+++ b/core/Clownfish/Test/TestNum.c
@@ -0,0 +1,325 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestNum.h"
+
+#include "Clownfish/CharBuf.h"
+#include "Clownfish/Num.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Test/TestFormatter.h"
+#include "Clownfish/Test/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);
+}
+
+static void
+test_To_String(TestBatch *batch) {
+    Float32   *f32 = Float32_new(1.33f);
+    Float64   *f64 = Float64_new(1.33);
+    Integer32 *i32 = Int32_new(INT32_MAX);
+    Integer64 *i64 = Int64_new(INT64_MAX);
+    CharBuf *f32_string = Float32_To_String(f32);
+    CharBuf *f64_string = Float64_To_String(f64);
+    CharBuf *i32_string = Int32_To_String(i32);
+    CharBuf *i64_string = Int64_To_String(i64);
+    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),
+              "Float32_To_String");
+    TEST_TRUE(batch, CB_Starts_With_Str(f64_string, "1.3", 3),
+              "Float64_To_String");
+    TEST_TRUE(batch, CB_Equals_Str(i32_string, "2147483647", 10),
+              "Int32_To_String");
+    TEST_TRUE(batch, CB_Equals_Str(i64_string, "9223372036854775807", 19),
+              "Int64_To_String");
+    TEST_TRUE(batch, CB_Equals_Str(true_string, "true", 4),
+              "Bool_To_String [true]");
+    TEST_TRUE(batch, CB_Equals_Str(false_string, "false", 5),
+              "Bool_To_String [false]");
+
+    DECREF(false_string);
+    DECREF(true_string);
+    DECREF(i64_string);
+    DECREF(i32_string);
+    DECREF(f64_string);
+    DECREF(f32_string);
+    DECREF(i64);
+    DECREF(i32);
+    DECREF(f64);
+    DECREF(f32);
+}
+
+static void
+test_accessors(TestBatch *batch) {
+    Float32   *f32 = Float32_new(1.0);
+    Float64   *f64 = Float64_new(1.0);
+    Integer32 *i32 = Int32_new(1);
+    Integer64 *i64 = Int64_new(1);
+    float  wanted32 = 1.33f;
+    double wanted64 = 1.33;
+    float  got32;
+    double got64;
+
+    Float32_Set_Value(f32, 1.33f);
+    TEST_FLOAT_EQ(batch, 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,
+              "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");
+
+    got32 = (float)Float32_To_F64(f32);
+    TEST_TRUE(batch, *(int32_t*)&got32 == *(int32_t*)&wanted32,
+              "Float32_To_F64");
+
+    got64 = Float64_To_F64(f64);
+    TEST_TRUE(batch, *(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,
+                "I32 Set_Value Get_Value");
+
+    Int64_Set_Value(i64, INT64_MIN);
+    TEST_TRUE(batch, 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_INT_EQ(batch, Bool_Get_Value(CFISH_TRUE), true,
+                "Bool_Get_Value [true]");
+    TEST_INT_EQ(batch, Bool_Get_Value(CFISH_FALSE), false,
+                "Bool_Get_Value [false]");
+    TEST_TRUE(batch, Bool_To_I64(CFISH_TRUE) == true,
+              "Bool_To_I64 [true]");
+    TEST_TRUE(batch, Bool_To_I64(CFISH_FALSE) == false,
+              "Bool_To_I64 [false]");
+    TEST_TRUE(batch, Bool_To_F64(CFISH_TRUE) == 1.0,
+              "Bool_To_F64 [true]");
+    TEST_TRUE(batch, Bool_To_F64(CFISH_FALSE) == 0.0,
+              "Bool_To_F64 [false]");
+
+    DECREF(i64);
+    DECREF(i32);
+    DECREF(f64);
+    DECREF(f32);
+}
+
+static void
+test_Equals_and_Compare_To(TestBatch *batch) {
+    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,
+              "F32_Compare_To equal");
+    TEST_TRUE(batch, Float32_Equals(f32, (Obj*)f64),
+              "F32_Equals equal");
+
+    Float64_Set_Value(f64, 2.0);
+    TEST_TRUE(batch, Float32_Compare_To(f32, (Obj*)f64) < 0,
+              "F32_Compare_To less than");
+    TEST_FALSE(batch, 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,
+              "F32_Compare_To greater than");
+    TEST_FALSE(batch, 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,
+              "F64_Compare_To equal");
+    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f32),
+              "F64_Equals equal");
+
+    Float32_Set_Value(f32, 2.0);
+    TEST_TRUE(batch, Float64_Compare_To(f64, (Obj*)f32) < 0,
+              "F64_Compare_To less than");
+    TEST_FALSE(batch, 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,
+              "F64_Compare_To greater than");
+    TEST_FALSE(batch, 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,
+              "Float64 comparison to Integer64");
+    TEST_TRUE(batch, 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,
+              "Float32 comparison to Integer32");
+    TEST_TRUE(batch, 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,
+              "Integer64 comparison to same number");
+
+    TEST_TRUE(batch, Bool_Equals(CFISH_TRUE, (Obj*)CFISH_TRUE),
+              "CFISH_TRUE Equals itself");
+    TEST_TRUE(batch, Bool_Equals(CFISH_FALSE, (Obj*)CFISH_FALSE),
+              "CFISH_FALSE Equals itself");
+    TEST_FALSE(batch, Bool_Equals(CFISH_FALSE, (Obj*)CFISH_TRUE),
+               "CFISH_FALSE not Equals CFISH_TRUE ");
+    TEST_FALSE(batch, Bool_Equals(CFISH_TRUE, (Obj*)CFISH_FALSE),
+               "CFISH_TRUE not Equals CFISH_FALSE ");
+    TEST_FALSE(batch, Bool_Equals(CFISH_TRUE, (Obj*)CHARBUF),
+               "CFISH_TRUE not Equals random other object ");
+
+    DECREF(i64_copy);
+    DECREF(i64);
+    DECREF(i32);
+    DECREF(f64);
+    DECREF(f32);
+}
+
+static void
+test_Clone(TestBatch *batch) {
+    Float32   *f32 = Float32_new(1.33f);
+    Float64   *f64 = Float64_new(1.33);
+    Integer32 *i32 = Int32_new(INT32_MAX);
+    Integer64 *i64 = Int64_new(INT64_MAX);
+    Float32   *f32_dupe = Float32_Clone(f32);
+    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),
+              "Float32 Clone");
+    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f64_dupe),
+              "Float64 Clone");
+    TEST_TRUE(batch, Int32_Equals(i32, (Obj*)i32_dupe),
+              "Integer32 Clone");
+    TEST_TRUE(batch, Int64_Equals(i64, (Obj*)i64_dupe),
+              "Integer64 Clone");
+    TEST_TRUE(batch, Bool_Equals(CFISH_TRUE, (Obj*)Bool_Clone(CFISH_TRUE)),
+              "BoolNum Clone");
+    DECREF(i64_dupe);
+    DECREF(i32_dupe);
+    DECREF(f64_dupe);
+    DECREF(f32_dupe);
+    DECREF(i64);
+    DECREF(i32);
+    DECREF(f64);
+    DECREF(f32);
+}
+
+static void
+test_Mimic(TestBatch *batch) {
+    Float32   *f32 = Float32_new(1.33f);
+    Float64   *f64 = Float64_new(1.33);
+    Integer32 *i32 = Int32_new(INT32_MAX);
+    Integer64 *i64 = Int64_new(INT64_MAX);
+    Float32   *f32_dupe = Float32_new(0.0f);
+    Float64   *f64_dupe = Float64_new(0.0);
+    Integer32 *i32_dupe = Int32_new(0);
+    Integer64 *i64_dupe = Int64_new(0);
+    Float32_Mimic(f32_dupe, (Obj*)f32);
+    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),
+              "Float32 Mimic");
+    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f64_dupe),
+              "Float64 Mimic");
+    TEST_TRUE(batch, Int32_Equals(i32, (Obj*)i32_dupe),
+              "Integer32 Mimic");
+    TEST_TRUE(batch, Int64_Equals(i64, (Obj*)i64_dupe),
+              "Integer64 Mimic");
+    DECREF(i64_dupe);
+    DECREF(i32_dupe);
+    DECREF(f64_dupe);
+    DECREF(f32_dupe);
+    DECREF(i64);
+    DECREF(i32);
+    DECREF(f64);
+    DECREF(f32);
+}
+
+static void
+test_serialization(TestBatch *batch) {
+    Float32   *f32 = Float32_new(1.33f);
+    Float64   *f64 = Float64_new(1.33);
+    Integer32 *i32 = Int32_new(-1);
+    Integer64 *i64 = Int64_new(-1);
+    Float32   *f32_thaw = (Float32*)TestUtils_freeze_thaw((Obj*)f32);
+    Float64   *f64_thaw = (Float64*)TestUtils_freeze_thaw((Obj*)f64);
+    Integer32 *i32_thaw = (Integer32*)TestUtils_freeze_thaw((Obj*)i32);
+    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),
+              "Float32 freeze/thaw");
+    TEST_TRUE(batch, Float64_Equals(f64, (Obj*)f64_thaw),
+              "Float64 freeze/thaw");
+    TEST_TRUE(batch, Int32_Equals(i32, (Obj*)i32_thaw),
+              "Integer32 freeze/thaw");
+    TEST_TRUE(batch, Int64_Equals(i64, (Obj*)i64_thaw),
+              "Integer64 freeze/thaw");
+    TEST_TRUE(batch, Bool_Equals(CFISH_TRUE, (Obj*)true_thaw),
+              "BoolNum freeze/thaw");
+
+    DECREF(i64_thaw);
+    DECREF(i32_thaw);
+    DECREF(f64_thaw);
+    DECREF(f32_thaw);
+    DECREF(i64);
+    DECREF(i32);
+    DECREF(f64);
+    DECREF(f32);
+}
+
+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);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestNum.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestNum.cfh b/core/Clownfish/Test/TestNum.cfh
new file mode 100644
index 0000000..b6d1035
--- /dev/null
+++ b/core/Clownfish/Test/TestNum.cfh
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestNum
+    inherits Clownfish::Test::TestBatch {
+
+    inert incremented TestNum*
+    new(TestFormatter *formatter);
+
+    inert TestNum*
+    init(TestNum *self, TestFormatter *formatter);
+
+    void
+    Run_Tests(TestNum *self);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestObj.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestObj.c b/core/Clownfish/Test/TestObj.c
new file mode 100644
index 0000000..fa0a80b
--- /dev/null
+++ b/core/Clownfish/Test/TestObj.c
@@ -0,0 +1,216 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * 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/TestObj.h"
+
+#include "Clownfish/CharBuf.h"
+#include "Clownfish/Err.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Test/TestFormatter.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);
+}
+
+static Obj*
+S_new_testobj() {
+    ZombieCharBuf *klass = ZCB_WRAP_STR("TestObj", 7);
+    Obj *obj;
+    VTable *vtable = VTable_fetch_vtable((CharBuf*)klass);
+    if (!vtable) {
+        vtable = VTable_singleton((CharBuf*)klass, OBJ);
+    }
+    obj = VTable_Make_Obj(vtable);
+    return Obj_init(obj);
+}
+
+static void
+test_refcounts(TestBatch *batch) {
+    Obj *obj = S_new_testobj();
+
+    TEST_INT_EQ(batch, Obj_Get_RefCount(obj), 1,
+                "Correct starting refcount");
+
+    Obj_Inc_RefCount(obj);
+    TEST_INT_EQ(batch, Obj_Get_RefCount(obj), 2, "Inc_RefCount");
+
+    Obj_Dec_RefCount(obj);
+    TEST_INT_EQ(batch, Obj_Get_RefCount(obj), 1, "Dec_RefCount");
+
+    DECREF(obj);
+}
+
+static void
+test_To_String(TestBatch *batch) {
+    Obj *testobj = S_new_testobj();
+    CharBuf *string = Obj_To_String(testobj);
+    ZombieCharBuf *temp = ZCB_WRAP(string);
+    while (ZCB_Get_Size(temp)) {
+        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");
+    DECREF(string);
+    DECREF(testobj);
+}
+
+static void
+test_Dump(TestBatch *batch) {
+    Obj *testobj = S_new_testobj();
+    CharBuf *string = Obj_To_String(testobj);
+    Obj *dump = Obj_Dump(testobj);
+    TEST_TRUE(batch, Obj_Equals(dump, (Obj*)string),
+              "Default Dump returns To_String");
+    DECREF(dump);
+    DECREF(string);
+    DECREF(testobj);
+}
+
+static void
+test_Equals(TestBatch *batch) {
+    Obj *testobj = S_new_testobj();
+    Obj *other   = S_new_testobj();
+
+    TEST_TRUE(batch, Obj_Equals(testobj, testobj),
+              "Equals is true for the same object");
+    TEST_FALSE(batch, Obj_Equals(testobj, other),
+               "Distinct objects are not equal");
+
+    DECREF(testobj);
+    DECREF(other);
+}
+
+static void
+test_Hash_Sum(TestBatch *batch) {
+    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),
+              "Hash_Sum uses memory address");
+    DECREF(testobj);
+}
+
+static void
+test_Is_A(TestBatch *batch) {
+    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),
+              "Get_Class_Name");
+
+    DECREF(charbuf);
+}
+
+static void
+S_attempt_init(void *context) {
+    Obj_init((Obj*)context);
+}
+
+static void
+S_attempt_Clone(void *context) {
+    Obj_Clone((Obj*)context);
+}
+
+static void
+S_attempt_Make(void *context) {
+    Obj_Make((Obj*)context);
+}
+
+static void
+S_attempt_Compare_To(void *context) {
+    Obj_Compare_To((Obj*)context, (Obj*)context);
+}
+
+static void
+S_attempt_To_I64(void *context) {
+    Obj_To_I64((Obj*)context);
+}
+
+static void
+S_attempt_To_F64(void *context) {
+    Obj_To_F64((Obj*)context);
+}
+
+static void
+S_attempt_Load(void *context) {
+    Obj_Load((Obj*)context, (Obj*)context);
+}
+
+static void
+S_attempt_Mimic(void *context) {
+    Obj_Mimic((Obj*)context, (Obj*)context);
+}
+
+static void
+S_verify_abstract_error(TestBatch *batch, 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
+              && Err_Is_A(error, ERR) 
+              && CB_Find_Str(Err_Get_Mess(error), "bstract", 7) != -1,
+              message);
+    DECREF(error);
+}
+
+static void
+test_abstract_routines(TestBatch *batch) {
+    Obj *blank = VTable_Make_Obj(OBJ);
+    S_verify_abstract_error(batch, 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");
+    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);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestObj.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestObj.cfh b/core/Clownfish/Test/TestObj.cfh
new file mode 100644
index 0000000..3c41f2b
--- /dev/null
+++ b/core/Clownfish/Test/TestObj.cfh
@@ -0,0 +1,32 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel TestClownfish;
+
+class Clownfish::Test::TestObj
+    inherits Clownfish::Test::TestBatch {
+
+    inert incremented TestObj*
+    new(TestFormatter *formatter);
+
+    inert TestObj*
+    init(TestObj *self, TestFormatter *formatter);
+
+    void
+    Run_Tests(TestObj *self);
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestUtils.c
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestUtils.c b/core/Clownfish/Test/TestUtils.c
new file mode 100644
index 0000000..0f7b72a
--- /dev/null
+++ b/core/Clownfish/Test/TestUtils.c
@@ -0,0 +1,140 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#define CHY_USE_SHORT_NAMES
+#define CFISH_USE_SHORT_NAMES
+#define LUCY_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestUtils.h"
+
+#include "Clownfish/CharBuf.h"
+#include "Clownfish/Util/Memory.h"
+#include "Lucy/Store/InStream.h"
+#include "Lucy/Store/OutStream.h"
+#include "Lucy/Store/RAMFile.h"
+#include "Lucy/Util/Freezer.h"
+
+uint64_t
+TestUtils_random_u64() {
+    uint64_t num = ((uint64_t)(rand()   & 0x7FFF) << 60)
+                   | ((uint64_t)(rand() & 0x7FFF) << 45)
+                   | ((uint64_t)(rand() & 0x7FFF) << 30)
+                   | ((uint64_t)(rand() & 0x7FFF) << 15)
+                   | ((uint64_t)(rand() & 0x7FFF) << 0);
+    return num;
+}
+
+int64_t*
+TestUtils_random_i64s(int64_t *buf, size_t count, int64_t min,
+                      int64_t limit) {
+    uint64_t  range = min < limit ? limit - min : 0;
+    int64_t *ints = buf ? buf : (int64_t*)CALLOCATE(count, sizeof(int64_t));
+    for (size_t i = 0; i < count; i++) {
+        ints[i] = min + TestUtils_random_u64() % range;
+    }
+    return ints;
+}
+
+uint64_t*
+TestUtils_random_u64s(uint64_t *buf, size_t count, uint64_t min,
+                      uint64_t limit) {
+    uint64_t  range = min < limit ? limit - min : 0;
+    uint64_t *ints = buf ? buf : (uint64_t*)CALLOCATE(count, sizeof(uint64_t));
+    for (size_t i = 0; i < count; i++) {
+        ints[i] = min + TestUtils_random_u64() % range;
+    }
+    return ints;
+}
+
+double*
+TestUtils_random_f64s(double *buf, size_t count) {
+    double *f64s = buf ? buf : (double*)CALLOCATE(count, sizeof(double));
+    for (size_t i = 0; i < count; i++) {
+        uint64_t num = TestUtils_random_u64();
+        f64s[i] = U64_TO_DOUBLE(num) / UINT64_MAX;
+    }
+    return f64s;
+}
+
+static uint32_t
+S_random_code_point(void) {
+    uint32_t code_point = 0;
+    while (1) {
+        uint8_t chance = (rand() % 9) + 1;
+        switch (chance) {
+            case 1: case 2: case 3:
+                code_point = rand() % 0x80;
+                break;
+            case 4: case 5: case 6:
+                code_point = (rand() % (0x0800  - 0x0080)) + 0x0080;
+                break;
+            case 7: case 8:
+                code_point = (rand() % (0x10000 - 0x0800)) + 0x0800;
+                break;
+            case 9: {
+                    uint64_t num = TestUtils_random_u64();
+                    code_point = (num % (0x10FFFF - 0x10000)) + 0x10000;
+                }
+        }
+        if (code_point > 0x10FFFF) {
+            continue; // Too high.
+        }
+        if (code_point > 0xD7FF && code_point < 0xE000) {
+            continue; // UTF-16 surrogate.
+        }
+        break;
+    }
+    return code_point;
+}
+
+CharBuf*
+TestUtils_random_string(size_t length) {
+    CharBuf *string = CB_new(length);
+    while (length--) {
+        CB_Cat_Char(string, S_random_code_point());
+    }
+    return string;
+}
+
+CharBuf*
+TestUtils_get_cb(const char *ptr) {
+    return CB_new_from_utf8(ptr, strlen(ptr));
+}
+
+Obj*
+TestUtils_freeze_thaw(Obj *object) {
+    if (object) {
+        RAMFile *ram_file = RAMFile_new(NULL, false);
+        OutStream *outstream = OutStream_open((Obj*)ram_file);
+        FREEZE(object, outstream);
+        OutStream_Close(outstream);
+        DECREF(outstream);
+
+        InStream *instream = InStream_open((Obj*)ram_file);
+        Obj *retval = THAW(instream);
+        DECREF(instream);
+        DECREF(ram_file);
+        return retval;
+    }
+    else {
+        return NULL;
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/lucy/blob/a51ff172/core/Clownfish/Test/TestUtils.cfh
----------------------------------------------------------------------
diff --git a/core/Clownfish/Test/TestUtils.cfh b/core/Clownfish/Test/TestUtils.cfh
new file mode 100644
index 0000000..5c88ec4
--- /dev/null
+++ b/core/Clownfish/Test/TestUtils.cfh
@@ -0,0 +1,71 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+parcel Clownfish;
+
+inert class Clownfish::Test::TestUtils  {
+
+    /** Testing-only CharBuf factory which uses strlen().
+     */
+    inert incremented CharBuf*
+    get_cb(const char *utf8);
+
+    /** Return a random unsigned 64-bit integer.
+     */
+    inert uint64_t
+    random_u64();
+
+    /** Return an array of <code>count</code> random 64-bit integers where
+     * <code>min <= n < limit</code>.
+     *
+     * If <code>buf</code> is NULL, it will be allocated, otherwise it will
+     * be used.
+     */
+    inert int64_t*
+    random_i64s(int64_t *buf, size_t count, int64_t min, int64_t limit);
+
+    /** Return an array of <code>count</code> random unsigned, 64-bit integers
+     * where <code>min <= n < limit</code>.
+     *
+     * If <code>buf</code> is NULL, it will be allocated, otherwise it will
+     * be used.
+     */
+    inert uint64_t*
+    random_u64s(uint64_t *buf, size_t count, uint64_t min, uint64_t limit);
+
+    /** Return an array of <code>count</code> random double-precision floating
+     * point numbers between 0 and 1.
+     *
+     * If <code>buf</code> is NULL, it will be allocated, otherwise it will
+     * be used.
+     */
+    inert double*
+    random_f64s(double *buf, size_t count);
+
+    /** Return a string with a random (legal) sequence of code points.
+     * @param length Length of the string in code points.
+     */
+    inert incremented CharBuf*
+    random_string(size_t length);
+
+    /** Return the result of round-tripping the object through FREEZE and
+     * THAW.
+     */
+    inert incremented Obj*
+    freeze_thaw(Obj *object);
+}
+
+