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 2016/07/11 11:25:39 UTC
[06/14] lucy-clownfish git commit: Move tests to separate directory
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestClass.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestClass.c b/runtime/test/Clownfish/Test/TestClass.c
new file mode 100644
index 0000000..a6646f8
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestClass.c
@@ -0,0 +1,158 @@
+/* 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 C_CFISH_BOOLEAN
+#define C_CFISH_CLASS
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "charmony.h"
+
+#include <string.h>
+
+#include "Clownfish/Test/TestClass.h"
+
+#include "Clownfish/Boolean.h"
+#include "Clownfish/Class.h"
+#include "Clownfish/Method.h"
+#include "Clownfish/String.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/Util/Memory.h"
+#include "Clownfish/Vector.h"
+
+TestClass*
+TestClass_new() {
+ return (TestClass*)Class_Make_Obj(TESTCLASS);
+}
+
+#if DEBUG_CLASS_CONTENTS
+
+#include <stdio.h>
+
+static void
+S_memdump(void *vptr, size_t size) {
+ unsigned char *ptr = (unsigned char*)vptr;
+ for (size_t i = 0; i < size; i++) {
+ printf("%02X ", ptr[i]);
+ }
+ printf("\n");
+}
+
+#endif /* DEBUG_CLASS_CONTENTS */
+
+static void
+test_bootstrap_idempotence(TestBatchRunner *runner) {
+ Class *bool_class = BOOLEAN;
+ uint32_t bool_class_size = BOOLEAN->class_alloc_size;
+ uint32_t bool_ivars_offset = cfish_Bool_IVARS_OFFSET;
+ Boolean *true_singleton = Bool_true_singleton;
+
+ char *bool_class_contents = (char*)MALLOCATE(bool_class_size);
+ memcpy(bool_class_contents, BOOLEAN, bool_class_size);
+
+ // Force another bootstrap run.
+ cfish_bootstrap_internal(1);
+
+#if DEBUG_CLASS_CONTENTS
+ printf("Before\n");
+ S_memdump(bool_class_contents, bool_class_size);
+ printf("After\n");
+ S_memdump(BOOLEAN, bool_class_size);
+#endif
+
+ TEST_TRUE(runner, bool_class == BOOLEAN,
+ "Boolean class pointer unchanged");
+ TEST_TRUE(runner,
+ memcmp(bool_class_contents, BOOLEAN, bool_class_size) == 0,
+ "Boolean class unchanged");
+ TEST_TRUE(runner, bool_ivars_offset == cfish_Bool_IVARS_OFFSET,
+ "Boolean ivars offset unchanged");
+ TEST_TRUE(runner, true_singleton == Bool_true_singleton,
+ "Boolean singleton unchanged");
+
+ FREEMEM(bool_class_contents);
+}
+
+static String*
+MyObj_To_String_IMP(Obj *self) {
+ UNUSED_VAR(self);
+ return Str_newf("delta");
+}
+
+static void
+test_simple_subclass(TestBatchRunner *runner) {
+ String *class_name = SSTR_WRAP_C("Clownfish::Test::MyObj");
+ Class *subclass = Class_singleton(class_name, OBJ);
+
+ TEST_TRUE(runner, Str_Equals(Class_Get_Name(subclass), (Obj*)class_name),
+ "Get_Name");
+ TEST_TRUE(runner, Class_Get_Parent(subclass) == OBJ, "Get_Parent");
+
+ Obj *obj = Class_Make_Obj(subclass);
+ TEST_TRUE(runner, Obj_is_a(obj, subclass), "Make_Obj");
+
+ Class_Override(subclass, (cfish_method_t)MyObj_To_String_IMP,
+ CFISH_Obj_To_String_OFFSET);
+ String *str = Obj_To_String(obj);
+ TEST_TRUE(runner, Str_Equals_Utf8(str, "delta", 5), "Override");
+ DECREF(str);
+
+ DECREF(obj);
+}
+
+static void
+test_add_alias_to_registry(TestBatchRunner *runner) {
+ static const char alias[] = "Clownfish::Test::ObjAlias";
+ bool added;
+
+ added = Class_add_alias_to_registry(OBJ, alias, sizeof(alias) - 1);
+ TEST_TRUE(runner, added, "add_alias_to_registry returns true");
+ Class *klass = Class_fetch_class(SSTR_WRAP_C(alias));
+ TEST_TRUE(runner, klass == OBJ, "add_alias_to_registry works");
+
+ added = Class_add_alias_to_registry(CLASS, alias, sizeof(alias) - 1);
+ TEST_FALSE(runner, added, "add_alias_to_registry returns false");
+}
+
+static void
+test_Get_Methods(TestBatchRunner *runner) {
+ Vector *methods = Class_Get_Methods(OBJ);
+ Method *destroy = NULL;
+
+ for (size_t i = 0, size = Vec_Get_Size(methods); i < size; i++) {
+ Method *method = (Method*)Vec_Fetch(methods, i);
+
+ if (Str_Equals_Utf8(Method_Get_Name(method), "Destroy", 7)) {
+ destroy = method;
+ }
+ }
+
+ TEST_TRUE(runner, destroy != NULL, "Destroy method found");
+
+ DECREF(methods);
+}
+
+void
+TestClass_Run_IMP(TestClass *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 12);
+ test_bootstrap_idempotence(runner);
+ test_simple_subclass(runner);
+ test_add_alias_to_registry(runner);
+ test_Get_Methods(runner);
+}
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestClass.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestClass.cfh b/runtime/test/Clownfish/Test/TestClass.cfh
new file mode 100644
index 0000000..615c34e
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestClass.cfh
@@ -0,0 +1,28 @@
+/* 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::TestClass
+ inherits Clownfish::TestHarness::TestBatch {
+
+ inert incremented TestClass*
+ new();
+
+ void
+ Run(TestClass *self, TestBatchRunner *runner);
+}
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestErr.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestErr.c b/runtime/test/Clownfish/Test/TestErr.c
new file mode 100644
index 0000000..46ecb1e
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestErr.c
@@ -0,0 +1,193 @@
+/* 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/String.h"
+#include "Clownfish/Err.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Clownfish/Class.h"
+
+TestErr*
+TestErr_new() {
+ return (TestErr*)Class_Make_Obj(TESTERR);
+}
+
+static void
+test_To_String(TestBatchRunner *runner) {
+ String *message = Str_newf("oops");
+ Err *error = Err_new(message);
+ String *string = Err_To_String(error);
+ TEST_TRUE(runner, Str_Equals(message, (Obj*)string),
+ "Stringifies as message");
+ DECREF(string);
+ DECREF(error);
+}
+
+static void
+test_Cat_Mess(TestBatchRunner *runner) {
+ Err *error = Err_new(Str_newf("alpha"));
+ Err_Cat_Mess(error, SSTR_WRAP_C("\nbeta"));
+ String *mess = Err_Get_Mess(error);
+ TEST_TRUE(runner, Str_Equals_Utf8(mess, "alpha\nbeta", 10), "Cat_Mess");
+ DECREF(error);
+}
+
+static void
+test_Add_Frame(TestBatchRunner *runner) {
+ {
+ Err *error = Err_new(Str_newf("alpha"));
+ Err_Add_Frame(error, "source.c", 128, "function");
+ String *mess = Err_Get_Mess(error);
+ const char *expected = "alpha\n\tfunction at source.c line 128\n";
+ TEST_TRUE(runner, Str_Equals_Utf8(mess, expected, strlen(expected)),
+ "Add_Frame");
+ DECREF(error);
+ }
+
+ {
+ Err *error = Err_new(Str_newf("alpha\n"));
+ Err_Add_Frame(error, "source.c", 128, "function");
+ String *mess = Err_Get_Mess(error);
+ const char *expected = "alpha\n\tfunction at source.c line 128\n";
+ TEST_TRUE(runner, Str_Equals_Utf8(mess, expected, strlen(expected)),
+ "Add_Frame with trailing newline");
+ DECREF(error);
+ }
+
+ {
+ Err *error = Err_new(Str_newf("alpha"));
+ Err_Add_Frame(error, "source.c", 128, NULL);
+ String *mess = Err_Get_Mess(error);
+ const char *expected = "alpha\n\tat source.c line 128\n";
+ TEST_TRUE(runner, Str_Equals_Utf8(mess, expected, strlen(expected)),
+ "Add_Frame without func");
+ DECREF(error);
+ }
+}
+
+static void
+S_rethrow(void *context) {
+ Err *error = (Err*)context;
+ Err_rethrow(error, "src.c", 12, "fn");
+}
+
+static void
+test_rethrow(TestBatchRunner *runner) {
+ Err *error = Err_new(Str_newf("error"));
+ Err *rethrown = Err_trap(S_rethrow, error);
+ String *mess = Err_Get_Mess(rethrown);
+ const char *expected = "error\n\tfn at src.c line 12\n";
+ TEST_TRUE(runner, Str_Starts_With_Utf8(mess, expected, strlen(expected)),
+ "rethrow");
+ DECREF(error);
+}
+
+static void
+S_invalid_downcast(void *context) {
+ Obj *obj = (Obj*)context;
+ DOWNCAST(obj, ERR);
+}
+
+static void
+test_downcast(TestBatchRunner *runner) {
+ Obj *obj = (Obj*)Str_newf("gamma");
+
+ TEST_TRUE(runner, DOWNCAST(obj, STRING) != NULL, "downcast");
+
+ TEST_TRUE(runner, DOWNCAST(NULL, STRING) == NULL, "downcast NULL");
+
+ Err *error = Err_trap(S_invalid_downcast, obj);
+ TEST_TRUE(runner, error != NULL, "downcast throws");
+ DECREF(error);
+
+ DECREF(obj);
+}
+
+static void
+S_invalid_certify(void *context) {
+ Obj *obj = (Obj*)context;
+ CERTIFY(obj, ERR);
+}
+
+static void
+test_certify(TestBatchRunner *runner) {
+ Obj *obj = (Obj*)Str_newf("epsilon");
+ Err *error;
+
+ TEST_TRUE(runner, CERTIFY(obj, STRING) != NULL, "certify");
+
+ error = Err_trap(S_invalid_certify, NULL);
+ TEST_TRUE(runner, error != NULL, "certify NULL");
+ DECREF(error);
+
+ error = Err_trap(S_invalid_certify, obj);
+ TEST_TRUE(runner, error != NULL, "certify throws");
+ DECREF(error);
+
+ DECREF(obj);
+}
+
+static void
+S_err_thread(void *arg) {
+ TestBatchRunner *runner = (TestBatchRunner*)arg;
+
+ TEST_TRUE(runner, Err_get_error() == NULL,
+ "global error in thread initialized to null");
+
+ Err_set_error(Err_new(Str_newf("thread")));
+ String *mess = Err_Get_Mess(Err_get_error());
+ TEST_TRUE(runner, Str_Equals_Utf8(mess, "thread", 6),
+ "set_error in thread works");
+}
+
+static void
+test_threads(TestBatchRunner *runner) {
+ if (!TestUtils_has_threads) {
+ SKIP(runner, 3, "no thread support");
+ return;
+ }
+
+ Err_set_error(Err_new(Str_newf("main")));
+
+ void *runtime = TestUtils_clone_host_runtime();
+ Thread *thread = TestUtils_thread_create(S_err_thread, runner, runtime);
+ TestUtils_thread_join(thread);
+ TestUtils_destroy_host_runtime(runtime);
+
+ String *mess = Err_Get_Mess(Err_get_error());
+ TEST_TRUE(runner, Str_Equals_Utf8(mess, "main", 4),
+ "thread doesn't clobber global error");
+}
+
+void
+TestErr_Run_IMP(TestErr *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 15);
+ test_To_String(runner);
+ test_Cat_Mess(runner);
+ test_Add_Frame(runner);
+ test_rethrow(runner);
+ test_downcast(runner);
+ test_certify(runner);
+ test_threads(runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestErr.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestErr.cfh b/runtime/test/Clownfish/Test/TestErr.cfh
new file mode 100644
index 0000000..178017c
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestErr.cfh
@@ -0,0 +1,28 @@
+/* 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::TestHarness::TestBatch {
+
+ inert incremented TestErr*
+ new();
+
+ void
+ Run(TestErr *self, TestBatchRunner *runner);
+}
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestHash.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestHash.c b/runtime/test/Clownfish/Test/TestHash.c
new file mode 100644
index 0000000..b00d891
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestHash.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.
+ */
+
+#include <stdlib.h>
+#include <time.h>
+
+#define CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+#define C_CFISH_HASH
+
+#include "Clownfish/Test/TestHash.h"
+
+#include "Clownfish/String.h"
+#include "Clownfish/Boolean.h"
+#include "Clownfish/Hash.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Clownfish/Vector.h"
+#include "Clownfish/Class.h"
+
+TestHash*
+TestHash_new() {
+ return (TestHash*)Class_Make_Obj(TESTHASH);
+}
+
+static void
+test_Equals(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0);
+ Hash *other = Hash_new(0);
+ String *stuff = SSTR_WRAP_C("stuff");
+
+ TEST_TRUE(runner, Hash_Equals(hash, (Obj*)other),
+ "Empty hashes are equal");
+
+ Hash_Store_Utf8(hash, "foo", 3, (Obj*)CFISH_TRUE);
+ TEST_FALSE(runner, Hash_Equals(hash, (Obj*)other),
+ "Add one pair and Equals returns false");
+
+ Hash_Store_Utf8(other, "foo", 3, (Obj*)CFISH_TRUE);
+ TEST_TRUE(runner, Hash_Equals(hash, (Obj*)other),
+ "Add a matching pair and Equals returns true");
+
+ Hash_Store_Utf8(other, "foo", 3, INCREF(stuff));
+ TEST_FALSE(runner, Hash_Equals(hash, (Obj*)other),
+ "Non-matching value spoils Equals");
+
+ DECREF(hash);
+ DECREF(other);
+}
+
+static void
+test_Store_and_Fetch(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(100);
+ Hash *dupe = Hash_new(100);
+ const size_t starting_cap = Hash_Get_Capacity(hash);
+ Vector *expected = Vec_new(100);
+ Vector *got = Vec_new(100);
+ String *twenty = SSTR_WRAP_C("20");
+ String *forty = SSTR_WRAP_C("40");
+ String *foo = SSTR_WRAP_C("foo");
+
+ for (int32_t i = 0; i < 100; i++) {
+ String *str = Str_newf("%i32", i);
+ Hash_Store(hash, str, (Obj*)str);
+ Hash_Store(dupe, str, INCREF(str));
+ Vec_Push(expected, INCREF(str));
+ }
+ TEST_TRUE(runner, Hash_Equals(hash, (Obj*)dupe), "Equals");
+
+ TEST_UINT_EQ(runner, Hash_Get_Capacity(hash), starting_cap,
+ "Initial capacity sufficient (no rebuilds)");
+
+ for (size_t i = 0; i < 100; i++) {
+ String *key = (String*)Vec_Fetch(expected, i);
+ Obj *elem = Hash_Fetch(hash, key);
+ Vec_Push(got, (Obj*)INCREF(elem));
+ }
+
+ TEST_TRUE(runner, Vec_Equals(got, (Obj*)expected),
+ "basic Store and Fetch");
+ TEST_UINT_EQ(runner, Hash_Get_Size(hash), 100,
+ "size incremented properly by Hash_Store");
+
+ TEST_TRUE(runner, Hash_Fetch(hash, foo) == NULL,
+ "Fetch against non-existent key returns NULL");
+
+ String *twelve = (String*)Hash_Fetch_Utf8(hash, "12", 2);
+ TEST_TRUE(runner, Str_Equals_Utf8(twelve, "12", 2), "Fetch_Utf8");
+
+ Obj *stored_foo = INCREF(foo);
+ Hash_Store(hash, forty, stored_foo);
+ TEST_TRUE(runner, Str_Equals(foo, Hash_Fetch(hash, forty)),
+ "Hash_Store replaces existing value");
+ TEST_FALSE(runner, Hash_Equals(hash, (Obj*)dupe),
+ "replacement value spoils equals");
+ TEST_UINT_EQ(runner, Hash_Get_Size(hash), 100,
+ "size unaffected after value replaced");
+
+ TEST_TRUE(runner, Hash_Delete(hash, forty) == stored_foo,
+ "Delete returns value");
+ DECREF(stored_foo);
+ TEST_UINT_EQ(runner, Hash_Get_Size(hash), 99,
+ "size decremented by successful Delete");
+ TEST_TRUE(runner, Hash_Delete(hash, forty) == NULL,
+ "Delete returns NULL when key not found");
+ TEST_UINT_EQ(runner, Hash_Get_Size(hash), 99,
+ "size not decremented by unsuccessful Delete");
+ DECREF(Hash_Delete(dupe, forty));
+ TEST_TRUE(runner, Vec_Equals(got, (Obj*)expected), "Equals after Delete");
+
+ Obj *forty_one = Hash_Delete_Utf8(hash, "41", 2);
+ TEST_TRUE(runner, forty_one != NULL, "Delete_Utf8");
+ TEST_UINT_EQ(runner, Hash_Get_Size(hash), 98,
+ "Delete_Utf8 decrements size");
+ DECREF(forty_one);
+
+ Hash_Clear(hash);
+ TEST_TRUE(runner, Hash_Fetch(hash, twenty) == NULL, "Clear");
+ TEST_TRUE(runner, Hash_Get_Size(hash) == 0, "size is 0 after Clear");
+
+ Hash_Clear(hash);
+ Hash_Store(hash, forty, NULL);
+ TEST_TRUE(runner, Hash_Fetch(hash, forty) == NULL, "Store NULL");
+ TEST_TRUE(runner, Hash_Get_Size(hash) == 1, "Size after Store NULL");
+ TEST_TRUE(runner, Hash_Delete(hash, forty) == NULL, "Delete NULL value");
+ TEST_TRUE(runner, Hash_Get_Size(hash) == 0,
+ "Size after Deleting NULL val");
+
+ DECREF(hash);
+ DECREF(dupe);
+ DECREF(got);
+ DECREF(expected);
+}
+
+static void
+test_Keys_Values(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0); // trigger multiple rebuilds.
+ Vector *expected = Vec_new(100);
+ Vector *keys;
+ Vector *values;
+
+ for (uint32_t i = 0; i < 500; i++) {
+ String *str = Str_newf("%u32", i);
+ Hash_Store(hash, str, (Obj*)str);
+ Vec_Push(expected, INCREF(str));
+ }
+
+ Vec_Sort(expected);
+
+ keys = Hash_Keys(hash);
+ values = Hash_Values(hash);
+ Vec_Sort(keys);
+ Vec_Sort(values);
+ TEST_TRUE(runner, Vec_Equals(keys, (Obj*)expected), "Keys");
+ TEST_TRUE(runner, Vec_Equals(values, (Obj*)expected), "Values");
+ Vec_Clear(keys);
+ Vec_Clear(values);
+
+ {
+ String *forty = SSTR_WRAP_C("40");
+ String *nope = SSTR_WRAP_C("nope");
+ TEST_TRUE(runner, Hash_Has_Key(hash, forty), "Has_Key");
+ TEST_FALSE(runner, Hash_Has_Key(hash, nope),
+ "Has_Key returns false for non-existent key");
+ }
+
+ DECREF(hash);
+ DECREF(expected);
+ DECREF(keys);
+ DECREF(values);
+}
+
+static void
+test_stress(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0); // trigger multiple rebuilds.
+ Vector *expected = Vec_new(1000);
+ Vector *keys;
+ Vector *values;
+
+ for (uint32_t i = 0; i < 1000; i++) {
+ String *str = TestUtils_random_string((size_t)(rand() % 1200));
+ while (Hash_Fetch(hash, str)) {
+ DECREF(str);
+ str = TestUtils_random_string((size_t)(rand() % 1200));
+ }
+ Hash_Store(hash, str, (Obj*)str);
+ Vec_Push(expected, INCREF(str));
+ }
+
+ Vec_Sort(expected);
+
+ // Overwrite for good measure.
+ for (uint32_t i = 0; i < 1000; i++) {
+ String *str = (String*)Vec_Fetch(expected, i);
+ Hash_Store(hash, str, INCREF(str));
+ }
+
+ keys = Hash_Keys(hash);
+ values = Hash_Values(hash);
+ Vec_Sort(keys);
+ Vec_Sort(values);
+ TEST_TRUE(runner, Vec_Equals(keys, (Obj*)expected), "stress Keys");
+ TEST_TRUE(runner, Vec_Equals(values, (Obj*)expected), "stress Values");
+
+ DECREF(keys);
+ DECREF(values);
+ DECREF(expected);
+ DECREF(hash);
+}
+
+static void
+test_collision(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0);
+ String *one = Str_newf("A");
+ String *two = Str_newf("P{2}|=~-ULE/d");
+
+ TEST_TRUE(runner, Str_Hash_Sum(one) == Str_Hash_Sum(two),
+ "Keys have the same hash sum");
+
+ Hash_Store(hash, one, INCREF(one));
+ Hash_Store(hash, two, INCREF(two));
+ String *elem = (String*)Hash_Fetch(hash, two);
+ TEST_TRUE(runner, elem == two, "Fetch works with collisions");
+
+ DECREF(one);
+ DECREF(two);
+ DECREF(hash);
+}
+
+static void
+test_store_skips_tombstone(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0);
+ size_t mask = Hash_Get_Capacity(hash) - 1;
+
+ String *one = Str_newf("one");
+ size_t slot = Str_Hash_Sum(one) & mask;
+
+ // Find a colliding key.
+ String *two = NULL;
+ for (int i = 0; i < 100000; i++) {
+ two = Str_newf("%i32", i);
+ if (slot == (Str_Hash_Sum(two) & mask)) {
+ break;
+ }
+ DECREF(two);
+ two = NULL;
+ }
+
+ Hash_Store(hash, one, (Obj*)CFISH_TRUE);
+ Hash_Store(hash, two, (Obj*)CFISH_TRUE);
+ Hash_Delete(hash, one);
+ Hash_Store(hash, two, (Obj*)CFISH_TRUE);
+
+ TEST_UINT_EQ(runner, Hash_Get_Size(hash), 1, "Store skips tombstone");
+
+ DECREF(one);
+ DECREF(two);
+ DECREF(hash);
+}
+
+static void
+test_threshold_accounting(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(20);
+ String *key = Str_newf("key");
+
+ size_t threshold = hash->threshold;
+ Hash_Store(hash, key, (Obj*)CFISH_TRUE);
+ Hash_Delete(hash, key);
+ TEST_UINT_EQ(runner, hash->threshold, threshold - 1,
+ "Tombstone creation decreases threshold");
+
+ Hash_Store(hash, key, (Obj*)CFISH_TRUE);
+ TEST_UINT_EQ(runner, hash->threshold, threshold,
+ "Tombstone destruction increases threshold");
+
+ DECREF(key);
+ DECREF(hash);
+}
+
+static void
+test_tombstone_identification(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(20);
+ String *key = Str_newf("P{2}|=~-U@!y>");
+
+ // Tombstones have a zero hash_sum.
+ TEST_UINT_EQ(runner, Str_Hash_Sum(key), 0, "Key has zero hash sum");
+
+ Hash_Store(hash, key, (Obj*)CFISH_TRUE);
+ Hash_Delete(hash, key);
+ TEST_TRUE(runner, Hash_Fetch(hash, key) == NULL,
+ "Key with zero hash sum isn't mistaken for tombstone");
+
+ DECREF(key);
+ DECREF(hash);
+}
+
+void
+TestHash_Run_IMP(TestHash *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 39);
+ srand((unsigned int)time((time_t*)NULL));
+ test_Equals(runner);
+ test_Store_and_Fetch(runner);
+ test_Keys_Values(runner);
+ test_stress(runner);
+ test_collision(runner);
+ test_store_skips_tombstone(runner);
+ test_threshold_accounting(runner);
+ test_tombstone_identification(runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestHash.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestHash.cfh b/runtime/test/Clownfish/Test/TestHash.cfh
new file mode 100644
index 0000000..a730105
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestHash.cfh
@@ -0,0 +1,29 @@
+/* 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::TestHarness::TestBatch {
+
+ inert incremented TestHash*
+ new();
+
+ void
+ Run(TestHash *self, TestBatchRunner *runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestHashIterator.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestHashIterator.c b/runtime/test/Clownfish/Test/TestHashIterator.c
new file mode 100644
index 0000000..fac7d69
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestHashIterator.c
@@ -0,0 +1,253 @@
+/* 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/TestHashIterator.h"
+
+#include "Clownfish/Err.h"
+#include "Clownfish/String.h"
+#include "Clownfish/Hash.h"
+#include "Clownfish/HashIterator.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/Vector.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Clownfish/Class.h"
+
+TestHashIterator*
+TestHashIterator_new() {
+ return (TestHashIterator*)Class_Make_Obj(TESTHASHITERATOR);
+}
+
+static void
+test_Next(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0); // trigger multiple rebuilds.
+ Vector *expected = Vec_new(100);
+ Vector *keys = Vec_new(500);
+ Vector *values = Vec_new(500);
+
+ for (uint32_t i = 0; i < 500; i++) {
+ String *str = Str_newf("%u32", i);
+ Hash_Store(hash, str, (Obj*)str);
+ Vec_Push(expected, INCREF(str));
+ }
+
+ Vec_Sort(expected);
+
+ {
+ HashIterator *iter = HashIter_new(hash);
+ while (HashIter_Next(iter)) {
+ String *key = HashIter_Get_Key(iter);
+ Obj *value = HashIter_Get_Value(iter);
+ Vec_Push(keys, INCREF(key));
+ Vec_Push(values, INCREF(value));
+ }
+ TEST_TRUE(runner, !HashIter_Next(iter),
+ "Next continues to return false after iteration finishes.");
+
+ DECREF(iter);
+ }
+
+ Vec_Sort(keys);
+ Vec_Sort(values);
+ TEST_TRUE(runner, Vec_Equals(keys, (Obj*)expected), "Keys from Iter");
+ TEST_TRUE(runner, Vec_Equals(values, (Obj*)expected), "Values from Iter");
+
+ DECREF(hash);
+ DECREF(expected);
+ DECREF(keys);
+ DECREF(values);
+}
+
+static void
+S_invoke_Next(void *context) {
+ HashIterator *iter = (HashIterator*)context;
+ HashIter_Next(iter);
+}
+
+static void
+S_invoke_Get_Key(void *context) {
+ HashIterator *iter = (HashIterator*)context;
+ HashIter_Get_Key(iter);
+}
+
+static void
+S_invoke_Get_Value(void *context) {
+ HashIterator *iter = (HashIterator*)context;
+ HashIter_Get_Value(iter);
+}
+
+static void
+test_empty(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0);
+ HashIterator *iter = HashIter_new(hash);
+
+ TEST_TRUE(runner, !HashIter_Next(iter),
+ "First call to next false on empty hash iteration");
+
+ Err *get_key_error = Err_trap(S_invoke_Get_Key, iter);
+ TEST_TRUE(runner, get_key_error != NULL,
+ "Get_Key throws exception on empty hash.");
+ DECREF(get_key_error);
+
+ Err *get_value_error = Err_trap(S_invoke_Get_Value, iter);
+ TEST_TRUE(runner, get_value_error != NULL,
+ "Get_Value throws exception on empty hash.");
+ DECREF(get_value_error);
+
+ DECREF(hash);
+ DECREF(iter);
+}
+
+static void
+test_Get_Key_and_Get_Value(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0);
+ String *str = Str_newf("foo");
+ Hash_Store(hash, str, (Obj*)str);
+ bool ok;
+
+ HashIterator *iter = HashIter_new(hash);
+ DECREF(hash);
+
+ Err *get_key_error = Err_trap(S_invoke_Get_Key, iter);
+ TEST_TRUE(runner, get_key_error != NULL,
+ "Get_Key throws exception before first call to Next.");
+ ok = Str_Contains_Utf8(Err_Get_Mess(get_key_error), "before", 6);
+ TEST_TRUE(runner, ok, "Get_Key before Next throws correct message");
+ DECREF(get_key_error);
+
+ Err *get_value_error = Err_trap(S_invoke_Get_Value, iter);
+ TEST_TRUE(runner, get_value_error != NULL,
+ "Get_Value throws exception before first call to Next.");
+ ok = Str_Contains_Utf8(Err_Get_Mess(get_value_error), "before", 6);
+ TEST_TRUE(runner, ok, "Get_Value before Next throws correct message");
+ DECREF(get_value_error);
+
+ HashIter_Next(iter);
+ TEST_TRUE(runner, HashIter_Get_Key(iter) != NULL,
+ "Get_Key during iteration.");
+ TEST_TRUE(runner, HashIter_Get_Value(iter) != NULL,
+ "Get_Value during iteration.");
+
+ HashIter_Next(iter);
+ get_key_error = Err_trap(S_invoke_Get_Key, iter);
+ TEST_TRUE(runner, get_key_error != NULL,
+ "Get_Key throws exception after end of iteration.");
+ ok = Str_Contains_Utf8(Err_Get_Mess(get_key_error), "after", 5);
+ TEST_TRUE(runner, ok, "Get_Key after end throws correct message");
+ DECREF(get_key_error);
+
+ get_value_error = Err_trap(S_invoke_Get_Value, iter);
+ TEST_TRUE(runner, get_value_error != NULL,
+ "Get_Value throws exception after end of iteration.");
+ ok = Str_Contains_Utf8(Err_Get_Mess(get_value_error), "after", 5);
+ TEST_TRUE(runner, ok, "Get_Value after end throws correct message");
+ DECREF(get_value_error);
+
+
+ DECREF(iter);
+}
+
+static void
+test_illegal_modification(TestBatchRunner *runner) {
+ Hash *hash = Hash_new(0);
+
+ for (uint32_t i = 0; i < 3; i++) {
+ String *str = Str_newf("%u32", i);
+ Hash_Store(hash, str, (Obj*)str);
+ }
+
+ HashIterator *iter = HashIter_new(hash);
+ HashIter_Next(iter);
+
+ for (uint32_t i = 0; i < 100; i++) {
+ String *str = Str_newf("foo %u32", i);
+ Hash_Store(hash, str, (Obj*)str);
+ }
+
+ Err *next_error = Err_trap(S_invoke_Next, iter);
+ TEST_TRUE(runner, next_error != NULL,
+ "Next on resized hash throws exception.");
+ DECREF(next_error);
+
+ Err *get_key_error = Err_trap(S_invoke_Get_Key, iter);
+ TEST_TRUE(runner, get_key_error != NULL,
+ "Get_Key on resized hash throws exception.");
+ DECREF(get_key_error);
+
+ Err *get_value_error = Err_trap(S_invoke_Get_Value, iter);
+ TEST_TRUE(runner, get_value_error != NULL,
+ "Get_Value on resized hash throws exception.");
+ DECREF(get_value_error);
+
+ DECREF(hash);
+ DECREF(iter);
+}
+
+static void
+test_tombstone(TestBatchRunner *runner) {
+ {
+ Hash *hash = Hash_new(0);
+ String *str = Str_newf("foo");
+ Hash_Store(hash, str, INCREF(str));
+ DECREF(Hash_Delete(hash, str));
+ DECREF(str);
+
+ HashIterator *iter = HashIter_new(hash);
+ TEST_TRUE(runner, !HashIter_Next(iter), "Next advances past tombstones.");
+
+ DECREF(iter);
+ DECREF(hash);
+ }
+
+ {
+ Hash *hash = Hash_new(0);
+ String *str = Str_newf("foo");
+ Hash_Store(hash, str, INCREF(str));
+
+ HashIterator *iter = HashIter_new(hash);
+ HashIter_Next(iter);
+ DECREF(Hash_Delete(hash, str));
+
+
+ Err *get_key_error = Err_trap(S_invoke_Get_Key, iter);
+ TEST_TRUE(runner, get_key_error != NULL,
+ "Get_Key doesn't return tombstone and throws error.");
+ DECREF(get_key_error);
+
+ DECREF(str);
+ DECREF(iter);
+ DECREF(hash);
+ }
+}
+
+void
+TestHashIterator_Run_IMP(TestHashIterator *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 21);
+ srand((unsigned int)time((time_t*)NULL));
+ test_Next(runner);
+ test_empty(runner);
+ test_Get_Key_and_Get_Value(runner);
+ test_illegal_modification(runner);
+ test_tombstone(runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestHashIterator.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestHashIterator.cfh b/runtime/test/Clownfish/Test/TestHashIterator.cfh
new file mode 100644
index 0000000..4765f76
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestHashIterator.cfh
@@ -0,0 +1,29 @@
+/* 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::TestHashIterator
+ inherits Clownfish::TestHarness::TestBatch {
+
+ inert incremented TestHashIterator*
+ new();
+
+ void
+ Run(TestHashIterator *self, TestBatchRunner *runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestHost.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestHost.c b/runtime/test/Clownfish/Test/TestHost.c
new file mode 100644
index 0000000..6da0efa
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestHost.c
@@ -0,0 +1,125 @@
+/* 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/TestHost.h"
+#include "Clownfish/Class.h"
+#include "Clownfish/String.h"
+
+TestHost*
+TestHost_new() {
+ return (TestHost*)Class_Make_Obj(TESTHOST);
+}
+
+Obj*
+TestHost_Test_Obj_Pos_Arg_IMP(TestHost *self, Obj *arg) {
+ UNUSED_VAR(self);
+ return arg;
+}
+
+Obj*
+TestHost_Test_Obj_Pos_Arg_Def_IMP(TestHost *self, Obj *arg) {
+ UNUSED_VAR(self);
+ return arg;
+}
+
+Obj*
+TestHost_Test_Obj_Label_Arg_IMP(TestHost *self, Obj *arg, bool unused) {
+ UNUSED_VAR(self);
+ UNUSED_VAR(unused);
+ return arg;
+}
+
+Obj*
+TestHost_Test_Obj_Label_Arg_Def_IMP(TestHost *self, Obj *arg, bool unused) {
+ UNUSED_VAR(self);
+ UNUSED_VAR(unused);
+ return arg;
+}
+
+int32_t
+TestHost_Test_Int32_Pos_Arg_IMP(TestHost *self, int32_t arg) {
+ UNUSED_VAR(self);
+ return arg;
+}
+
+int32_t
+TestHost_Test_Int32_Pos_Arg_Def_IMP(TestHost *self, int32_t arg) {
+ UNUSED_VAR(self);
+ return arg;
+}
+
+int32_t
+TestHost_Test_Int32_Label_Arg_IMP(TestHost *self, int32_t arg, bool unused) {
+ UNUSED_VAR(self);
+ UNUSED_VAR(unused);
+ return arg;
+}
+
+int32_t
+TestHost_Test_Int32_Label_Arg_Def_IMP(TestHost *self, int32_t arg,
+ bool unused) {
+ UNUSED_VAR(self);
+ UNUSED_VAR(unused);
+ return arg;
+}
+
+bool
+TestHost_Test_Bool_Pos_Arg_IMP(TestHost *self, bool arg) {
+ UNUSED_VAR(self);
+ return arg;
+}
+
+bool
+TestHost_Test_Bool_Pos_Arg_Def_IMP(TestHost *self, bool arg) {
+ UNUSED_VAR(self);
+ return arg;
+}
+
+bool
+TestHost_Test_Bool_Label_Arg_IMP(TestHost *self, bool arg, bool unused) {
+ UNUSED_VAR(self);
+ UNUSED_VAR(unused);
+ return arg;
+}
+
+bool
+TestHost_Test_Bool_Label_Arg_Def_IMP(TestHost *self, bool arg, bool unused) {
+ UNUSED_VAR(self);
+ UNUSED_VAR(unused);
+ return arg;
+}
+
+void
+TestHost_Invoke_Invalid_Callback_From_C_IMP(TestHost *self) {
+ TestHost_Invalid_Callback(self);
+}
+
+String*
+TestHost_Aliased_IMP(TestHost *self) {
+ UNUSED_VAR(self);
+ return Str_newf("C");
+}
+
+String*
+TestHost_Invoke_Aliased_From_C_IMP(TestHost *self) {
+ UNUSED_VAR(self);
+ return TestHost_Aliased(self);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestHost.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestHost.cfh b/runtime/test/Clownfish/Test/TestHost.cfh
new file mode 100644
index 0000000..92bc272
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestHost.cfh
@@ -0,0 +1,81 @@
+/* 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;
+
+/** Clownfish test suite.
+ */
+class Clownfish::Test::TestHost {
+ inert incremented TestHost*
+ new();
+
+ Obj*
+ Test_Obj_Pos_Arg(TestHost *self, Obj *arg);
+
+ Obj*
+ Test_Obj_Pos_Arg_Def(TestHost *self, nullable Obj *arg = NULL);
+
+ Obj*
+ Test_Obj_Label_Arg(TestHost *self, Obj *arg, bool unused = false);
+
+ Obj*
+ Test_Obj_Label_Arg_Def(TestHost *self, nullable Obj *arg = NULL,
+ bool unused = false);
+
+ int32_t
+ Test_Int32_Pos_Arg(TestHost *self, int32_t arg);
+
+ int32_t
+ Test_Int32_Pos_Arg_Def(TestHost *self, int32_t arg = 101);
+
+ int32_t
+ Test_Int32_Label_Arg(TestHost *self, int32_t arg, bool unused = false);
+
+ int32_t
+ Test_Int32_Label_Arg_Def(TestHost *self, int32_t arg = 101,
+ bool unused = false);
+
+ bool
+ Test_Bool_Pos_Arg(TestHost *self, bool arg);
+
+ bool
+ Test_Bool_Pos_Arg_Def(TestHost *self, bool arg = true);
+
+ bool
+ Test_Bool_Label_Arg(TestHost *self, bool arg, bool unused = false);
+
+ bool
+ Test_Bool_Label_Arg_Def(TestHost *self, bool arg = true,
+ bool unused = false);
+
+ /** A method that can't be overridden from the host language.
+ */
+ abstract void*
+ Invalid_Callback(TestHost *self);
+
+ void
+ Invoke_Invalid_Callback_From_C(TestHost *self);
+
+ /** A method with a custom host language alias.
+ */
+ incremented String*
+ Aliased(TestHost *self);
+
+ incremented String*
+ Invoke_Aliased_From_C(TestHost* self);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestLockFreeRegistry.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestLockFreeRegistry.c b/runtime/test/Clownfish/Test/TestLockFreeRegistry.c
new file mode 100644
index 0000000..b70ff1f
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestLockFreeRegistry.c
@@ -0,0 +1,168 @@
+/* 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 CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "Clownfish/Test/TestLockFreeRegistry.h"
+
+#include "Clownfish/Class.h"
+#include "Clownfish/LockFreeRegistry.h"
+#include "Clownfish/String.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Clownfish/Util/Memory.h"
+
+#define NUM_THREADS 5
+
+typedef struct ThreadArgs {
+ LockFreeRegistry *registry;
+ uint32_t *nums;
+ uint32_t num_objs;
+ uint64_t target_time;
+ uint32_t succeeded;
+} ThreadArgs;
+
+TestLockFreeRegistry*
+TestLFReg_new() {
+ return (TestLockFreeRegistry*)Class_Make_Obj(TESTLOCKFREEREGISTRY);
+}
+
+static void
+test_all(TestBatchRunner *runner) {
+ LockFreeRegistry *registry = LFReg_new(1);
+ String *foo = Str_newf("foo");
+ String *bar = Str_newf("bar");
+ String *baz = Str_newf("baz");
+ String *foo_dupe = Str_newf("foo");
+
+ TEST_TRUE(runner, LFReg_register(registry, foo, (Obj*)foo),
+ "Register() returns true on success");
+ TEST_FALSE(runner,
+ LFReg_register(registry, foo_dupe, (Obj*)foo_dupe),
+ "Can't Register() keys that test equal");
+
+ TEST_TRUE(runner, LFReg_register(registry, bar, (Obj*)bar),
+ "Register() key with the same Hash_Sum but that isn't Equal");
+
+ TEST_TRUE(runner, LFReg_fetch(registry, foo_dupe) == (Obj*)foo,
+ "Fetch()");
+ TEST_TRUE(runner, LFReg_fetch(registry, bar) == (Obj*)bar,
+ "Fetch() again");
+ TEST_TRUE(runner, LFReg_fetch(registry, baz) == NULL,
+ "Fetch() non-existent key returns NULL");
+
+ DECREF(foo_dupe);
+ DECREF(baz);
+ DECREF(bar);
+ DECREF(foo);
+ LFReg_destroy(registry);
+}
+
+static void
+S_register_many(void *varg) {
+ ThreadArgs *args = (ThreadArgs*)varg;
+
+ // Encourage contention, so that all threads try to register at the same
+ // time.
+
+ // Sleep until target_time.
+ uint64_t time = TestUtils_time();
+ if (args->target_time > time) {
+ TestUtils_usleep(args->target_time - time);
+ }
+
+ TestUtils_thread_yield();
+
+ uint32_t succeeded = 0;
+ for (uint32_t i = 0; i < args->num_objs; i++) {
+ String *obj = Str_newf("%u32", args->nums[i]);
+ if (LFReg_register(args->registry, obj, (Obj*)obj)) {
+ succeeded++;
+ }
+ DECREF(obj);
+ }
+
+ args->succeeded = succeeded;
+}
+
+static void
+test_threads(TestBatchRunner *runner) {
+ if (!TestUtils_has_threads) {
+ SKIP(runner, 1, "No thread support");
+ return;
+ }
+
+ LockFreeRegistry *registry = LFReg_new(32);
+ ThreadArgs thread_args[NUM_THREADS];
+ uint32_t num_objs = 10000;
+
+ for (uint32_t i = 0; i < NUM_THREADS; i++) {
+ uint32_t *nums = (uint32_t*)MALLOCATE(num_objs * sizeof(uint32_t));
+
+ for (uint32_t j = 0; j < num_objs; j++) {
+ nums[j] = j;
+ }
+
+ // Fisher-Yates shuffle.
+ for (uint32_t j = num_objs - 1; j > 0; j--) {
+ uint32_t r = (uint32_t)TestUtils_random_u64() % (j + 1);
+ uint32_t tmp = nums[j];
+ nums[j] = nums[r];
+ nums[r] = tmp;
+ }
+
+ thread_args[i].registry = registry;
+ thread_args[i].nums = nums;
+ thread_args[i].num_objs = num_objs;
+ }
+
+ Thread *threads[NUM_THREADS];
+ uint64_t target_time = TestUtils_time() + 200 * 1000;
+
+ for (uint32_t i = 0; i < NUM_THREADS; i++) {
+ thread_args[i].target_time = target_time;
+ threads[i]
+ = TestUtils_thread_create(S_register_many, &thread_args[i], NULL);
+ }
+
+ uint32_t total_succeeded = 0;
+
+ for (uint32_t i = 0; i < NUM_THREADS; i++) {
+ TestUtils_thread_join(threads[i]);
+ total_succeeded += thread_args[i].succeeded;
+ FREEMEM(thread_args[i].nums);
+ }
+
+ TEST_INT_EQ(runner, total_succeeded, num_objs,
+ "registered exactly the right number of entries across all"
+ " threads");
+
+ LFReg_destroy(registry);
+}
+
+void
+TestLFReg_Run_IMP(TestLockFreeRegistry *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 7);
+ test_all(runner);
+ test_threads(runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestLockFreeRegistry.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestLockFreeRegistry.cfh b/runtime/test/Clownfish/Test/TestLockFreeRegistry.cfh
new file mode 100644
index 0000000..784f745
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestLockFreeRegistry.cfh
@@ -0,0 +1,29 @@
+/* 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 nickname TestLFReg
+ inherits Clownfish::TestHarness::TestBatch {
+
+ inert incremented TestLockFreeRegistry*
+ new();
+
+ void
+ Run(TestLockFreeRegistry *self, TestBatchRunner *runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestMethod.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestMethod.c b/runtime/test/Clownfish/Test/TestMethod.c
new file mode 100644
index 0000000..7c39759
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestMethod.c
@@ -0,0 +1,91 @@
+/* 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/TestMethod.h"
+
+#include "Clownfish/Err.h"
+#include "Clownfish/Method.h"
+#include "Clownfish/String.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+
+TestMethod*
+TestMethod_new() {
+ return (TestMethod*)Class_Make_Obj(TESTMETHOD);
+}
+
+static void
+S_set_host_alias(void *context) {
+ Method *method = (Method*)context;
+ Method_Set_Host_Alias(method, SSTR_WRAP_C("foo"));
+}
+
+static void
+test_accessors(TestBatchRunner *runner) {
+ String *name = SSTR_WRAP_C("Frobnicate_Widget");
+ Method *method = Method_new(name, NULL, 0);
+
+ TEST_TRUE(runner, Str_Equals(Method_Get_Name(method), (Obj*)name),
+ "Get_Name");
+
+ String *alias = SSTR_WRAP_C("host_frob");
+ Method_Set_Host_Alias(method, alias);
+ TEST_TRUE(runner, Str_Equals(Method_Get_Host_Alias(method), (Obj*)alias),
+ "Set_Host_Alias");
+ Err *error = Err_trap(S_set_host_alias, method);
+ TEST_TRUE(runner, error != NULL,
+ "Set_Host_Alias can't be called more than once");
+ DECREF(error);
+
+ TEST_FALSE(runner, Method_Is_Excluded_From_Host(method),
+ "Is_Excluded_From_Host");
+
+ Method_Destroy(method);
+}
+
+static void
+test_lower_snake_alias(TestBatchRunner *runner) {
+ String *name = SSTR_WRAP_C("Frobnicate_Widget");
+ Method *method = Method_new(name, NULL, 0);
+
+ {
+ String *alias = Method_lower_snake_alias(method);
+ TEST_TRUE(runner, Str_Equals_Utf8(alias, "frobnicate_widget", 17),
+ "lower_snake_alias without explicit alias");
+ DECREF(alias);
+ }
+
+ {
+ String *new_alias = SSTR_WRAP_C("host_frob");
+ Method_Set_Host_Alias(method, new_alias);
+ String *alias = Method_lower_snake_alias(method);
+ TEST_TRUE(runner, Str_Equals(alias, (Obj*)new_alias),
+ "lower_snake_alias with explicit alias");
+ DECREF(alias);
+ }
+
+ Method_Destroy(method);
+}
+
+void
+TestMethod_Run_IMP(TestMethod *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 6);
+ test_accessors(runner);
+ test_lower_snake_alias(runner);
+}
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestMethod.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestMethod.cfh b/runtime/test/Clownfish/Test/TestMethod.cfh
new file mode 100644
index 0000000..76b9558
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestMethod.cfh
@@ -0,0 +1,28 @@
+/* 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::TestMethod
+ inherits Clownfish::TestHarness::TestBatch {
+
+ inert incremented TestMethod*
+ new();
+
+ void
+ Run(TestMethod *self, TestBatchRunner *runner);
+}
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestNum.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestNum.c b/runtime/test/Clownfish/Test/TestNum.c
new file mode 100644
index 0000000..dfa6769
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestNum.c
@@ -0,0 +1,278 @@
+/* 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 <math.h>
+
+#include "charmony.h"
+
+#include "Clownfish/Test/TestNum.h"
+
+#include "Clownfish/Err.h"
+#include "Clownfish/String.h"
+#include "Clownfish/Num.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Clownfish/Class.h"
+
+TestNum*
+TestNum_new() {
+ return (TestNum*)Class_Make_Obj(TESTNUM);
+}
+
+static void
+test_To_String(TestBatchRunner *runner) {
+ Float *f64 = Float_new(1.33);
+ Integer *i64 = Int_new(INT64_MAX);
+ String *f64_string = Float_To_String(f64);
+ String *i64_string = Int_To_String(i64);
+
+ TEST_TRUE(runner, Str_Starts_With_Utf8(f64_string, "1.3", 3),
+ "Float_To_String");
+ TEST_TRUE(runner, Str_Equals_Utf8(i64_string, "9223372036854775807", 19),
+ "Int_To_String");
+
+ DECREF(i64_string);
+ DECREF(f64_string);
+ DECREF(i64);
+ DECREF(f64);
+}
+
+static void
+S_float_to_i64(void *context) {
+ Float *f = (Float*)context;
+ Float_To_I64(f);
+}
+
+static void
+test_accessors(TestBatchRunner *runner) {
+ Float *f64 = Float_new(1.33);
+ Integer *i64 = Int_new(INT64_MIN);
+ double wanted64 = 1.33;
+ double got64;
+
+ got64 = Float_Get_Value(f64);
+ TEST_TRUE(runner, *(int64_t*)&got64 == *(int64_t*)&wanted64,
+ "F64 Get_Value");
+ TEST_TRUE(runner, Float_To_I64(f64) == 1, "Float_To_I64");
+
+ {
+ Float *huge = Float_new(1e40);
+ Err *error = Err_trap(S_float_to_i64, huge);
+ TEST_TRUE(runner, error != NULL,
+ "Float_To_I64 throws when out of range (+)");
+ DECREF(error);
+ DECREF(huge);
+ }
+
+ {
+ Float *huge = Float_new(-1e40);
+ Err *error = Err_trap(S_float_to_i64, huge);
+ TEST_TRUE(runner, error != NULL,
+ "Float_To_I64 throws when out of range (-)");
+ DECREF(error);
+ DECREF(huge);
+ }
+
+ TEST_TRUE(runner, Int_Get_Value(i64) == INT64_MIN, "I64 Get_Value");
+ TEST_TRUE(runner, Int_To_F64(i64) == -9223372036854775808.0, "Int_To_F64");
+
+ DECREF(i64);
+ DECREF(f64);
+}
+
+static void
+S_test_compare_float_int(TestBatchRunner *runner, double f64_val,
+ int64_t i64_val, int32_t result) {
+ Float *f64;
+ Integer *i64;
+
+ f64 = Float_new(f64_val);
+ i64 = Int_new(i64_val);
+ TEST_INT_EQ(runner, Float_Compare_To(f64, (Obj*)i64), result,
+ "Float_Compare_To %f %" PRId64, f64_val, i64_val);
+ TEST_INT_EQ(runner, Int_Compare_To(i64, (Obj*)f64), -result,
+ "Int_Compare_To %" PRId64" %f", i64_val, f64_val);
+ TEST_INT_EQ(runner, Float_Equals(f64, (Obj*)i64), result == 0,
+ "Float_Equals %f %" PRId64, f64_val, i64_val);
+ TEST_INT_EQ(runner, Int_Equals(i64, (Obj*)f64), result == 0,
+ "Int_Equals %" PRId64 " %f", i64_val, f64_val);
+ DECREF(f64);
+ DECREF(i64);
+
+ if (i64_val == INT64_MIN) { return; }
+
+ f64 = Float_new(-f64_val);
+ i64 = Int_new(-i64_val);
+ TEST_INT_EQ(runner, Float_Compare_To(f64, (Obj*)i64), -result,
+ "Float_Compare_To %f %" PRId64, -f64_val, -i64_val);
+ TEST_INT_EQ(runner, Int_Compare_To(i64, (Obj*)f64), result,
+ "Int_Compare_To %" PRId64" %f", -i64_val, -f64_val);
+ TEST_INT_EQ(runner, Float_Equals(f64, (Obj*)i64), result == 0,
+ "Float_Equals %f %" PRId64, -f64_val, -i64_val);
+ TEST_INT_EQ(runner, Int_Equals(i64, (Obj*)f64), result == 0,
+ "Int_Equals %" PRId64 " %f", -i64_val, -f64_val);
+ DECREF(f64);
+ DECREF(i64);
+}
+
+static void
+S_float_compare_to(void *context) {
+ Float *f = (Float*)context;
+ Float_Compare_To(f, (Obj*)OBJ);
+}
+
+static void
+S_int_compare_to(void *context) {
+ Integer *i = (Integer*)context;
+ Int_Compare_To(i, (Obj*)OBJ);
+}
+
+static void
+test_Equals_and_Compare_To(TestBatchRunner *runner) {
+ {
+ Float *f1 = Float_new(1.0);
+ Float *f2 = Float_new(1.0);
+ TEST_TRUE(runner, Float_Compare_To(f1, (Obj*)f2) == 0,
+ "Float_Compare_To equal");
+ TEST_TRUE(runner, Float_Equals(f1, (Obj*)f2),
+ "Float_Equals equal");
+ DECREF(f1);
+ DECREF(f2);
+ }
+
+ {
+ Float *f1 = Float_new(1.0);
+ Float *f2 = Float_new(2.0);
+ TEST_TRUE(runner, Float_Compare_To(f1, (Obj*)f2) < 0,
+ "Float_Compare_To less than");
+ TEST_FALSE(runner, Float_Equals(f1, (Obj*)f2),
+ "Float_Equals less than");
+ DECREF(f1);
+ DECREF(f2);
+ }
+
+ {
+ Float *f1 = Float_new(1.0);
+ Float *f2 = Float_new(0.0);
+ TEST_TRUE(runner, Float_Compare_To(f1, (Obj*)f2) > 0,
+ "Float_Compare_To greater than");
+ TEST_FALSE(runner, Float_Equals(f1, (Obj*)f2),
+ "Float_Equals greater than");
+ DECREF(f1);
+ DECREF(f2);
+ }
+
+ {
+ Float *f = Float_new(1.0);
+ Err *error = Err_trap(S_float_compare_to, f);
+ TEST_TRUE(runner, error != NULL,
+ "Float_Compare_To with invalid type throws");
+ TEST_FALSE(runner, Float_Equals(f, (Obj*)OBJ),
+ "Float_Equals with different type");
+ DECREF(error);
+ DECREF(f);
+ }
+
+ {
+ Integer *i1 = Int_new(INT64_C(0x6666666666666666));
+ Integer *i2 = Int_new(INT64_C(0x6666666666666666));
+ TEST_TRUE(runner, Int_Compare_To(i1, (Obj*)i2) == 0,
+ "Int_Compare_To equal");
+ TEST_TRUE(runner, Int_Equals(i1, (Obj*)i2),
+ "Int_Equals equal");
+ DECREF(i1);
+ DECREF(i2);
+ }
+
+ {
+ Integer *i1 = Int_new(INT64_C(0x6666666666666666));
+ Integer *i2 = Int_new(INT64_C(0x6666666666666667));
+ TEST_TRUE(runner, Int_Compare_To(i1, (Obj*)i2) < 0,
+ "Int_Compare_To less than");
+ TEST_FALSE(runner, Int_Equals(i1, (Obj*)i2),
+ "Int_Equals less than");
+ DECREF(i1);
+ DECREF(i2);
+ }
+
+ {
+ Integer *i1 = Int_new(INT64_C(0x6666666666666666));
+ Integer *i2 = Int_new(INT64_C(0x6666666666666665));
+ TEST_TRUE(runner, Int_Compare_To(i1, (Obj*)i2) > 0,
+ "Int_Compare_To greater than");
+ TEST_FALSE(runner, Int_Equals(i1, (Obj*)i2),
+ "Int_Equals greater than");
+ DECREF(i1);
+ DECREF(i2);
+ }
+
+ {
+ Integer *i = Int_new(0);
+ Err *error = Err_trap(S_int_compare_to, i);
+ TEST_TRUE(runner, error != NULL,
+ "Int_Compare_To with invalid type throws");
+ TEST_FALSE(runner, Int_Equals(i, (Obj*)OBJ),
+ "Int_Equals with different type");
+ DECREF(error);
+ DECREF(i);
+ }
+
+ // NOTICE: When running these tests on x86/x64, it's best to compile
+ // with -ffloat-store to avoid excess FPU precision which can hide
+ // implementation bugs.
+ S_test_compare_float_int(runner, (double)INT64_MAX * 2.0, INT64_MAX, 1);
+ S_test_compare_float_int(runner, pow(2.0, 60.0), INT64_C(1) << 60, 0);
+ S_test_compare_float_int(runner, pow(2.0, 60.0), (INT64_C(1) << 60) - 1,
+ 1);
+ S_test_compare_float_int(runner, pow(2.0, 60.0), (INT64_C(1) << 60) + 1,
+ -1);
+ S_test_compare_float_int(runner, pow(2.0, 63.0), INT64_MAX, 1);
+ S_test_compare_float_int(runner, -pow(2.0, 63.0), INT64_MIN, 0);
+ // -9223372036854777856.0 == nextafter(-pow(2, 63), -INFINITY)
+ S_test_compare_float_int(runner, -9223372036854777856.0, INT64_MIN, -1);
+ S_test_compare_float_int(runner, 1.0, 2, -1);
+}
+
+static void
+test_Clone(TestBatchRunner *runner) {
+ Float *f64 = Float_new(1.33);
+ Integer *i64 = Int_new(INT64_MAX);
+ Float *f64_dupe = Float_Clone(f64);
+ Integer *i64_dupe = Int_Clone(i64);
+ TEST_TRUE(runner, Float_Equals(f64, (Obj*)f64_dupe),
+ "Float Clone");
+ TEST_TRUE(runner, Int_Equals(i64, (Obj*)i64_dupe),
+ "Integer Clone");
+ DECREF(i64_dupe);
+ DECREF(f64_dupe);
+ DECREF(i64);
+ DECREF(f64);
+}
+
+void
+TestNum_Run_IMP(TestNum *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 82);
+ test_To_String(runner);
+ test_accessors(runner);
+ test_Equals_and_Compare_To(runner);
+ test_Clone(runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestNum.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestNum.cfh b/runtime/test/Clownfish/Test/TestNum.cfh
new file mode 100644
index 0000000..6d1f663
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestNum.cfh
@@ -0,0 +1,29 @@
+/* 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::TestHarness::TestBatch {
+
+ inert incremented TestNum*
+ new();
+
+ void
+ Run(TestNum *self, TestBatchRunner *runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestObj.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestObj.c b/runtime/test/Clownfish/Test/TestObj.c
new file mode 100644
index 0000000..c4cdafe
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestObj.c
@@ -0,0 +1,152 @@
+/* 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 CFISH_USE_SHORT_NAMES
+#define TESTCFISH_USE_SHORT_NAMES
+
+#include "charmony.h"
+
+#include "Clownfish/Test/TestObj.h"
+
+#include "Clownfish/String.h"
+#include "Clownfish/Err.h"
+#include "Clownfish/Test.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/Class.h"
+
+TestObj*
+TestObj_new() {
+ return (TestObj*)Class_Make_Obj(TESTOBJ);
+}
+
+static Obj*
+S_new_testobj() {
+ String *class_name = SSTR_WRAP_C("TestObj");
+ Obj *obj;
+ Class *klass = Class_fetch_class(class_name);
+ if (!klass) {
+ klass = Class_singleton(class_name, OBJ);
+ }
+ obj = Class_Make_Obj(klass);
+ return Obj_init(obj);
+}
+
+static void
+test_refcounts(TestBatchRunner *runner) {
+ Obj *obj = S_new_testobj();
+
+ TEST_INT_EQ(runner, CFISH_REFCOUNT_NN(obj), 1,
+ "Correct starting refcount");
+
+ obj = CFISH_INCREF_NN(obj);
+ TEST_INT_EQ(runner, CFISH_REFCOUNT_NN(obj), 2, "INCREF_NN");
+
+ CFISH_DECREF_NN(obj);
+ TEST_INT_EQ(runner, CFISH_REFCOUNT_NN(obj), 1, "DECREF_NN");
+
+ DECREF(obj);
+}
+
+static void
+test_To_String(TestBatchRunner *runner) {
+ Obj *testobj = S_new_testobj();
+ String *string = Obj_To_String(testobj);
+ TEST_TRUE(runner, Str_Contains_Utf8(string, "TestObj", 7), "To_String");
+ DECREF(string);
+ DECREF(testobj);
+}
+
+static void
+test_Equals(TestBatchRunner *runner) {
+ Obj *testobj = S_new_testobj();
+ Obj *other = S_new_testobj();
+
+ TEST_TRUE(runner, Obj_Equals(testobj, testobj),
+ "Equals is true for the same object");
+ TEST_FALSE(runner, Obj_Equals(testobj, other),
+ "Distinct objects are not equal");
+
+ DECREF(testobj);
+ DECREF(other);
+}
+
+static void
+test_is_a(TestBatchRunner *runner) {
+ String *string = Str_new_from_trusted_utf8("", 0);
+ Class *str_class = Str_get_class(string);
+ String *class_name = Str_get_class_name(string);
+
+ TEST_TRUE(runner, Str_is_a(string, STRING), "String is_a String.");
+ TEST_TRUE(runner, Str_is_a(string, OBJ), "String is_a Obj.");
+ TEST_TRUE(runner, str_class == STRING, "get_class");
+ TEST_TRUE(runner, Str_Equals(Class_Get_Name(STRING), (Obj*)class_name),
+ "get_class_name");
+ TEST_FALSE(runner, Obj_is_a(NULL, OBJ), "NULL is not an Obj");
+
+ DECREF(string);
+}
+
+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_Compare_To(void *context) {
+ Obj_Compare_To((Obj*)context, (Obj*)context);
+}
+
+static void
+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(runner, error != NULL
+ && Err_is_a(error, ERR)
+ && Str_Contains_Utf8(Err_Get_Mess(error), "bstract", 7),
+ message);
+ DECREF(error);
+}
+
+static void
+test_abstract_routines(TestBatchRunner *runner) {
+ Obj *blank = Class_Make_Obj(OBJ);
+ S_verify_abstract_error(runner, S_attempt_init, blank, "init");
+
+ Obj *obj = S_new_testobj();
+ S_verify_abstract_error(runner, S_attempt_Clone, obj, "Clone");
+ S_verify_abstract_error(runner, S_attempt_Compare_To, obj, "Compare_To");
+ DECREF(obj);
+}
+
+void
+TestObj_Run_IMP(TestObj *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 14);
+ test_refcounts(runner);
+ test_To_String(runner);
+ test_Equals(runner);
+ test_is_a(runner);
+ test_abstract_routines(runner);
+}
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestObj.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestObj.cfh b/runtime/test/Clownfish/Test/TestObj.cfh
new file mode 100644
index 0000000..c5cc401
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestObj.cfh
@@ -0,0 +1,28 @@
+/* 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::TestHarness::TestBatch {
+
+ inert incremented TestObj*
+ new();
+
+ void
+ Run(TestObj *self, TestBatchRunner *runner);
+}
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestPtrHash.c
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestPtrHash.c b/runtime/test/Clownfish/Test/TestPtrHash.c
new file mode 100644
index 0000000..3bf7003
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestPtrHash.c
@@ -0,0 +1,108 @@
+/* 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 <stdlib.h>
+#include <time.h>
+
+#include "Clownfish/Test/TestPtrHash.h"
+#include "Clownfish/Class.h"
+#include "Clownfish/PtrHash.h"
+#include "Clownfish/TestHarness/TestBatchRunner.h"
+#include "Clownfish/TestHarness/TestUtils.h"
+#include "Clownfish/Util/Memory.h"
+
+TestPtrHash*
+TestPtrHash_new() {
+ return (TestPtrHash*)Class_Make_Obj(TESTPTRHASH);
+}
+
+static void
+test_Store_and_Fetch(TestBatchRunner *runner) {
+ PtrHash *hash = PtrHash_new(100);
+ char dummy[100];
+
+ for (int i = 0; i < 100; i++) {
+ void *key = &dummy[i];
+ PtrHash_Store(hash, key, key);
+ }
+
+ bool all_equal = true;
+ for (int i = 0; i < 100; i++) {
+ void *key = &dummy[i];
+ void *value = PtrHash_Fetch(hash, key);
+ if (value != key) {
+ all_equal = false;
+ break;
+ }
+ }
+ TEST_TRUE(runner, all_equal, "basic Store and Fetch");
+
+ TEST_TRUE(runner, PtrHash_Fetch(hash, &dummy[100]) == NULL,
+ "Fetch against non-existent key returns NULL");
+
+ PtrHash_Store(hash, &dummy[50], dummy);
+ TEST_TRUE(runner, PtrHash_Fetch(hash, &dummy[50]) == dummy,
+ "Store replaces existing value");
+
+ PtrHash_Destroy(hash);
+}
+
+static void
+test_stress(TestBatchRunner *runner) {
+ PtrHash *hash = PtrHash_new(0); // trigger multiple rebuilds.
+ size_t num_elems = 200000;
+ void **keys = (void**)MALLOCATE(num_elems * sizeof(void*));
+
+ for (size_t i = 0; i < num_elems; i++) {
+ size_t index = (size_t)(TestUtils_random_u64() % num_elems);
+ void *key = &keys[index];
+ PtrHash_Store(hash, key, key);
+ keys[i] = key;
+ }
+
+ // Overwrite for good measure.
+ for (size_t i = 0; i < num_elems; i++) {
+ void *key = keys[i];
+ PtrHash_Store(hash, key, key);
+ }
+
+ bool all_equal = true;
+ for (size_t i = 0; i < num_elems; i++) {
+ void *key = keys[i];
+ void *got = PtrHash_Fetch(hash, key);
+ if (got != key) {
+ all_equal = false;
+ break;
+ }
+ }
+ TEST_TRUE(runner, all_equal, "stress test");
+
+ FREEMEM(keys);
+ PtrHash_Destroy(hash);
+}
+
+void
+TestPtrHash_Run_IMP(TestPtrHash *self, TestBatchRunner *runner) {
+ TestBatchRunner_Plan(runner, (TestBatch*)self, 4);
+ srand((unsigned int)time(NULL));
+ test_Store_and_Fetch(runner);
+ test_stress(runner);
+}
+
+
http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/8ba4e619/runtime/test/Clownfish/Test/TestPtrHash.cfh
----------------------------------------------------------------------
diff --git a/runtime/test/Clownfish/Test/TestPtrHash.cfh b/runtime/test/Clownfish/Test/TestPtrHash.cfh
new file mode 100644
index 0000000..83589cb
--- /dev/null
+++ b/runtime/test/Clownfish/Test/TestPtrHash.cfh
@@ -0,0 +1,29 @@
+/* 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::TestPtrHash
+ inherits Clownfish::TestHarness::TestBatch {
+
+ inert incremented TestPtrHash*
+ new();
+
+ void
+ Run(TestPtrHash *self, TestBatchRunner *runner);
+}
+
+