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); +} + +
