Example #1
0
static void
S_run_parser_tests(CFCTest *test) {
    CFCParser *parser = CFCParser_new();
    CFCParcel *neato_parcel
        = CFCTest_parse_parcel(test, parser, "parcel Neato;");
    CFCParser_set_class_name(parser, "Neato::Obj");
    CFCParser_set_class_nickname(parser, "Obj");

    {
        static const char *method_strings[4] = {
            "public int Do_Foo(Obj *self);",
            "Obj* Gimme_An_Obj(Obj *self);",
            "void Do_Whatever(Obj *self, uint32_t a_num, float real);",
            "Foo* Fetch_Foo(Obj *self, int num);",
        };
        for (int i = 0; i < 4; ++i) {
            CFCMethod *method
                = CFCTest_parse_method(test, parser, method_strings[i]);
            CFCBase_decref((CFCBase*)method);
        }
    }

    {
        CFCMethod *method
            = CFCTest_parse_method(test, parser,
                                   "public final void The_End(Obj *self);");
        OK(test, CFCMethod_final(method), "final");
        CFCBase_decref((CFCBase*)method);
    }

    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)parser);

    CFCParcel_reap_singletons();
}
Example #2
0
static void
S_run_overridden_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, "(Foo *self)");
    CFCMethod *orig
        = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                        "Return_An_Obj", return_type, param_list,
                        NULL, 0, 0);

    CFCParamList *overrider_param_list
        = CFCTest_parse_param_list(test, parser, "(FooJr *self)");
    CFCMethod *overrider
        = CFCMethod_new(neato_parcel, NULL, "Neato::Foo::FooJr", "FooJr",
                        "Return_An_Obj", return_type, overrider_param_list,
                        NULL, 0, 0);

    CFCMethod_override(overrider, orig);
    OK(test, !CFCMethod_novel(overrider),
       "A Method which overrides another is not 'novel'");

    CFCBase_decref((CFCBase*)parser);
    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)return_type);
    CFCBase_decref((CFCBase*)param_list);
    CFCBase_decref((CFCBase*)orig);
    CFCBase_decref((CFCBase*)overrider_param_list);
    CFCBase_decref((CFCBase*)overrider);

    CFCParcel_reap_singletons();
}
Example #3
0
static void
S_run_basic_tests(CFCTest *test) {
    CFCParcel *neato_parcel = CFCParcel_new("Neato", 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, is_string_type);
    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();
}
Example #4
0
static void
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)");

    {
        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");

        FREEMEM(error);
        CFCBase_decref((CFCBase*)func);
    }

    {
        CFCClass *neato_obj
            = CFCClass_create(neato_parcel, NULL, "Neato::Obj", NULL, NULL,
                              NULL, NULL, false, false, false);
        CFCParser_set_class(parser, neato_obj);
        static const char *func_strings[2] = {
            "inert int running_count(int biscuit);",
            "public inert Hash* init_fave_hash(int32_t num_buckets,"
            " bool o_rly);"
        };
        for (int i = 0; i < 2; ++i) {
            CFCFunction *func
                = CFCTest_parse_function(test, parser, func_strings[i]);
            CFCBase_decref((CFCBase*)func);
        }
        CFCBase_decref((CFCBase*)neato_obj);
    }

    CFCBase_decref((CFCBase*)return_type);
    CFCBase_decref((CFCBase*)param_list);
    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)parser);

    CFCParcel_reap_singletons();
}
Example #5
0
static void
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)");
        CFCFunction *func
            = CFCFunction_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                              "return_an_obj", return_type, param_list,
                              NULL, 0);
        OK(test, func != NULL, "new");

        CFCBase_decref((CFCBase*)return_type);
        CFCBase_decref((CFCBase*)param_list);
        CFCBase_decref((CFCBase*)func);
    }

    {
        CFCParser_set_class_name(parser, "Neato::Obj");
        CFCParser_set_class_cnick(parser, "Obj");
        static const char *func_strings[2] = {
            "inert int running_count(int biscuit);",
            "public inert Hash* init_fave_hash(int32_t num_buckets,"
            " bool o_rly);"
        };
        for (int i = 0; i < 2; ++i) {
            CFCFunction *func
                = CFCTest_parse_function(test, parser, func_strings[i]);
            CFCBase_decref((CFCBase*)func);
        }
    }

    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)parser);

    CFCParcel_reap_singletons();
}
Example #6
0
static void
S_run_arbitrary_tests(CFCTest *test) {
    {
        CFCParcel *neato_parcel = CFCParcel_new("Neato", 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();
}
Example #7
0
static void
S_run_final_tests(CFCTest *test) {
    CFCParser *parser = CFCParser_new();
    CFCParcel *neato_parcel
        = CFCTest_parse_parcel(test, parser, "parcel Neato;");
    CFCClass *obj_class
        = CFCTest_parse_class(test, parser, "class Obj {}");
    CFCClass *foo_class
        = CFCTest_parse_class(test, parser, "class Neato::Foo {}");
    CFCType *return_type = CFCTest_parse_type(test, parser, "Obj*");
    CFCParamList *param_list
        = CFCTest_parse_param_list(test, parser, "(Foo *self)");

    CFCMethod *not_final
        = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                        "Return_An_Obj", return_type, param_list,
                        NULL, 0, 0);
    CFCMethod_resolve_types(not_final);
    CFCMethod *final = CFCMethod_finalize(not_final);
    OK(test, CFCMethod_compatible(not_final, final),
       "finalize clones properly");
    OK(test, !CFCMethod_final(not_final), "not final by default");
    OK(test, CFCMethod_final(final), "finalize");

    CFCBase_decref((CFCBase*)parser);
    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)obj_class);
    CFCBase_decref((CFCBase*)foo_class);
    CFCBase_decref((CFCBase*)return_type);
    CFCBase_decref((CFCBase*)param_list);
    CFCBase_decref((CFCBase*)not_final);
    CFCBase_decref((CFCBase*)final);

    CFCClass_clear_registry();
    CFCParcel_reap_singletons();
}
Example #8
0
int
main(int argc, char **argv) {
    int           i;
    size_t        file_len;
    CFCArgs       args;
    CFCHierarchy *hierarchy;
    CFCBindCore  *core_binding;
    CFCC         *c_binding;
    char         *header = NULL;
    char         *footer = NULL;
    const char   *include_env;

    S_parse_arguments(argc, argv, &args);

    hierarchy = CFCHierarchy_new(args.dest);

    for (i = 0; args.source_dirs[i]; ++i) {
        CFCHierarchy_add_source_dir(hierarchy, args.source_dirs[i]);
    }
    for (i = 0; args.include_dirs[i]; ++i) {
        CFCHierarchy_add_include_dir(hierarchy, args.include_dirs[i]);
    }

    /* Add include dirs from environment variable CLOWNFISH_INCLUDE. */
    include_env = getenv("CLOWNFISH_INCLUDE");
    if (include_env != NULL) {
        char *include_env_copy = CFCUtil_strdup(include_env);
        const char *include_dir;

        for (include_dir = strtok(include_env_copy, ":");
             include_dir != NULL;
             include_dir = strtok(NULL, ":")
        ) {
            if (include_dir[0] != '\0') {
                CFCHierarchy_add_include_dir(hierarchy, include_dir);
            }
        }

        FREEMEM(include_env_copy);
    }
    else if (UNIX_FILESYSTEM) {
        /*
         * Only add system include dirs if CLOWNFISH_INCLUDE is unset to
         * avoid errors when a parcel is found in multiple locations.
         */
        CFCHierarchy_add_include_dir(hierarchy, "/usr/local/" SYS_INCLUDE_DIR);
        CFCHierarchy_add_include_dir(hierarchy, "/usr/" SYS_INCLUDE_DIR);
    }

    for (i = 0; args.parcels[i]; ++i) {
        CFCHierarchy_add_prereq(hierarchy, args.parcels[i]);
    }

    CFCHierarchy_build(hierarchy);

    if (args.header_filename) {
        header = CFCUtil_slurp_text(args.header_filename, &file_len);
    }
    else {
        header = CFCUtil_strdup("");
    }
    if (args.footer_filename) {
        footer = CFCUtil_slurp_text(args.footer_filename, &file_len);
    }
    else {
        footer = CFCUtil_strdup("");
    }

    core_binding = CFCBindCore_new(hierarchy, header, footer);
    CFCBindCore_write_all_modified(core_binding, 0);

    c_binding = CFCC_new(hierarchy, header, footer);
    CFCC_write_hostdefs(c_binding);
    if (args.num_source_dirs != 0) {
        CFCC_write_html_docs(c_binding);
        CFCC_write_man_pages(c_binding);
    }

    CFCHierarchy_write_log(hierarchy);

    CFCBase_decref((CFCBase*)c_binding);
    CFCBase_decref((CFCBase*)core_binding);
    CFCBase_decref((CFCBase*)hierarchy);
    FREEMEM(header);
    FREEMEM(footer);

    CFCClass_clear_registry();
    CFCParcel_reap_singletons();

    S_free_arguments(&args);

    return EXIT_SUCCESS;
}
Example #9
0
static void
S_run_basic_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,
                                   "(Foo *self, int32_t count = 0)");
    CFCMethod *method
        = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                        "Return_An_Obj", return_type, param_list,
                        NULL, 0, 0);
    OK(test, method != NULL, "new");
    OK(test, CFCSymbol_parcel((CFCSymbol*)method),
       "parcel exposure by default");

    {
        CFCMethod *dupe
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                            "Return_An_Obj", return_type, param_list,
                            NULL, 0, 0);
        OK(test, CFCMethod_compatible(method, dupe), "compatible");
        CFCBase_decref((CFCBase*)dupe);
    }

    {
        CFCMethod *macro_sym_differs
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo", "Eat",
                            return_type, param_list, NULL, 0, 0);
        OK(test, !CFCMethod_compatible(method, macro_sym_differs),
           "different macro_sym spoils compatible");
        OK(test, !CFCMethod_compatible(macro_sym_differs, method),
           "... reversed");
        CFCBase_decref((CFCBase*)macro_sym_differs);
    }

    {
        static const char *param_strings[5] = {
            "(Foo *self, int32_t count = 0, int b)",
            "(Foo *self, int32_t count = 1)",
            "(Foo *self, int32_t count)",
            "(Foo *self, int32_t countess = 0)",
            "(Foo *self, uint32_t count = 0)"
        };
        static const char *test_names[5] = {
            "extra param",
            "different initial_value",
            "missing initial_value",
            "different param name",
            "different param type"
        };
        for (int i = 0; i < 5; ++i) {
            CFCParamList *other_param_list
                = CFCTest_parse_param_list(test, parser, param_strings[i]);
            CFCMethod *other
                = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                                "Return_An_Obj", return_type, other_param_list,
                                NULL, 0, 0);
            OK(test, !CFCMethod_compatible(method, other),
               "%s spoils compatible", test_names[i]);
            OK(test, !CFCMethod_compatible(other, method),
               "... reversed");
            CFCBase_decref((CFCBase*)other_param_list);
            CFCBase_decref((CFCBase*)other);
        }
    }

    {
        CFCParamList *self_differs_list
            = CFCTest_parse_param_list(test, parser,
                                       "(Bar *self, int32_t count = 0)");
        CFCMethod *self_differs
            = CFCMethod_new(neato_parcel, NULL, "Neato::Bar", "Bar",
                            "Return_An_Obj", return_type, self_differs_list,
                            NULL, 0, 0);
        OK(test, CFCMethod_compatible(method, self_differs),
           "different self type still compatible(),"
           " since can't test inheritance");
        OK(test, CFCMethod_compatible(self_differs, method),
           "... reversed");
        CFCBase_decref((CFCBase*)self_differs_list);
        CFCBase_decref((CFCBase*)self_differs);
    }

    {
        CFCMethod *aliased
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                            "Aliased", return_type, param_list,
                            NULL, 0, 0);
        OK(test, !CFCMethod_get_host_alias(aliased),
           "no host alias by default");
        CFCMethod_set_host_alias(aliased, "Host_Alias");
        STR_EQ(test, CFCMethod_get_host_alias(aliased), "Host_Alias",
               "set/get host alias");
        CFCBase_decref((CFCBase*)aliased);
    }

    {
        CFCMethod *excluded
            = CFCMethod_new(neato_parcel, NULL, "Neato::Foo", "Foo",
                            "Excluded", return_type, param_list,
                            NULL, 0, 0);
        OK(test, !CFCMethod_excluded_from_host(excluded),
           "not excluded by default");
        CFCMethod_exclude_from_host(excluded);
        OK(test, CFCMethod_excluded_from_host(excluded), "exclude from host");
        CFCBase_decref((CFCBase*)excluded);
    }

    CFCBase_decref((CFCBase*)parser);
    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)return_type);
    CFCBase_decref((CFCBase*)param_list);
    CFCBase_decref((CFCBase*)method);

    CFCParcel_reap_singletons();
}
Example #10
0
static void
S_run_tests(CFCTest *test) {
#define STUFF_THING "Stuff" CHY_DIR_SEP "Thing"

    CFCParser *parser = CFCParser_new();
    CFCFileSpec *file_spec = CFCFileSpec_new(".", STUFF_THING, 0);

    {
        const char *string =
            "parcel Stuff;\n"
            "class Stuff::Thing {\n"
            "    Foo *foo;\n"
            "    Bar *bar;\n"
            "}\n"
            "class Foo {}\n"
            "class Bar {}\n"
            "__C__\n"
            "int foo;\n"
            "__END_C__\n";
        CFCFile *file = CFCParser_parse_file(parser, string, file_spec);

        STR_EQ(test, CFCFile_get_source_dir(file), ".", "get_source_dir");
        STR_EQ(test, CFCFile_get_path_part(file), STUFF_THING,
               "get_path_part");
        OK(test, !CFCFile_included(file), "included");

        STR_EQ(test, CFCFile_guard_name(file), "H_STUFF_THING", "guard_name");
        STR_EQ(test, CFCFile_guard_start(file),
               "#ifndef H_STUFF_THING\n#define H_STUFF_THING 1\n",
               "guard_start");
        STR_EQ(test, CFCFile_guard_close(file), "#endif /* H_STUFF_THING */\n",
               "guard_close");

        OK(test, !CFCFile_get_modified(file), "modified false at start");
        CFCFile_set_modified(file, 1);
        OK(test, CFCFile_get_modified(file), "set_modified, get_modified");

#define PATH_TO_STUFF_THING \
    "path" CHY_DIR_SEP \
    "to" CHY_DIR_SEP \
    "Stuff" CHY_DIR_SEP \
    "Thing"

        char *cfh_path = CFCFile_cfh_path(file, "path/to");
        STR_EQ(test, cfh_path, PATH_TO_STUFF_THING ".cfh", "cfh_path");
        FREEMEM(cfh_path);
        char *c_path = CFCFile_c_path(file, "path/to");
        STR_EQ(test, c_path, PATH_TO_STUFF_THING ".c", "c_path");
        FREEMEM(c_path);
        char *h_path = CFCFile_h_path(file, "path/to");
        STR_EQ(test, h_path, PATH_TO_STUFF_THING ".h", "h_path");
        FREEMEM(h_path);

        CFCClass **classes = CFCFile_classes(file);
        OK(test,
           classes[0] != NULL && classes[1] != NULL && classes[2] != NULL
           && classes[3] == NULL,
           "classes() filters blocks");
        CFCVariable **member_vars = CFCClass_fresh_member_vars(classes[0]);
        CFCType *foo_type = CFCVariable_get_type(member_vars[0]);
        CFCType_resolve(foo_type);
        STR_EQ(test, CFCType_get_specifier(foo_type), "stuff_Foo",
               "file production picked up parcel def");
        CFCType *bar_type = CFCVariable_get_type(member_vars[1]);
        CFCType_resolve(bar_type);
        STR_EQ(test, CFCType_get_specifier(bar_type), "stuff_Bar",
               "parcel def is sticky");

        CFCParcel *parcel = CFCFile_get_parcel(file);
        STR_EQ(test, CFCParcel_get_name(parcel), "Stuff", "get_parcel");

        CFCBase **blocks = CFCFile_blocks(file);
        STR_EQ(test, CFCBase_get_cfc_class(blocks[0]),
               "Clownfish::CFC::Model::Class", "blocks[0]");
        STR_EQ(test, CFCBase_get_cfc_class(blocks[1]),
               "Clownfish::CFC::Model::Class", "blocks[1]");
        STR_EQ(test, CFCBase_get_cfc_class(blocks[2]),
               "Clownfish::CFC::Model::Class", "blocks[2]");
        STR_EQ(test, CFCBase_get_cfc_class(blocks[3]),
               "Clownfish::CFC::Model::CBlock", "blocks[3]");
        OK(test, blocks[4] == NULL, "blocks[4]");

        CFCBase_decref((CFCBase*)file);

        CFCClass_clear_registry();
    }

    CFCBase_decref((CFCBase*)file_spec);
    CFCBase_decref((CFCBase*)parser);

    CFCParcel_reap_singletons();
}
Example #11
0
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);
            CFCClass_clear_registry();
        }

        CFCBase_decref((CFCBase*)neato_parcel);
        CFCBase_decref((CFCBase*)parser);
    }

    CFCParcel *neato_parcel = CFCParcel_new("Neato", NULL, NULL, NULL);
    CFCClass *foo_class
        = CFCClass_create(neato_parcel, NULL, "Foo", NULL, NULL, NULL, NULL,
                          NULL, false, false, false);
    CFCType *foo = CFCType_new_object(0, neato_parcel, "Foo", 1);
    CFCType_resolve(foo);

    {
        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, 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);
        CFCClass *foreign_foo_class
            = CFCClass_create(foreign_parcel, NULL, "Foreign::Foo", NULL, 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_is_string_type(string_type), "%s", "is_string_type");
        OK(test, !CFCType_is_string_type(foo), "not %s", "not is_string_type");
        CFCBase_decref((CFCBase*)string_type);
    }

    CFCBase_decref((CFCBase*)neato_parcel);
    CFCBase_decref((CFCBase*)foo_class);
    CFCBase_decref((CFCBase*)foo);

    CFCClass_clear_registry();
    CFCParcel_reap_singletons();
}
static void
S_run_basic_tests(CFCTest *test) {
    CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
    STR_EQ(test, CFCHierarchy_get_dest(hierarchy), T_CFDEST, "get_dest");
    STR_EQ(test, CFCHierarchy_get_include_dest(hierarchy), T_CFDEST_INCLUDE,
           "get_include_dest");
    STR_EQ(test, CFCHierarchy_get_source_dest(hierarchy), T_CFDEST_SOURCE,
           "get_source_dest");

    CFCHierarchy_add_source_dir(hierarchy, T_CFBASE);
    const char **source_dirs = CFCHierarchy_get_source_dirs(hierarchy);
    STR_EQ(test, source_dirs[0], T_CFBASE, "source_dirs[0]");
    OK(test, source_dirs[1] == NULL, "source_dirs[1]");

    CFCHierarchy_build(hierarchy);

    CFCFile **files  = CFCHierarchy_files(hierarchy);
    CFCFile  *animal = NULL;
    CFCFile  *dog    = NULL;
    CFCFile  *util   = NULL;
    for (int i = 0; i < 3; ++i) {
        CFCFile *file = files[i];
        OK(test, file != NULL, "files[%d]", i);
        OK(test, !CFCFile_get_modified(file), "start off not modified");

        CFCBase **blocks = CFCFile_blocks(file);
        for (int j = 0; blocks[j]; ++j) {
            CFCBase *block = blocks[j];
            const char *cfc_class_name = CFCBase_get_cfc_class(block);
            if (strcmp(cfc_class_name, "Clownfish::CFC::Model::Class") == 0) {
                CFCClass *klass = (CFCClass*)block;
                const char *class_name = CFCClass_get_name(klass);
                if (strcmp(class_name, "Animal") == 0) {
                    animal = file;
                }
                else if (strcmp(class_name, "Animal::Dog") == 0) {
                    dog = file;
                }
                else if (strcmp(class_name, "Animal::Util") == 0) {
                    util = file;
                }
            }
        }
    }
    OK(test, files[3] == NULL, "recursed and found all three files");

    {
        CFCClass **ordered_classes = CFCHierarchy_ordered_classes(hierarchy);
        OK(test, ordered_classes[0] != NULL, "ordered_classes[0]");
        OK(test, ordered_classes[1] != NULL, "ordered_classes[1]");
        OK(test, ordered_classes[2] != NULL, "ordered_classes[2]");
        OK(test, ordered_classes[3] != NULL, "ordered_classes[3]");
        OK(test, ordered_classes[4] == NULL, "all classes");
        FREEMEM(ordered_classes);
    }

    // Generate fake C files, with times set to two seconds ago.
    time_t now       = time(NULL);
    time_t past_time = now - 2;
    static const char *const h_paths[] = {
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal.h",
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Dog.h",
        T_CFDEST_INCLUDE CHY_DIR_SEP "Animal" CHY_DIR_SEP "Util.h"
    };
    OK(test, CFCUtil_make_path(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal"),
       "make_path");
    for (int i = 0; i < 3; ++i) {
        const char *h_path  = h_paths[i];
        const char *content = "#include <stdio.h>\n";
        CFCUtil_write_file(h_path, content, strlen(content));
        CFCTest_set_file_times(h_path, past_time);
    }

    char *cfh_path = CFCFile_cfh_path(animal, T_CFBASE);
    CFCTest_set_file_times(cfh_path, now);
    FREEMEM(cfh_path);

    CFCHierarchy_propagate_modified(hierarchy, 0);

    OK(test, CFCFile_get_modified(animal), "Animal modified");
    OK(test, CFCFile_get_modified(dog),
       "Parent's modification propagates to child's file");
    OK(test, !CFCFile_get_modified(util),
       "Modification doesn't propagate to inert class");

    for (int i = 0; i < 3; ++i) {
        remove(h_paths[i]);
    }
    rmdir(T_CFDEST_INCLUDE CHY_DIR_SEP "Animal");
    rmdir(T_CFDEST_INCLUDE);
    rmdir(T_CFDEST_SOURCE);
    rmdir(T_CFDEST);

    CFCBase_decref((CFCBase*)hierarchy);
    CFCClass_clear_registry();
    CFCParcel_reap_singletons();
}
static void
S_run_include_tests(CFCTest *test) {
    {
        CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
        CFCHierarchy_add_source_dir(hierarchy, T_CFEXT);
        CFCHierarchy_add_include_dir(hierarchy, T_CFBASE);
        const char **include_dirs = CFCHierarchy_get_include_dirs(hierarchy);
        STR_EQ(test, include_dirs[0], T_CFBASE, "include_dirs[0]");
        OK(test, include_dirs[1] == NULL, "include_dirs[1]");

        CFCHierarchy_build(hierarchy);

        CFCClass **classes    = CFCHierarchy_ordered_classes(hierarchy);
        CFCClass  *rottweiler = NULL;;
        int num_classes;
        int num_source_classes = 0;
        for (num_classes = 0; classes[num_classes]; ++num_classes) {
            CFCClass *klass = classes[num_classes];
            int expect_included = 1;
            const char *class_name = CFCClass_get_name(klass);
            if (strcmp(class_name, "Animal::Rottweiler") == 0) {
                rottweiler      = klass;
                expect_included = 0;
                ++num_source_classes;
            }
            INT_EQ(test, CFCClass_included(klass), expect_included,
                   "included");
        }
        INT_EQ(test, num_classes, 5, "class count");
        INT_EQ(test, num_source_classes, 1, "source class count");
        STR_EQ(test, CFCClass_get_name(CFCClass_get_parent(rottweiler)),
               "Animal::Dog", "parent of included class");

        FREEMEM(classes);
        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_source_dir(hierarchy, T_CFEXT);

        CFCHierarchy_build(hierarchy);

        CFCClass **classes    = CFCHierarchy_ordered_classes(hierarchy);
        CFCClass  *rottweiler = NULL;;
        int num_classes;
        for (num_classes = 0; classes[num_classes]; ++num_classes) {
            CFCClass *klass = classes[num_classes];
            const char *class_name = CFCClass_get_name(klass);
            if (strcmp(class_name, "Animal::Rottweiler") == 0) {
                rottweiler = klass;
            }
            OK(test, !CFCClass_included(klass), "not included");
        }
        INT_EQ(test, num_classes, 5, "class count");
        OK(test, rottweiler != NULL, "found rottweiler");
        STR_EQ(test, CFCClass_get_name(CFCClass_get_parent(rottweiler)),
               "Animal::Dog", "parent of class from second source");

        FREEMEM(classes);
        CFCBase_decref((CFCBase*)hierarchy);
        CFCClass_clear_registry();
        CFCParcel_reap_singletons();
    }

    {
        CFCHierarchy *hierarchy = CFCHierarchy_new(T_CFDEST);
        CFCHierarchy_add_include_dir(hierarchy, T_CFBASE);
        CFCHierarchy_add_include_dir(hierarchy, T_CFEXT);
        CFCHierarchy_add_prereq(hierarchy, "AnimalExtension");

        CFCHierarchy_build(hierarchy);

        CFCParcel *animal = CFCParcel_fetch("Animal");
        OK(test, animal != NULL, "parcel Animal registered");
        OK(test, CFCParcel_required(animal), "parcel Animal required");
        CFCParcel *animal_ext = CFCParcel_fetch("AnimalExtension");
        OK(test, animal_ext != NULL, "parcel AnimalExtension registered");
        OK(test, CFCParcel_required(animal_ext),
           "parcel AnimalExtension required");

        CFCClass **classes = CFCHierarchy_ordered_classes(hierarchy);
        int num_classes = 0;
        while (classes[num_classes]) {
            ++num_classes;
        }
        INT_EQ(test, num_classes, 5, "class count");

        FREEMEM(classes);
        CFCBase_decref((CFCBase*)hierarchy);
        CFCClass_clear_registry();
        CFCParcel_reap_singletons();
    }

    rmdir(T_CFDEST_INCLUDE);
    rmdir(T_CFDEST_SOURCE);
    rmdir(T_CFDEST);
}
Example #14
0
static void
S_run_parcel_tests(CFCTest *test) {
    {
        CFCParcel *parcel = CFCParcel_new("Foo", NULL, NULL, NULL);
        OK(test, parcel != NULL, "new");
        OK(test, !CFCParcel_included(parcel), "not included");
        CFCBase_decref((CFCBase*)parcel);
    }

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

    {
        const char *json =
            "        {\n"
            "            \"name\": \"Crustacean\",\n"
            "            \"nickname\": \"Crust\",\n"
            "            \"version\": \"v0.1.0\"\n"
            "        }\n";
        CFCParcel *parcel = CFCParcel_new_from_json(json, NULL);
        OK(test, parcel != NULL, "new_from_json");
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        const char *path = "t" CHY_DIR_SEP "cfbase" CHY_DIR_SEP "Animal.cfp";
        CFCParcel *parcel = CFCParcel_new_from_file(path, NULL);
        OK(test, parcel != NULL, "new_from_file");
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        CFCParcel *parcel = CFCParcel_new("Crustacean", "Crust", NULL, NULL);
        CFCParcel_register(parcel);
        STR_EQ(test, CFCVersion_get_vstring(CFCParcel_get_version(parcel)),
               "v0", "get_version");

        CFCBase_decref((CFCBase*)parcel);
        CFCParcel_reap_singletons();
    }

    {
        const char *json =
            "        {\n"
            "            \"name\": \"Crustacean\",\n"
            "            \"version\": \"v0.1.0\",\n"
            "            \"prerequisites\": {\n"
            "                \"Clownfish\": null,\n"
            "                \"Arthropod\": \"v30.104.5\"\n"
            "            }\n"
            "        }\n";
        CFCParcel *parcel = CFCParcel_new_from_json(json, NULL);

        CFCPrereq **prereqs = CFCParcel_get_prereqs(parcel);
        OK(test, prereqs != NULL, "prereqs");

        CFCPrereq *cfish = prereqs[0];
        OK(test, cfish != NULL, "prereqs[0]");
        const char *cfish_name = CFCPrereq_get_name(cfish);
        STR_EQ(test, cfish_name, "Clownfish", "prereqs[0] name");
        CFCVersion *v0            = CFCVersion_new("v0");
        CFCVersion *cfish_version = CFCPrereq_get_version(cfish);
        INT_EQ(test, CFCVersion_compare_to(cfish_version, v0), 0,
               "prereqs[0] version");

        CFCPrereq *apod = prereqs[1];
        OK(test, apod != NULL, "prereqs[1]");
        const char *apod_name = CFCPrereq_get_name(apod);
        STR_EQ(test, apod_name, "Arthropod", "prereqs[1] name");
        CFCVersion *v30_104_5    = CFCVersion_new("v30.104.5");
        CFCVersion *apod_version = CFCPrereq_get_version(apod);
        INT_EQ(test, CFCVersion_compare_to(apod_version, v30_104_5), 0,
               "prereqs[1] version");

        OK(test, prereqs[2] == NULL, "prereqs[2]");

        CFCBase_decref((CFCBase*)v30_104_5);
        CFCBase_decref((CFCBase*)v0);
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        CFCFileSpec *foo_file_spec = CFCFileSpec_new(".", "Foo", true);
        CFCParcel *foo = CFCParcel_new("Foo", NULL, NULL, foo_file_spec);
        CFCParcel_register(foo);

        CFCVersion *cfish_version = CFCVersion_new("v0.8.7");
        CFCFileSpec *cfish_file_spec
            = CFCFileSpec_new(".", "Clownfish", true);
        CFCParcel *cfish
            = CFCParcel_new("Clownfish", NULL, cfish_version, cfish_file_spec);
        CFCParcel_register(cfish);

        const char *crust_json =
            "        {\n"
            "            \"name\": \"Crustacean\",\n"
            "            \"version\": \"v0.1.0\",\n"
            "            \"prerequisites\": {\n"
            "                \"Clownfish\": \"v0.8.5\",\n"
            "            }\n"
            "        }\n";
        CFCParcel *crust = CFCParcel_new_from_json(crust_json, NULL);
        CFCParcel_register(crust);

        CFCParcel_check_prereqs(crust);
        INT_EQ(test, CFCParcel_required(foo), false, "parcel not required");
        INT_EQ(test, CFCParcel_required(cfish), true, "prereq required");
        INT_EQ(test, CFCParcel_required(crust), true, "self required");

        CFCParcel **prereq_parcels = CFCParcel_prereq_parcels(crust);
        OK(test, prereq_parcels[0] != NULL, "prereq_parcels[0]");
        const char *name = CFCParcel_get_name(prereq_parcels[0]);
        STR_EQ(test, name, "Clownfish", "prereq_parcels[0] name");
        OK(test, prereq_parcels[1] == NULL, "prereq_parcels[0]");

        OK(test, CFCParcel_has_prereq(crust, cfish), "has_prereq");
        OK(test, CFCParcel_has_prereq(crust, crust), "has_prereq self");
        OK(test, !CFCParcel_has_prereq(crust, foo), "has_prereq false");

        CFCParcel_add_struct_sym(cfish, "Swim");
        CFCParcel_add_struct_sym(crust, "Pinch");
        CFCParcel_add_struct_sym(foo, "Bar");
        CFCParcel *found;
        found = CFCParcel_lookup_struct_sym(crust, "Swim");
        OK(test, found == cfish, "lookup_struct_sym prereq");
        found = CFCParcel_lookup_struct_sym(crust, "Pinch");
        OK(test, found == crust, "lookup_struct_sym self");
        found = CFCParcel_lookup_struct_sym(crust, "Bar");
        OK(test, found == NULL, "lookup_struct_sym other");

        FREEMEM(prereq_parcels);
        CFCBase_decref((CFCBase*)crust);
        CFCBase_decref((CFCBase*)cfish_version);
        CFCBase_decref((CFCBase*)cfish_file_spec);
        CFCBase_decref((CFCBase*)cfish);
        CFCBase_decref((CFCBase*)foo_file_spec);
        CFCBase_decref((CFCBase*)foo);
        CFCParcel_reap_singletons();
    }
}
Example #15
0
static void
S_run_tests(CFCTest *test) {
    CFCParcel *parcel = CFCParcel_default_parcel();

    {
        static const char *exposures[4] = {
            "public", "private", "parcel", "local"
        };
        static int (*accessors[4])(CFCSymbol *sym) = {
            CFCSymbol_public,
            CFCSymbol_private,
            CFCSymbol_parcel,
            CFCSymbol_local
        };
        for (int i = 0; i < 4; ++i) {
            CFCSymbol *symbol
                = CFCSymbol_new(parcel, exposures[i], NULL, NULL, "sym");
            for (int j = 0; j < 4; ++j) {
                int has_exposure = accessors[j](symbol);
                if (i == j) {
                    OK(test, has_exposure, "exposure %s", exposures[i]);
                }
                else {
                    OK(test, !has_exposure, "%s means not %s", exposures[i],
                       exposures[j]);
                }
            }
            CFCBase_decref((CFCBase*)symbol);
        }
    }

    {
        CFCSymbol *foo = CFCSymbol_new(parcel, "parcel", "Foo", NULL, "sym");
        CFCSymbol *foo_jr
            = CFCSymbol_new(parcel, "parcel", "Foo::FooJr", NULL, "sym");

        int equal = CFCSymbol_equals(foo, foo_jr);
        OK(test, !equal, "different class_name spoils equals");
        const char *foo_jr_name = CFCSymbol_get_class_name(foo_jr);
        STR_EQ(test, foo_jr_name, "Foo::FooJr", "get_class_name");
        const char *foo_jr_cnick = CFCSymbol_get_class_cnick(foo_jr);
        STR_EQ(test, foo_jr_cnick, "FooJr",
               "derive class_cnick from class_name");

        CFCBase_decref((CFCBase*)foo);
        CFCBase_decref((CFCBase*)foo_jr);
    }

    {
        CFCSymbol *public_exposure
            = CFCSymbol_new(parcel, "public", NULL, NULL, "sym");
        CFCSymbol *parcel_exposure
            = CFCSymbol_new(parcel, "parcel", NULL, NULL, "sym");
        int equal = CFCSymbol_equals(public_exposure, parcel_exposure);
        OK(test, !equal, "different exposure spoils equals");
        CFCBase_decref((CFCBase*)public_exposure);
        CFCBase_decref((CFCBase*)parcel_exposure);
    }

    {
        CFCParcel *lucifer_parcel
            = CFCParcel_new("Lucifer", NULL, NULL, false);
        CFCParcel_register(lucifer_parcel);
        CFCSymbol *lucifer
            = CFCSymbol_new(lucifer_parcel, "parcel", NULL, NULL, "sym");

        CFCParcel *symbol_parcel = CFCSymbol_get_parcel(lucifer);
        OK(test, symbol_parcel == lucifer_parcel, "derive parcel");
        const char *prefix = CFCSymbol_get_prefix(lucifer);
        STR_EQ(test, prefix, "lucifer_", "get_prefix");
        const char *Prefix = CFCSymbol_get_Prefix(lucifer);
        STR_EQ(test, Prefix, "Lucifer_", "get_Prefix");
        const char *PREFIX = CFCSymbol_get_PREFIX(lucifer);
        STR_EQ(test, PREFIX, "LUCIFER_", "get_PREFIX");

        CFCParcel *luser_parcel = CFCParcel_new("Luser", NULL, NULL, false);
        CFCParcel_register(luser_parcel);
        CFCSymbol *luser
            = CFCSymbol_new(luser_parcel, "parcel", NULL, NULL, "sym");
        int equal = CFCSymbol_equals(lucifer, luser);
        OK(test, !equal, "different exposure spoils equals");

        CFCBase_decref((CFCBase*)lucifer_parcel);
        CFCBase_decref((CFCBase*)lucifer);
        CFCBase_decref((CFCBase*)luser_parcel);
        CFCBase_decref((CFCBase*)luser);
    }

    {
        CFCSymbol *ooga = CFCSymbol_new(parcel, "parcel", NULL, NULL, "ooga");
        CFCSymbol *booga
            = CFCSymbol_new(parcel, "parcel", NULL, NULL, "booga");
        int equal = CFCSymbol_equals(ooga, booga);
        OK(test, !equal, "different micro_sym spoils equals");
        CFCBase_decref((CFCBase*)ooga);
        CFCBase_decref((CFCBase*)booga);
    }

    {
        CFCParcel *eep_parcel = CFCParcel_new("Eep", NULL, NULL, false);
        CFCParcel_register(eep_parcel);
        CFCSymbol *eep
            = CFCSymbol_new(eep_parcel, "parcel", "Op::Ork", NULL, "ah_ah");
        const char *short_sym = CFCSymbol_short_sym(eep);
        STR_EQ(test, short_sym, "Ork_ah_ah", "short_sym");
        const char *full_sym = CFCSymbol_full_sym(eep);
        STR_EQ(test, full_sym, "eep_Ork_ah_ah", "full_sym");
        CFCBase_decref((CFCBase*)eep_parcel);
        CFCBase_decref((CFCBase*)eep);
    }

    CFCParcel_reap_singletons();
}
Example #16
0
static void
S_run_tests(CFCTest *test) {
    CFCParser *parser = CFCParser_new();
    OK(test, parser != NULL, "new");

    {
        CFCParcel *fish = CFCTest_parse_parcel(test, parser, "parcel Fish;");

        CFCParcel *registered
            = CFCParcel_new("Crustacean", "Crust", NULL, false);
        CFCParcel_register(registered);
        CFCParcel *parcel
            = CFCTest_parse_parcel(test, parser, "parcel Crustacean;");
        OK(test, parcel == registered, "Fetch registered parcel");
        OK(test, CFCParser_get_parcel(parser) == parcel,
           "parcel_definition sets internal var");

        CFCBase_decref((CFCBase*)fish);
        CFCBase_decref((CFCBase*)registered);
        CFCBase_decref((CFCBase*)parcel);
    }

    {
        static const char *const specifiers[8] = {
            "foo", "_foo", "foo_yoo", "FOO", "Foo", "fOO", "f00", "foo_foo_foo"
        };
        for (int i = 0; i < 8; ++i) {
            const char *specifier = specifiers[i];
            char *src = CFCUtil_sprintf("int32_t %s;", specifier);
            CFCVariable *var = CFCTest_parse_variable(test, parser, src);
            STR_EQ(test, CFCVariable_micro_sym(var), specifier,
                   "identifier/declarator: %s", specifier);
            FREEMEM(src);
            CFCBase_decref((CFCBase*)var);
        }
    }

    {
        static const char *const specifiers[6] = {
            "void", "float", "uint32_t", "int64_t", "uint8_t", "bool"
        };
        for (int i = 0; i < 6; ++i) {
            const char *specifier = specifiers[i];
            char *src = CFCUtil_sprintf("int32_t %s;", specifier);
            CFCBase *result = CFCParser_parse(parser, src);
            OK(test, result == NULL,
               "reserved word not parsed as identifier: %s", specifier);
            FREEMEM(src);
            CFCBase_decref(result);
        }
    }

    {
        static const char *const type_strings[7] = {
            "bool", "const char *", "Obj*", "i32_t", "char[]", "long[1]",
            "i64_t[30]"
        };
        for (int i = 0; i < 7; ++i) {
            const char *type_string = type_strings[i];
            CFCType *type = CFCTest_parse_type(test, parser, type_string);
            CFCBase_decref((CFCBase*)type);
        }
    }

    {
        static const char *const class_names[7] = {
            "ByteBuf", "Obj", "ANDMatcher", "Foo", "FooJr", "FooIII", "Foo4th"
        };
        CFCClass *class_list[8];
        for (int i = 0; i < 7; ++i) {
            char *class_code = CFCUtil_sprintf("class %s {}", class_names[i]);
            CFCClass *klass = CFCTest_parse_class(test, parser, class_code);
            class_list[i] = klass;
            FREEMEM(class_code);
        }
        class_list[7] = NULL;
        for (int i = 0; i < 7; ++i) {
            const char *class_name = class_names[i];
            char *src      = CFCUtil_sprintf("%s*", class_name);
            char *expected = CFCUtil_sprintf("crust_%s", class_name);
            CFCType *type = CFCTest_parse_type(test, parser, src);
            CFCType_resolve(type, class_list);
            STR_EQ(test, CFCType_get_specifier(type), expected,
                   "object_type_specifier: %s", class_name);
            FREEMEM(src);
            FREEMEM(expected);
            CFCBase_decref((CFCBase*)type);
        }
        for (int i = 0; i < 7; ++i) {
            CFCBase_decref((CFCBase*)class_list[i]);
        }
        CFCClass_clear_registry();
    }

    {
        CFCType *type = CFCTest_parse_type(test, parser, "const char");
        OK(test, CFCType_const(type), "type_qualifier const");
        CFCBase_decref((CFCBase*)type);
    }

    {
        static const char *const exposures[2] = {
            "public", ""
        };
        static int (*const accessors[2])(CFCSymbol *sym) = {
            CFCSymbol_public, CFCSymbol_parcel
        };
        for (int i = 0; i < 2; ++i) {
            const char *exposure = exposures[i];
            char *src = CFCUtil_sprintf("%s inert int32_t foo;", exposure);
            CFCVariable *var = CFCTest_parse_variable(test, parser, src);
            OK(test, accessors[i]((CFCSymbol*)var), "exposure_specifier %s",
               exposure);
            FREEMEM(src);
            CFCBase_decref((CFCBase*)var);
        }
    }

    {
        static const char *const hex_constants[] = {
            "0x1", "0x0a", "0xFFFFFFFF", "-0xFC", NULL
        };
        S_test_initial_value(test, parser, hex_constants, "int32_t",
                             "hex_constant:");
    }

    {
        static const char *const integer_constants[] = {
            "1", "-9999", "0", "10000", NULL
        };
        S_test_initial_value(test, parser, integer_constants, "int32_t",
                             "integer_constant:");
    }

    {
        static const char *const float_constants[] = {
            "1.0", "-9999.999", "0.1", "0.0", NULL
        };
        S_test_initial_value(test, parser, float_constants, "double",
                             "float_constant:");
    }

    {
        static const char *const string_literals[] = {
            "\"blah\"", "\"blah blah\"", "\"\\\"blah\\\" \\\"blah\\\"\"", NULL
        };
        S_test_initial_value(test, parser, string_literals, "String*",
                             "string_literal:");
    }

    {
        static const char *const composites[5] = {
            "int[]", "i32_t **", "Foo **", "Foo ***", "const void *"
        };
        for (int i = 0; i < 5; ++i) {
            const char *composite = composites[i];
            CFCType *type = CFCTest_parse_type(test, parser, composite);
            OK(test, CFCType_is_composite(type), "composite_type: %s",
               composite);
            CFCBase_decref((CFCBase*)type);
        }
    }

    {
        static const char *const object_types[5] = {
            "Obj *", "incremented Foo*", "decremented String *"
        };
        for (int i = 0; i < 3; ++i) {
            const char *object_type = object_types[i];
            CFCType *type = CFCTest_parse_type(test, parser, object_type);
            OK(test, CFCType_is_object(type), "object_type: %s",
               object_type);
            CFCBase_decref((CFCBase*)type);
        }
    }

    {
        static const char *const param_list_strings[3] = {
            "()",
            "(int foo)",
            "(Obj *foo, Foo **foo_ptr)"
        };
        for (int i = 0; i < 3; ++i) {
            const char *param_list_string = param_list_strings[i];
            CFCParamList *param_list
                = CFCTest_parse_param_list(test, parser, param_list_string);
            INT_EQ(test, CFCParamList_num_vars(param_list), i,
                   "param list num_vars: %d", i);
            CFCBase_decref((CFCBase*)param_list);
        }
    }

    {
        CFCParamList *param_list
            = CFCTest_parse_param_list(test, parser, "(int foo, ...)");
        OK(test, CFCParamList_variadic(param_list), "variadic param list");
        CFCBase_decref((CFCBase*)param_list);
    }

    {
        const char *param_list_string =
            "(int foo = 0xFF, char *bar =\"blah\")";
        CFCParamList *param_list
            = CFCTest_parse_param_list(test, parser, param_list_string);
        const char **initial_values
            = CFCParamList_get_initial_values(param_list);
        STR_EQ(test, initial_values[0], "0xFF",
               "param list initial_values[0]");
        STR_EQ(test, initial_values[1], "\"blah\"",
               "param list initial_values[1]");
        OK(test, initial_values[2] == NULL, "param list initial_values[2]");
        CFCBase_decref((CFCBase*)param_list);
    }

    {
        CFCParser_set_class_name(parser, "Stuff::Obj");
        CFCParser_set_class_cnick(parser, "Obj");

        const char *method_string =
            "public Foo* Spew_Foo(Obj *self, uint32_t *how_many);";
        CFCMethod *method = CFCTest_parse_method(test, parser, method_string);
        CFCBase_decref((CFCBase*)method);

        const char *var_string =
            "public inert Hash *hash;";
        CFCVariable *var = CFCTest_parse_variable(test, parser, var_string);
        CFCBase_decref((CFCBase*)var);
    }

    {
        static const char *const class_names[4] = {
            "Foo", "Foo::FooJr", "Foo::FooJr::FooIII",
            "Foo::FooJr::FooIII::Foo4th"
        };
        for (int i = 0; i < 4; ++i) {
            const char *class_name = class_names[i];
            char *class_string = CFCUtil_sprintf("class %s { }", class_name);
            CFCClass *klass
                = CFCTest_parse_class(test, parser, class_string);
            STR_EQ(test, CFCClass_get_class_name(klass), class_name,
                   "class_name: %s", class_name);
            FREEMEM(class_string);
            CFCBase_decref((CFCBase*)klass);
        }
    }

    {
        static const char *const cnicks[2] =  { "Food", "FF" };
        for (int i = 0; i < 2; ++i) {
            const char *cnick = cnicks[i];
            char *class_string
                = CFCUtil_sprintf("class Foodie%s cnick %s { }", cnick, cnick);
            CFCClass *klass
                = CFCTest_parse_class(test, parser, class_string);
            STR_EQ(test, CFCClass_get_cnick(klass), cnick, "cnick: %s", cnick);
            FREEMEM(class_string);
            CFCBase_decref((CFCBase*)klass);
        }
    }

    CFCBase_decref((CFCBase*)parser);

    CFCClass_clear_registry();
    CFCParcel_reap_singletons();
}