Test CFC exceptions in C test suite Catching exceptions sometimes requires a separate function because stack variables might be clobbered by longjmp. See
http://stackoverflow.com/q/2024933 Also add some other parcel tests from the Perl test suite. Fixes CLOWNFISH-14. Project: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/repo Commit: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/commit/e3ecf10d Tree: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/tree/e3ecf10d Diff: http://git-wip-us.apache.org/repos/asf/lucy-clownfish/diff/e3ecf10d Branch: refs/heads/master Commit: e3ecf10da6a3314c31e60133041ed3578357387b Parents: 6fdf098 Author: Nick Wellnhofer <wellnho...@aevum.de> Authored: Fri Mar 4 18:00:49 2016 +0100 Committer: Nick Wellnhofer <wellnho...@aevum.de> Committed: Sat Mar 5 18:15:14 2016 +0100 ---------------------------------------------------------------------- compiler/c/t/cfclash/bar/Bar.cfh | 25 +++++ compiler/c/t/cfclash/bar/Bar.cfp | 4 + compiler/c/t/cfclash/bar/Baz.cfh | 25 +++++ compiler/c/t/cfclash/class/Animal/DogClash.cfh | 28 ++++++ compiler/c/t/cfclash/class/AnimalExtension.cfp | 5 + compiler/c/t/cfclash/file/Animal/Dog.cfh | 28 ++++++ compiler/c/t/cfclash/foo/Foo.cfh | 25 +++++ compiler/c/t/cfclash/foo/Foo.cfp | 4 + compiler/src/CFCTestCBlock.c | 19 +++- compiler/src/CFCTestClass.c | 105 +++++++++++++++++++- compiler/src/CFCTestFunction.c | 30 ++++-- compiler/src/CFCTestHierarchy.c | 99 +++++++++++++++++- compiler/src/CFCTestMethod.c | 67 ++++++++++++- compiler/src/CFCTestParcel.c | 87 +++++++++++++--- compiler/src/CFCTestSymbol.c | 29 +++++- compiler/src/CFCTestType.c | 59 ++++++++++- compiler/src/CFCTestVariable.c | 32 +++++- 17 files changed, 645 insertions(+), 26 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/bar/Bar.cfh ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/bar/Bar.cfh b/compiler/c/t/cfclash/bar/Bar.cfh new file mode 100644 index 0000000..89e798e --- /dev/null +++ b/compiler/c/t/cfclash/bar/Bar.cfh @@ -0,0 +1,25 @@ +/* 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 Bar; + +public class Bar inherits Clownfish::Obj { + int var; + + public void + Method(Bar *self); +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/bar/Bar.cfp ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/bar/Bar.cfp b/compiler/c/t/cfclash/bar/Bar.cfp new file mode 100644 index 0000000..e5868f6 --- /dev/null +++ b/compiler/c/t/cfclash/bar/Bar.cfp @@ -0,0 +1,4 @@ +{ + "name": "Bar", + "version": "v1.0.0" +} http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/bar/Baz.cfh ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/bar/Baz.cfh b/compiler/c/t/cfclash/bar/Baz.cfh new file mode 100644 index 0000000..00e4033 --- /dev/null +++ b/compiler/c/t/cfclash/bar/Baz.cfh @@ -0,0 +1,25 @@ +/* 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 Foo; + +public class Baz inherits Clownfish::Obj { + int var; + + public void + Method(Baz *self); +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/class/Animal/DogClash.cfh ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/class/Animal/DogClash.cfh b/compiler/c/t/cfclash/class/Animal/DogClash.cfh new file mode 100644 index 0000000..3eba020 --- /dev/null +++ b/compiler/c/t/cfclash/class/Animal/DogClash.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 AnimalExtension; + +class Animal::Dog inherits Clownfish::Obj { + public inert incremented Dog* + new(); + + public inert Dog* + init(Dog *self); + + public void + Bark(Dog *self); +} http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/class/AnimalExtension.cfp ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/class/AnimalExtension.cfp b/compiler/c/t/cfclash/class/AnimalExtension.cfp new file mode 100644 index 0000000..76f31d3 --- /dev/null +++ b/compiler/c/t/cfclash/class/AnimalExtension.cfp @@ -0,0 +1,5 @@ +{ + "name": "AnimalExtension", + "nickname": "AniExt", + "version": "v0.1.0" +} http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/file/Animal/Dog.cfh ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/file/Animal/Dog.cfh b/compiler/c/t/cfclash/file/Animal/Dog.cfh new file mode 100644 index 0000000..1357109 --- /dev/null +++ b/compiler/c/t/cfclash/file/Animal/Dog.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 Animal; + +class Animal::AnotherDog inherits Animal { + public inert incremented AnotherDog* + new(); + + public inert AnotherDog* + init(AnotherDog *self); + + public void + Bark(AnotherDog *self); +} http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/foo/Foo.cfh ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/foo/Foo.cfh b/compiler/c/t/cfclash/foo/Foo.cfh new file mode 100644 index 0000000..b770d8a --- /dev/null +++ b/compiler/c/t/cfclash/foo/Foo.cfh @@ -0,0 +1,25 @@ +/* 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 Foo; + +public class Foo inherits Clownfish::Obj { + int var; + + public void + Method(Foo *self); +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/c/t/cfclash/foo/Foo.cfp ---------------------------------------------------------------------- diff --git a/compiler/c/t/cfclash/foo/Foo.cfp b/compiler/c/t/cfclash/foo/Foo.cfp new file mode 100644 index 0000000..2995169 --- /dev/null +++ b/compiler/c/t/cfclash/foo/Foo.cfp @@ -0,0 +1,4 @@ +{ + "name": "Foo", + "version": "v1.0.0" +} http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestCBlock.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestCBlock.c b/compiler/src/CFCTestCBlock.c index 6061d44..650a3eb 100644 --- a/compiler/src/CFCTestCBlock.c +++ b/compiler/src/CFCTestCBlock.c @@ -14,18 +14,21 @@ * limitations under the License. */ +#include <string.h> + #define CFC_USE_TEST_MACROS #include "CFCBase.h" #include "CFCCBlock.h" #include "CFCParser.h" #include "CFCTest.h" +#include "CFCUtil.h" static void S_run_tests(CFCTest *test); const CFCTestBatch CFCTEST_BATCH_C_BLOCK = { "Clownfish::CFC::Model::CBlock", - 4, + 5, S_run_tests }; @@ -41,6 +44,20 @@ S_run_tests(CFCTest *test) { } { + CFCCBlock *block = NULL; + char *error; + + CFCUTIL_TRY { + block = CFCCBlock_new(NULL); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "contents"), "content required"); + + FREEMEM(error); + CFCBase_decref((CFCBase*)block); + } + + { const char *cblock_string = " __C__\n" "#define FOO_BAR 1\n" http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestClass.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestClass.c b/compiler/src/CFCTestClass.c index a91e3b9..8acb300 100644 --- a/compiler/src/CFCTestClass.c +++ b/compiler/src/CFCTestClass.c @@ -44,10 +44,25 @@ S_has_symbol(CFCSymbol **symbols, const char *name); const CFCTestBatch CFCTEST_BATCH_CLASS = { "Clownfish::CFC::Model::Class", - 86, + 96, S_run_tests }; +static char* +S_try_create(CFCParcel *parcel, const char *name, const char *nickname) { + CFCClass *klass = NULL; + char *error; + + CFCUTIL_TRY { + klass = CFCClass_create(parcel, NULL, name, nickname, NULL, NULL, NULL, + false, false, false); + } + CFCUTIL_CATCH(error); + + CFCBase_decref((CFCBase*)klass); + return error; +} + static void S_run_tests(CFCTest *test) { CFCParser *parser = CFCParser_new(); @@ -94,6 +109,27 @@ S_run_tests(CFCTest *test) { OK(test, should_be_foo == foo, "fetch_singleton"); } + { + char *error = S_try_create(neato, "Foo", NULL); + OK(test, error && strstr(error, "Two classes with name"), + "Can't call create for the same class more than once"); + FREEMEM(error); + } + + { + char *error = S_try_create(neato, "Other::Foo", NULL); + OK(test, error && strstr(error, "Class name conflict"), + "Can't create classes wth the same final component"); + FREEMEM(error); + } + + { + char *error = S_try_create(neato, "Bar", "Foo"); + OK(test, error && strstr(error, "Class nickname conflict"), + "Can't create classes wth the same nickname"); + FREEMEM(error); + } + CFCClass *foo_jr = CFCClass_create(neato, NULL, "Foo::FooJr", NULL, NULL, NULL, "Foo", false, false, false); @@ -125,6 +161,50 @@ S_run_tests(CFCTest *test) { = CFCTest_parse_method(test, parser, "void Do_Stuff(Foo *self);"); CFCClass_add_method(foo, do_stuff); + CFCClass *inert_foo + = CFCClass_create(neato, NULL, "InertFoo", NULL, NULL, NULL, NULL, + false, true, false); + + { + CFCParser_set_class_name(parser, "InertFoo"); + CFCMethod *inert_do_stuff + = CFCTest_parse_method(test, parser, + "void Do_Stuff(InertFoo *self);"); + char *error; + + CFCUTIL_TRY { + CFCClass_add_method(inert_foo, inert_do_stuff); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "inert class"), + "Error out on conflict between inert attribute and object method"); + + FREEMEM(error); + CFCBase_decref((CFCBase*)inert_do_stuff); + } + + { + char *error; + CFCUTIL_TRY { + CFCClass_add_child(foo, inert_foo); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "Inert class"), + "inert class can't inherit"); + FREEMEM(error); + } + + { + char *error; + CFCUTIL_TRY { + CFCClass_add_child(inert_foo, foo); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "from inert class"), + "can't inherit from inert class"); + FREEMEM(error); + } + CFCClass_resolve_types(foo); CFCClass_resolve_types(foo_jr); CFCClass_resolve_types(final_foo); @@ -133,6 +213,28 @@ S_run_tests(CFCTest *test) { CFCClass_add_child(foo_jr, final_foo); CFCClass_grow_tree(foo); + { + char *error; + CFCUTIL_TRY { + CFCClass_grow_tree(foo); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "grow_tree"), + "call grow_tree only once."); + FREEMEM(error); + } + + { + char *error; + CFCUTIL_TRY { + CFCClass_add_method(foo_jr, do_stuff); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "grow_tree"), + "Forbid add_method after grow_tree."); + FREEMEM(error); + } + OK(test, CFCClass_get_parent(foo_jr) == foo, "grow_tree, one level" ); OK(test, CFCClass_get_parent(final_foo) == foo_jr, "grow_tree, two levels"); @@ -334,6 +436,7 @@ S_run_tests(CFCTest *test) { CFCBase_decref((CFCBase*)foo); CFCBase_decref((CFCBase*)foo_jr); CFCBase_decref((CFCBase*)final_foo); + CFCBase_decref((CFCBase*)inert_foo); CFCBase_decref((CFCBase*)do_stuff); CFCClass_clear_registry(); http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestFunction.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestFunction.c b/compiler/src/CFCTestFunction.c index 22dd487..459fea1 100644 --- a/compiler/src/CFCTestFunction.c +++ b/compiler/src/CFCTestFunction.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <string.h> + #define CFC_USE_TEST_MACROS #include "CFCBase.h" #include "CFCFunction.h" @@ -22,13 +24,14 @@ #include "CFCParser.h" #include "CFCTest.h" #include "CFCType.h" +#include "CFCUtil.h" static void S_run_tests(CFCTest *test); const CFCTestBatch CFCTEST_BATCH_FUNCTION = { "Clownfish::CFC::Model::Function", - 11, + 12, S_run_tests }; @@ -37,17 +40,30 @@ S_run_tests(CFCTest *test) { CFCParser *parser = CFCParser_new(); CFCParcel *neato_parcel = CFCTest_parse_parcel(test, parser, "parcel Neato;"); + CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*"); + CFCParamList *param_list + = CFCTest_parse_param_list(test, parser, "(int32_t some_num)"); { - CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*"); - CFCParamList *param_list - = CFCTest_parse_param_list(test, parser, "(int32_t some_num)"); CFCFunction *func = CFCFunction_new(NULL, "return_an_obj", return_type, param_list, NULL, 0); OK(test, func != NULL, "new"); + CFCBase_decref((CFCBase*)func); + } + + { + CFCFunction *func = NULL; + char *error; + + CFCUTIL_TRY { + func = CFCFunction_new(NULL, "Uh_Oh", return_type, param_list, + NULL, 0); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "Uh_Oh"), + "invalid name kills constructor"); - CFCBase_decref((CFCBase*)return_type); - CFCBase_decref((CFCBase*)param_list); + FREEMEM(error); CFCBase_decref((CFCBase*)func); } @@ -65,6 +81,8 @@ S_run_tests(CFCTest *test) { } } + CFCBase_decref((CFCBase*)return_type); + CFCBase_decref((CFCBase*)param_list); CFCBase_decref((CFCBase*)neato_parcel); CFCBase_decref((CFCBase*)parser); http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestHierarchy.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestHierarchy.c b/compiler/src/CFCTestHierarchy.c index fa005aa..670f872 100644 --- a/compiler/src/CFCTestHierarchy.c +++ b/compiler/src/CFCTestHierarchy.c @@ -17,6 +17,7 @@ #include "charmony.h" #include <stdio.h> +#include <stdlib.h> #include <string.h> /* For rmdir */ @@ -41,6 +42,10 @@ #define T_CFDEST "t" CHY_DIR_SEP "cfdest" #define T_CFDEST_INCLUDE T_CFDEST CHY_DIR_SEP "include" #define T_CFDEST_SOURCE T_CFDEST CHY_DIR_SEP "source" +#define T_CFCLASH_CLASS "t" CHY_DIR_SEP "cfclash" CHY_DIR_SEP "class" +#define T_CFCLASH_FILE "t" CHY_DIR_SEP "cfclash" CHY_DIR_SEP "file" +#define T_CFCLASH_FOO "t" CHY_DIR_SEP "cfclash" CHY_DIR_SEP "foo" +#define T_CFCLASH_BAR "t" CHY_DIR_SEP "cfclash" CHY_DIR_SEP "bar" static void S_run_tests(CFCTest *test); @@ -51,9 +56,12 @@ S_run_basic_tests(CFCTest *test); static void S_run_include_tests(CFCTest *test); +static void +S_run_clash_tests(CFCTest *test); + const CFCTestBatch CFCTEST_BATCH_HIERARCHY = { "Clownfish::CFC::Model::Hierarchy", - 44, + 48, S_run_tests }; @@ -61,6 +69,7 @@ static void S_run_tests(CFCTest *test) { S_run_basic_tests(test); S_run_include_tests(test); + S_run_clash_tests(test); } static void @@ -263,3 +272,91 @@ S_run_include_tests(CFCTest *test) { rmdir(T_CFDEST); } +static void +S_run_clash_tests(CFCTest *test) { + if (getenv("LUCY_VALGRIND")) { + SKIP(test, 1, "Exceptions leak"); + } + else { + CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); + CFCHierarchy_add_source_dir(hierarchy, T_CFBASE); + CFCHierarchy_add_source_dir(hierarchy, T_CFCLASH_FILE); + char *error; + + CFCUTIL_TRY { + CFCHierarchy_build(hierarchy); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "found twice"), + "source/source filename clash"); + + CFCBase_decref((CFCBase*)hierarchy); + CFCClass_clear_registry(); + CFCParcel_reap_singletons(); + } + + if (getenv("LUCY_VALGRIND")) { + SKIP(test, 1, "Exceptions leak"); + } + else { + CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); + CFCHierarchy_add_source_dir(hierarchy, T_CFCLASH_CLASS); + CFCHierarchy_add_include_dir(hierarchy, T_CFBASE); + char *error; + + CFCUTIL_TRY { + CFCHierarchy_build(hierarchy); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "Two classes with name"), + "source/include class name clash"); + + CFCBase_decref((CFCBase*)hierarchy); + CFCClass_clear_registry(); + CFCParcel_reap_singletons(); + } + + { + CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); + CFCHierarchy_add_source_dir(hierarchy, T_CFBASE); + CFCHierarchy_add_include_dir(hierarchy, T_CFCLASH_FILE); + + CFCHierarchy_build(hierarchy); + CFCClass **ordered = CFCHierarchy_ordered_classes(hierarchy); + int count = 0; + while (ordered[count]) { count++; } + INT_EQ(test, count, 4, "source/include filename clash"); + + FREEMEM(ordered); + CFCBase_decref((CFCBase*)hierarchy); + CFCClass_clear_registry(); + CFCParcel_reap_singletons(); + } + + if (getenv("LUCY_VALGRIND")) { + SKIP(test, 1, "Exceptions leak"); + } + else { + CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST); + CFCHierarchy_add_source_dir(hierarchy, T_CFCLASH_BAR); + CFCHierarchy_add_include_dir(hierarchy, T_CFCLASH_FOO); + CFCHierarchy_add_include_dir(hierarchy, T_CFBASE); + char *error; + + CFCUTIL_TRY { + CFCHierarchy_build(hierarchy); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "from source dir found"), + "source class with included parcel"); + + CFCBase_decref((CFCBase*)hierarchy); + CFCClass_clear_registry(); + CFCParcel_reap_singletons(); + } + + rmdir(T_CFDEST_INCLUDE); + rmdir(T_CFDEST_SOURCE); + rmdir(T_CFDEST); +} + http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestMethod.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestMethod.c b/compiler/src/CFCTestMethod.c index 3fc5d57..d92bbdc 100644 --- a/compiler/src/CFCTestMethod.c +++ b/compiler/src/CFCTestMethod.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <string.h> + #define CFC_USE_TEST_MACROS #include "CFCBase.h" #include "CFCClass.h" @@ -24,6 +26,7 @@ #include "CFCSymbol.h" #include "CFCTest.h" #include "CFCType.h" +#include "CFCUtil.h" static void S_run_tests(CFCTest *test); @@ -42,7 +45,7 @@ S_run_final_tests(CFCTest *test); const CFCTestBatch CFCTEST_BATCH_METHOD = { "Clownfish::CFC::Model::Method", - 74, + 84, S_run_tests }; @@ -54,6 +57,22 @@ S_run_tests(CFCTest *test) { S_run_final_tests(test); } +static char* +S_try_new_method(const char *name, CFCType *return_type, + CFCParamList *param_list, const char *class_name) { + CFCMethod *method = NULL; + char *error; + + CFCUTIL_TRY { + method = CFCMethod_new(NULL, name, return_type, param_list, NULL, + class_name, 0, 0); + } + CFCUTIL_CATCH(error); + + CFCBase_decref((CFCBase*)method); + return error; +} + static void S_run_basic_tests(CFCTest *test) { CFCParser *parser = CFCParser_new(); @@ -72,6 +91,39 @@ S_run_basic_tests(CFCTest *test) { "parcel exposure by default"); { + char *error = S_try_new_method("return_an_obj", return_type, + param_list, "Neato::Foo"); + OK(test, error && strstr(error, "name"), + "invalid name kills constructor"); + FREEMEM(error); + } + + { + static const char *bad_class_names[4] = { + "foo", "1Foo", "Foo_Bar", "1FOOBAR" + }; + for (int i = 0; i < 4; i++) { + const char *bad_class_name = bad_class_names[i]; + char *error; + + error = S_try_new_method("Return_An_Obj", return_type, + param_list, bad_class_name); + OK(test, error && strstr(error, "class_name"), + "Reject invalid class name %s", bad_class_name); + FREEMEM(error); + + char *bogus_middle + = CFCUtil_sprintf("Foo::%s::Bar", bad_class_name); + error = S_try_new_method("Return_An_Obj", return_type, + param_list, bogus_middle); + OK(test, error && strstr(error, "class_name"), + "Reject invalid class name %s", bogus_middle); + FREEMEM(error); + FREEMEM(bogus_middle); + } + } + + { CFCMethod *dupe = CFCMethod_new(NULL, "Return_An_Obj", return_type, param_list, NULL, "Neato::Foo", 0, 0); @@ -260,6 +312,19 @@ S_run_final_tests(CFCTest *test) { OK(test, !CFCMethod_final(not_final), "not final by default"); OK(test, CFCMethod_final(final), "finalize"); + { + char *error; + + CFCUTIL_TRY { + CFCMethod_override(not_final, final); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "final"), + "Can't override final method"); + + FREEMEM(error); + } + CFCBase_decref((CFCBase*)parser); CFCBase_decref((CFCBase*)neato_parcel); CFCBase_decref((CFCBase*)obj_class); http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestParcel.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestParcel.c b/compiler/src/CFCTestParcel.c index eec782a..263c60e 100644 --- a/compiler/src/CFCTestParcel.c +++ b/compiler/src/CFCTestParcel.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <string.h> + #include "charmony.h" #define CFC_USE_TEST_MACROS @@ -37,18 +39,22 @@ static void S_run_prereq_tests(CFCTest *test); static void -S_run_parcel_tests(CFCTest *test); +S_run_basic_tests(CFCTest *test); + +static void +S_run_extended_tests(CFCTest *test); const CFCTestBatch CFCTEST_BATCH_PARCEL = { "Clownfish::CFC::Model::Parcel", - 29, + 36, S_run_tests }; static void S_run_tests(CFCTest *test) { S_run_prereq_tests(test); - S_run_parcel_tests(test); + S_run_basic_tests(test); + S_run_extended_tests(test); } static void @@ -77,22 +83,77 @@ S_run_prereq_tests(CFCTest *test) { } static void -S_run_parcel_tests(CFCTest *test) { +S_run_basic_tests(CFCTest *test) { + CFCParcel *foo = CFCParcel_new("Foo", NULL, NULL, NULL); + OK(test, foo != NULL, "new"); + OK(test, !CFCParcel_included(foo), "not included"); + CFCParcel_register(foo); + { - CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, NULL); - OK(test, parcel != NULL, "new"); - OK(test, !CFCParcel_included(parcel), "not included"); - CFCBase_decref((CFCBase*)parcel); + CFCParcel *same_name = CFCParcel_new("Foo", NULL, NULL, NULL); + char *error; + + CFCUTIL_TRY { + CFCParcel_register(same_name); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "already registered"), + "can't register two parcels with the same name"); + + FREEMEM(error); + CFCBase_decref((CFCBase*)same_name); } { - CFCFileSpec *file_spec = CFCFileSpec_new(".", "Parcel", true); - CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, file_spec); - OK(test, CFCParcel_included(parcel), "included"); - CFCBase_decref((CFCBase*)parcel); - CFCBase_decref((CFCBase*)file_spec); + CFCParcel *same_nick + = CFCParcel_new("OtherFoo", "Foo", NULL, NULL); + char *error; + + CFCUTIL_TRY { + CFCParcel_register(same_nick); + } + CFCUTIL_CATCH(error); + OK(test, error && strstr(error, "already registered"), + "can't register two parcels with the same nickname"); + + FREEMEM(error); + CFCBase_decref((CFCBase*)same_nick); } + CFCFileSpec *file_spec = CFCFileSpec_new(".", "Parcel", true); + CFCParcel *included_foo + = CFCParcel_new("IncludedFoo", NULL, NULL, file_spec); + OK(test, CFCParcel_included(included_foo), "included"); + CFCParcel_register(included_foo); + + { + CFCParcel **all_parcels = CFCParcel_all_parcels(); + OK(test, all_parcels[0] && all_parcels[1] && !all_parcels[2], + "all_parcels returns two parcels"); + STR_EQ(test, CFCParcel_get_name(all_parcels[0]), "Foo", + "all_parcels returns parcel Foo"); + STR_EQ(test, CFCParcel_get_name(all_parcels[1]), "IncludedFoo", + "all_parcels returns parcel IncludedFoo"); + } + + { + CFCParcel_add_inherited_parcel(foo, included_foo); + CFCParcel **inh_parcels = CFCParcel_inherited_parcels(foo); + OK(test, inh_parcels[0] && !inh_parcels[1], + "inherited_parcels returns one parcel"); + STR_EQ(test, CFCParcel_get_name(inh_parcels[0]), "IncludedFoo", + "inh_parcels returns parcel IncludedFoo"); + FREEMEM(inh_parcels); + } + + CFCBase_decref((CFCBase*)included_foo); + CFCBase_decref((CFCBase*)file_spec); + CFCBase_decref((CFCBase*)foo); + CFCParcel_reap_singletons(); +} + +static void +S_run_extended_tests(CFCTest *test) { { const char *json = " {\n" http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestSymbol.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestSymbol.c b/compiler/src/CFCTestSymbol.c index 690e55c..e177d50 100644 --- a/compiler/src/CFCTestSymbol.c +++ b/compiler/src/CFCTestSymbol.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <string.h> + #define CFC_USE_TEST_MACROS #include "CFCBase.h" #include "CFCClass.h" @@ -32,10 +34,24 @@ S_run_tests(CFCTest *test); const CFCTestBatch CFCTEST_BATCH_SYMBOL = { "Clownfish::CFC::Model::Symbol", - 20, + 24, S_run_tests }; +static char* +S_try_new_symbol(const char *name) { + CFCSymbol *symbol = NULL; + char *error; + + CFCUTIL_TRY { + symbol = CFCSymbol_new("parcel", name); + } + CFCUTIL_CATCH(error); + + CFCBase_decref((CFCBase*)symbol); + return error; +} + static void S_run_tests(CFCTest *test) { CFCParcel *parcel = CFCParcel_new("Parcel", NULL, NULL, NULL); @@ -76,6 +92,17 @@ S_run_tests(CFCTest *test) { } { + static const char *names[4] = { + "1foo", "*", "0", "\xE2\x98\xBA" + }; + for (int i = 0; i < 4; i++) { + char *error = S_try_new_symbol(names[i]); + OK(test, error && strstr(error, "name"), "reject bad name"); + FREEMEM(error); + } + } + + { CFCSymbol *ooga = CFCSymbol_new("parcel", "ooga"); CFCSymbol *booga = CFCSymbol_new("parcel", "booga"); int equal = CFCSymbol_equals(ooga, booga); http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestType.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestType.c b/compiler/src/CFCTestType.c index 5e0d6df..ea61fa0 100644 --- a/compiler/src/CFCTestType.c +++ b/compiler/src/CFCTestType.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <string.h> + #define CFC_USE_TEST_MACROS #include "CFCBase.h" #include "CFCClass.h" @@ -60,7 +62,7 @@ S_run_composite_tests(CFCTest *test); const CFCTestBatch CFCTEST_BATCH_TYPE = { "Clownfish::CFC::Model::Type", - 360, + 369, S_run_tests }; @@ -260,6 +262,20 @@ S_run_void_tests(CFCTest *test) { CFCBase_decref((CFCBase*)parser); } +static char* +S_try_new_object(CFCParcel *parcel, const char *specifier, int indirection) { + CFCType *type = NULL; + char *error; + + CFCUTIL_TRY { + type = CFCType_new_object(0, parcel, specifier, indirection); + } + CFCUTIL_CATCH(error); + + CFCBase_decref((CFCBase*)type); + return error; +} + static void S_run_object_tests(CFCTest *test) { static const char *modifiers[4] = { @@ -336,6 +352,33 @@ S_run_object_tests(CFCTest *test) { 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"); @@ -494,6 +537,20 @@ S_run_composite_tests(CFCTest *test) { } { + 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); http://git-wip-us.apache.org/repos/asf/lucy-clownfish/blob/e3ecf10d/compiler/src/CFCTestVariable.c ---------------------------------------------------------------------- diff --git a/compiler/src/CFCTestVariable.c b/compiler/src/CFCTestVariable.c index d256c01..617bf15 100644 --- a/compiler/src/CFCTestVariable.c +++ b/compiler/src/CFCTestVariable.c @@ -14,6 +14,8 @@ * limitations under the License. */ +#include <string.h> + #define CFC_USE_TEST_MACROS #include "CFCBase.h" #include "CFCClass.h" @@ -35,10 +37,24 @@ S_run_tests(CFCTest *test); const CFCTestBatch CFCTEST_BATCH_VARIABLE = { "Clownfish::CFC::Model::Variable", - 29, + 33, S_run_tests }; +static char* +S_try_new_variable(const char *name, CFCType *type) { + CFCVariable *var = NULL; + char *error; + + CFCUTIL_TRY { + var = CFCVariable_new(NULL, name, type, 0); + } + CFCUTIL_CATCH(error); + + CFCBase_decref((CFCBase*)var); + return error; +} + static void S_run_tests(CFCTest *test) { CFCParser *parser = CFCParser_new(); @@ -47,6 +63,20 @@ S_run_tests(CFCTest *test) { CFCClass *foo_class = CFCTest_parse_class(test, parser, "class Foo {}"); { + char *error = S_try_new_variable("foo", NULL); + OK(test, error && strstr(error, "type"), "type is required"); + FREEMEM(error); + } + + { + CFCType *type = CFCTest_parse_type(test, parser, "int32_t"); + char *error = S_try_new_variable(NULL, type); + OK(test, error && strstr(error, "name"), "name is required"); + FREEMEM(error); + CFCBase_decref((CFCBase*)type); + } + + { CFCType *type = CFCTest_parse_type(test, parser, "float*"); CFCVariable *var = CFCVariable_new(NULL, "foo", type, 0); CFCVariable_resolve_type(var);