You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucy.apache.org by nw...@apache.org on 2016/03/10 14:35:22 UTC

[05/12] lucy-clownfish git commit: Test CFC exceptions in C test suite

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 <we...@aevum.de>
Authored: Fri Mar 4 18:00:49 2016 +0100
Committer: Nick Wellnhofer <we...@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);