| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <string.h> |
| |
| #define CFC_USE_TEST_MACROS |
| #include "CFCBase.h" |
| #include "CFCClass.h" |
| #include "CFCParcel.h" |
| #include "CFCParser.h" |
| #include "CFCTest.h" |
| #include "CFCType.h" |
| #include "CFCUtil.h" |
| |
| #ifndef true |
| #define true 1 |
| #define false 0 |
| #endif |
| |
| static void |
| S_run_tests(CFCTest *test); |
| |
| static void |
| S_run_basic_tests(CFCTest *test); |
| |
| static void |
| S_run_primitive_tests(CFCTest *test); |
| |
| static void |
| S_run_integer_tests(CFCTest *test); |
| |
| static void |
| S_run_float_tests(CFCTest *test); |
| |
| static void |
| S_run_void_tests(CFCTest *test); |
| |
| static void |
| S_run_object_tests(CFCTest *test); |
| |
| static void |
| S_run_va_list_tests(CFCTest *test); |
| |
| static void |
| S_run_arbitrary_tests(CFCTest *test); |
| |
| static void |
| S_run_composite_tests(CFCTest *test); |
| |
| const CFCTestBatch CFCTEST_BATCH_TYPE = { |
| "Clownfish::CFC::Model::Type", |
| 369, |
| S_run_tests |
| }; |
| |
| static void |
| S_run_tests(CFCTest *test) { |
| S_run_basic_tests(test); |
| S_run_primitive_tests(test); |
| S_run_integer_tests(test); |
| S_run_float_tests(test); |
| S_run_void_tests(test); |
| S_run_object_tests(test); |
| S_run_va_list_tests(test); |
| S_run_arbitrary_tests(test); |
| S_run_composite_tests(test); |
| } |
| |
| static void |
| S_run_basic_tests(CFCTest *test) { |
| CFCParcel *neato_parcel = CFCParcel_new("Neato", NULL, NULL, NULL, NULL); |
| CFCParcel_register(neato_parcel); |
| CFCType *type = CFCType_new(0, neato_parcel, "mytype_t", 0); |
| |
| OK(test, CFCType_get_parcel(type) == neato_parcel, "get_parcel"); |
| STR_EQ(test, CFCType_to_c(type), "mytype_t", "to_c"); |
| STR_EQ(test, CFCType_get_specifier(type), "mytype_t", "get_specifier"); |
| |
| #define TEST_BOOL_ACCESSOR(type, name) \ |
| OK(test, !CFCType_ ## name(type), #name " false by default"); |
| |
| TEST_BOOL_ACCESSOR(type, const); |
| TEST_BOOL_ACCESSOR(type, nullable); |
| TEST_BOOL_ACCESSOR(type, incremented); |
| TEST_BOOL_ACCESSOR(type, decremented); |
| TEST_BOOL_ACCESSOR(type, is_void); |
| TEST_BOOL_ACCESSOR(type, is_object); |
| TEST_BOOL_ACCESSOR(type, is_primitive); |
| TEST_BOOL_ACCESSOR(type, is_integer); |
| TEST_BOOL_ACCESSOR(type, is_floating); |
| TEST_BOOL_ACCESSOR(type, cfish_string); |
| TEST_BOOL_ACCESSOR(type, is_va_list); |
| TEST_BOOL_ACCESSOR(type, is_arbitrary); |
| TEST_BOOL_ACCESSOR(type, is_composite); |
| |
| CFCBase_decref((CFCBase*)neato_parcel); |
| CFCBase_decref((CFCBase*)type); |
| |
| CFCParcel_reap_singletons(); |
| } |
| |
| static void |
| S_run_primitive_tests(CFCTest *test) { |
| CFCParcel *parcel = CFCParcel_new("Parcel", NULL, NULL, NULL, NULL); |
| CFCType *type = CFCType_new(CFCTYPE_PRIMITIVE, parcel, "hump_t", 0); |
| OK(test, CFCType_is_primitive(type), "is_primitive"); |
| |
| { |
| CFCType *twin = CFCType_new(CFCTYPE_PRIMITIVE, parcel, "hump_t", 0); |
| OK(test, CFCType_equals(type, twin), "equals"); |
| CFCBase_decref((CFCBase*)twin); |
| } |
| |
| { |
| CFCType *other = CFCType_new(CFCTYPE_PRIMITIVE, parcel, "dump_t", 0); |
| OK(test, !CFCType_equals(type, other), "equals spoiled by specifier"); |
| CFCBase_decref((CFCBase*)other); |
| } |
| |
| { |
| CFCType *other = CFCType_new(CFCTYPE_PRIMITIVE|CFCTYPE_CONST, parcel, |
| "hump_t", 0); |
| OK(test, !CFCType_equals(type, other), "equals spoiled by const"); |
| CFCBase_decref((CFCBase*)other); |
| } |
| |
| CFCBase_decref((CFCBase*)type); |
| CFCBase_decref((CFCBase*)parcel); |
| } |
| |
| static void |
| S_run_integer_tests(CFCTest *test) { |
| { |
| CFCType *type = CFCType_new_integer(CFCTYPE_CONST, "int32_t"); |
| OK(test, CFCType_const(type), "const"); |
| STR_EQ(test, CFCType_get_specifier(type), "int32_t", "get_specifier"); |
| STR_EQ(test, CFCType_to_c(type), "const int32_t", |
| "'const' in C representation"); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| { |
| CFCParser *parser = CFCParser_new(); |
| static const char *specifiers[14] = { |
| "bool", |
| "char", |
| "short", |
| "int", |
| "long", |
| "size_t", |
| "int8_t", |
| "int16_t", |
| "int32_t", |
| "int64_t", |
| "uint8_t", |
| "uint16_t", |
| "uint32_t", |
| "uint64_t" |
| }; |
| for (int i = 0; i < 14; ++i) { |
| const char *specifier = specifiers[i]; |
| CFCType *type; |
| |
| type = CFCTest_parse_type(test, parser, specifier); |
| OK(test, CFCType_is_integer(type), "%s is_integer", specifier); |
| CFCBase_decref((CFCBase*)type); |
| |
| char *const_specifier = CFCUtil_sprintf("const %s", specifier); |
| type = CFCTest_parse_type(test, parser, const_specifier); |
| OK(test, CFCType_is_integer(type), "%s is_integer", |
| const_specifier); |
| OK(test, CFCType_const(type), "%s is const", const_specifier); |
| FREEMEM(const_specifier); |
| CFCBase_decref((CFCBase*)type); |
| } |
| CFCBase_decref((CFCBase*)parser); |
| } |
| } |
| |
| static void |
| S_run_float_tests(CFCTest *test) { |
| { |
| CFCType *type = CFCType_new_float(CFCTYPE_CONST, "float"); |
| OK(test, CFCType_const(type), "const"); |
| STR_EQ(test, CFCType_get_specifier(type), "float", "get_specifier"); |
| STR_EQ(test, CFCType_to_c(type), "const float", |
| "'const' in C representation"); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| { |
| CFCParser *parser = CFCParser_new(); |
| static const char *specifiers[2] = { |
| "float", |
| "double" |
| }; |
| for (int i = 0; i < 2; ++i) { |
| const char *specifier = specifiers[i]; |
| CFCType *type; |
| |
| type = CFCTest_parse_type(test, parser, specifier); |
| OK(test, CFCType_is_floating(type), "%s is_floating", specifier); |
| CFCBase_decref((CFCBase*)type); |
| |
| char *const_specifier = CFCUtil_sprintf("const %s", specifier); |
| type = CFCTest_parse_type(test, parser, const_specifier); |
| OK(test, CFCType_is_floating(type), "%s is_floating", |
| const_specifier); |
| OK(test, CFCType_const(type), "%s is const", const_specifier); |
| FREEMEM(const_specifier); |
| CFCBase_decref((CFCBase*)type); |
| } |
| CFCBase_decref((CFCBase*)parser); |
| } |
| } |
| |
| static void |
| S_run_void_tests(CFCTest *test) { |
| CFCParser *parser = CFCParser_new(); |
| |
| { |
| CFCType *type = CFCType_new_void(false); |
| STR_EQ(test, CFCType_get_specifier(type), "void", "get_specifier"); |
| STR_EQ(test, CFCType_to_c(type), "void", "to_c"); |
| OK(test, CFCType_is_void(type), "is_void"); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| { |
| CFCType *type = CFCType_new_void(true); |
| STR_EQ(test, CFCType_to_c(type), "const void", |
| "'const' in C representation"); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| { |
| CFCType *type = CFCTest_parse_type(test, parser, "void"); |
| OK(test, CFCType_is_void(type), "void is_void"); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| { |
| CFCType *type = CFCTest_parse_type(test, parser, "const void"); |
| OK(test, CFCType_is_void(type), "const void is_void"); |
| OK(test, CFCType_const(type), "const void is const"); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| CFCBase_decref((CFCBase*)parser); |
| } |
| |
| static char* |
| S_try_new_object(CFCParcel *parcel, const char *specifier, int indirection) { |
| char *error; |
| |
| CFCUTIL_TRY { |
| CFCType *type = CFCType_new_object(0, parcel, specifier, indirection); |
| CFCBase_decref((CFCBase*)type); |
| } |
| CFCUTIL_CATCH(error); |
| |
| return error; |
| } |
| |
| static void |
| S_run_object_tests(CFCTest *test) { |
| static const char *modifiers[4] = { |
| "const", "incremented", "decremented", "nullable" |
| }; |
| static int flags[4] = { |
| CFCTYPE_CONST, |
| CFCTYPE_INCREMENTED, |
| CFCTYPE_DECREMENTED, |
| CFCTYPE_NULLABLE |
| }; |
| static int (*accessors[4])(CFCType *type) = { |
| CFCType_const, |
| CFCType_incremented, |
| CFCType_decremented, |
| CFCType_nullable |
| }; |
| |
| { |
| CFCParser *parser = CFCParser_new(); |
| CFCParcel *neato_parcel |
| = CFCTest_parse_parcel(test, parser, "parcel Neato;"); |
| |
| static const char *specifiers[4] = { |
| "Foo", "FooJr", "FooIII", "Foo4th" |
| }; |
| for (int i = 0; i < 4; ++i) { |
| const char *specifier = specifiers[i]; |
| |
| char *class_code = CFCUtil_sprintf("class %s {}", specifier); |
| CFCClass *klass = CFCTest_parse_class(test, parser, class_code); |
| FREEMEM(class_code); |
| |
| static const char *prefixes[2] = { "", "neato_" }; |
| char *expect = CFCUtil_sprintf("neato_%s", specifier); |
| for (int j = 0; j < 2; ++j) { |
| char *src = CFCUtil_sprintf("%s%s*", prefixes[j], specifier); |
| CFCType *type = CFCTest_parse_type(test, parser, src); |
| CFCType_resolve(type); |
| STR_EQ(test, CFCType_get_specifier(type), expect, |
| "object_type_specifier: %s", src); |
| OK(test, CFCType_is_object(type), "%s is_object", src); |
| INT_EQ(test, CFCType_get_indirection(type), 1, |
| "%s indirection", src); |
| |
| FREEMEM(src); |
| CFCBase_decref((CFCBase*)type); |
| } |
| FREEMEM(expect); |
| |
| for (int j = 0; j < 4; ++j) { |
| char *src = CFCUtil_sprintf("%s %s*", modifiers[j], specifier); |
| CFCType *type = CFCTest_parse_type(test, parser, src); |
| OK(test, CFCType_is_object(type), "%s is_object", src); |
| OK(test, accessors[j](type), "%s accessor", src); |
| |
| FREEMEM(src); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| CFCBase_decref((CFCBase*)klass); |
| } |
| |
| CFCBase_decref((CFCBase*)neato_parcel); |
| CFCBase_decref((CFCBase*)parser); |
| } |
| |
| CFCParcel *neato_parcel = CFCParcel_new("Neato", NULL, NULL, NULL, NULL); |
| CFCClass *foo_class |
| = CFCClass_create(neato_parcel, NULL, "Foo", NULL, NULL, NULL, NULL, |
| false, false, false); |
| CFCType *foo = CFCType_new_object(0, neato_parcel, "Foo", 1); |
| CFCType_resolve(foo); |
| |
| { |
| static const char *bad_specifiers[5] = { |
| "foo", "Foo_Bar", "FOOBAR", "1Foo", "1FOO" |
| }; |
| for (int i = 0; i < 5; i++) { |
| char *error = S_try_new_object(neato_parcel, bad_specifiers[i], 1); |
| OK(test, error && strstr(error, "specifier"), |
| "constructor rejects bad specifier"); |
| FREEMEM(error); |
| } |
| } |
| |
| { |
| char *error = S_try_new_object(neato_parcel, NULL, 1); |
| OK(test, error && strstr(error, "specifier"), "specifier required"); |
| FREEMEM(error); |
| } |
| |
| { |
| for (int indirection = 0; indirection <= 2; indirection += 2) { |
| char *error = S_try_new_object(neato_parcel, "Foo", indirection); |
| OK(test, error && strstr(error, "indirection"), |
| "invalid indirection of %d", indirection); |
| FREEMEM(error); |
| } |
| } |
| |
| { |
| CFCType *another_foo = CFCType_new_object(0, neato_parcel, "Foo", 1); |
| CFCType_resolve(another_foo); |
| OK(test, CFCType_equals(foo, another_foo), "equals"); |
| CFCBase_decref((CFCBase*)another_foo); |
| } |
| |
| { |
| CFCClass *bar_class |
| = CFCClass_create(neato_parcel, NULL, "Bar", NULL, NULL, NULL, |
| NULL, false, false, false); |
| CFCType *bar = CFCType_new_object(0, neato_parcel, "Bar", 1); |
| CFCType_resolve(bar); |
| OK(test, !CFCType_equals(foo, bar), |
| "different specifier spoils equals"); |
| CFCBase_decref((CFCBase*)bar); |
| CFCBase_decref((CFCBase*)bar_class); |
| } |
| |
| { |
| CFCParcel *foreign_parcel |
| = CFCParcel_new("Foreign", NULL, NULL, NULL, NULL); |
| CFCClass *foreign_foo_class |
| = CFCClass_create(foreign_parcel, NULL, "Foreign::Foo", NULL, NULL, |
| NULL, NULL, false, false, false); |
| CFCType *foreign_foo = CFCType_new_object(0, foreign_parcel, "Foo", 1); |
| CFCType_resolve(foreign_foo); |
| OK(test, !CFCType_equals(foo, foreign_foo), |
| "different parcel spoils equals"); |
| STR_EQ(test, CFCType_get_specifier(foreign_foo), "foreign_Foo", |
| "prepend parcel prefix to specifier"); |
| CFCBase_decref((CFCBase*)foreign_parcel); |
| CFCBase_decref((CFCBase*)foreign_foo_class); |
| CFCBase_decref((CFCBase*)foreign_foo); |
| } |
| |
| { |
| for (int i = 0; i < 4; ++i) { |
| CFCType *modified_foo |
| = CFCType_new_object(flags[i], neato_parcel, "Foo", 1); |
| CFCType_resolve(modified_foo); |
| OK(test, accessors[i](modified_foo), "%s", modifiers[i]); |
| OK(test, !accessors[i](foo), "not %s", modifiers[i]); |
| OK(test, !CFCType_equals(foo, modified_foo), |
| "different %s spoils equals", modifiers[i]); |
| OK(test, !CFCType_similar(foo, modified_foo), |
| "different %s spoils similar", modifiers[i]); |
| CFCBase_decref((CFCBase*)modified_foo); |
| } |
| } |
| |
| { |
| CFCType *string_type |
| = CFCType_new_object(0, neato_parcel, "String", 1); |
| OK(test, CFCType_cfish_string(string_type), "%s", "cfish_string"); |
| OK(test, !CFCType_cfish_string(foo), "not %s", "not cfish_string"); |
| CFCBase_decref((CFCBase*)string_type); |
| } |
| |
| CFCBase_decref((CFCBase*)neato_parcel); |
| CFCBase_decref((CFCBase*)foo_class); |
| CFCBase_decref((CFCBase*)foo); |
| |
| CFCParcel_reap_singletons(); |
| } |
| |
| static void |
| S_run_va_list_tests(CFCTest *test) { |
| { |
| CFCType *type = CFCType_new_va_list(); |
| STR_EQ(test, CFCType_get_specifier(type), "va_list", |
| "specifier defaults to 'va_list'"); |
| STR_EQ(test, CFCType_to_c(type), "va_list", "to_c"); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| { |
| CFCParser *parser = CFCParser_new(); |
| CFCType *type = CFCTest_parse_type(test, parser, "va_list"); |
| OK(test, CFCType_is_va_list(type), "is_va_list"); |
| CFCBase_decref((CFCBase*)type); |
| CFCBase_decref((CFCBase*)parser); |
| } |
| } |
| |
| static void |
| S_run_arbitrary_tests(CFCTest *test) { |
| { |
| CFCParcel *neato_parcel = CFCParcel_new("Neato", NULL, NULL, NULL, |
| NULL); |
| CFCParcel_register(neato_parcel); |
| |
| CFCType *foo = CFCType_new_arbitrary(neato_parcel, "foo_t"); |
| STR_EQ(test, CFCType_get_specifier(foo), "foo_t", "get_specifier"); |
| STR_EQ(test, CFCType_to_c(foo), "foo_t", "to_c"); |
| |
| CFCType *twin = CFCType_new_arbitrary(neato_parcel, "foo_t"); |
| OK(test, CFCType_equals(foo, twin), "equals"); |
| |
| CFCType *compare_t |
| = CFCType_new_arbitrary(neato_parcel, "Sort_compare_t"); |
| OK(test, !CFCType_equals(foo, compare_t), |
| "equals spoiled by different specifier"); |
| |
| CFCBase_decref((CFCBase*)neato_parcel); |
| CFCBase_decref((CFCBase*)foo); |
| CFCBase_decref((CFCBase*)compare_t); |
| CFCBase_decref((CFCBase*)twin); |
| } |
| |
| { |
| CFCParser *parser = CFCParser_new(); |
| static const char *specifiers[2] = { "foo_t", "Sort_compare_t" }; |
| for (int i = 0; i < 2; ++i) { |
| const char *specifier = specifiers[i]; |
| CFCType *type = CFCTest_parse_type(test, parser, specifier); |
| OK(test, CFCType_is_arbitrary(type), "arbitrary type %s", |
| specifier); |
| CFCBase_decref((CFCBase*)type); |
| } |
| CFCBase_decref((CFCBase*)parser); |
| } |
| |
| CFCParcel_reap_singletons(); |
| } |
| |
| static void |
| S_run_composite_tests(CFCTest *test) { |
| CFCParser *parser = CFCParser_new(); |
| CFCParcel *neato_parcel = CFCParcel_new("Neato", NULL, NULL, NULL, NULL); |
| CFCParser_set_parcel(parser, neato_parcel); |
| |
| { |
| static const char *type_strings[14] = { |
| "char*", |
| "char**", |
| "char***", |
| "int32_t*", |
| "Obj**", |
| "int8_t[]", |
| "int8_t[1]", |
| "neato_method_t[]", |
| "neato_method_t[1]", |
| "multi_dimensional_t[1][10]", |
| "char * * ", |
| "const Obj**", |
| "const void*", |
| "int8_t[ 3 ]" |
| }; |
| for (int i = 0; i < 14; ++i) { |
| const char *type_string = type_strings[i]; |
| CFCType *type = CFCTest_parse_type(test, parser, type_string); |
| OK(test, CFCType_is_composite(type), "composite type %s", |
| type_string); |
| CFCBase_decref((CFCBase*)type); |
| } |
| } |
| |
| { |
| CFCType *type = NULL; |
| char *error; |
| |
| CFCUTIL_TRY { |
| type = CFCType_new_composite(0, NULL, 0, NULL); |
| } |
| CFCUTIL_CATCH(error); |
| OK(test, error && strstr(error, "child"), "child required"); |
| |
| FREEMEM(error); |
| CFCBase_decref((CFCBase*)type); |
| } |
| |
| { |
| CFCType *foo = CFCType_new_object(0, neato_parcel, "Foo", 1); |
| CFCType *const_foo |
| = CFCType_new_object(CFCTYPE_CONST, neato_parcel, "Foo", 1); |
| |
| CFCType *composite = CFCType_new_composite(0, foo, 1, NULL); |
| OK(test, CFCType_is_composite(composite), "is_composite"); |
| STR_EQ(test, CFCType_get_specifier(composite), "Foo", |
| "get_specifier delegates to child" ); |
| |
| CFCType *twin = CFCType_new_composite(0, foo, 1, NULL); |
| OK(test, CFCType_equals(composite, twin), "equals"); |
| CFCBase_decref((CFCBase*)twin); |
| |
| CFCType *const_composite |
| = CFCType_new_composite(0, const_foo, 1, NULL); |
| OK(test, !CFCType_equals(composite, const_composite), |
| "equals spoiled by different child"); |
| CFCBase_decref((CFCBase*)const_composite); |
| |
| CFCBase_decref((CFCBase*)composite); |
| CFCBase_decref((CFCBase*)foo); |
| CFCBase_decref((CFCBase*)const_foo); |
| } |
| |
| { |
| CFCType *foo_array = CFCTest_parse_type(test, parser, "foo_t[]"); |
| CFCType_resolve(foo_array); |
| STR_EQ(test, CFCType_get_array(foo_array), "[]", "get_array"); |
| STR_EQ(test, CFCType_to_c(foo_array), "foo_t", |
| "array subscripts not included by to_c"); |
| CFCType *foo_array_array |
| = CFCTest_parse_type(test, parser, "foo_t[][]"); |
| OK(test, !CFCType_equals(foo_array, foo_array_array), |
| "equals spoiled by different array postfixes"); |
| |
| CFCBase_decref((CFCBase*)foo_array); |
| CFCBase_decref((CFCBase*)foo_array_array); |
| } |
| |
| { |
| CFCType *foo_star = CFCTest_parse_type(test, parser, "foo_t*"); |
| CFCType *foo_star_star = CFCTest_parse_type(test, parser, "foo_t**"); |
| OK(test, !CFCType_equals(foo_star, foo_star_star), |
| "equals spoiled by different levels of indirection"); |
| INT_EQ(test, CFCType_get_indirection(foo_star), 1, |
| "foo_t* indirection"); |
| INT_EQ(test, CFCType_get_indirection(foo_star_star), 2, |
| "foo_t** indirection"); |
| |
| CFCBase_decref((CFCBase*)foo_star); |
| CFCBase_decref((CFCBase*)foo_star_star); |
| } |
| |
| CFCBase_decref((CFCBase*)neato_parcel); |
| CFCBase_decref((CFCBase*)parser); |
| } |
| |