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(); }
CFCParcel* CFCParcel_clownfish_parcel(void) { CFCParcel *parcel = CFCParcel_fetch("Lucy"); if (!parcel) { CFCVersion *version = CFCVersion_new("v0.3.0"); parcel = CFCParcel_new("Lucy", "Lucy", version); CFCParcel_register(parcel); CFCBase_decref((CFCBase*)version); CFCBase_decref((CFCBase*)parcel); } return parcel; }
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(); }
static void S_parse_source_cfp_files(CFCHierarchy *self, const char *source_dir) { CFCFindFilesContext context; context.ext = ".cfp"; context.paths = (char**)CALLOCATE(1, sizeof(char*)); context.num_paths = 0; CFCUtil_walk(source_dir, S_find_files, &context); // Parse .cfp files and register the parcels they define. for (int i = 0; context.paths[i] != NULL; i++) { const char *path = context.paths[i]; char *path_part = S_extract_path_part(path, source_dir, ".cfp"); CFCFileSpec *file_spec = CFCFileSpec_new(source_dir, path_part, ".cfp", false); CFCParcel *parcel = CFCParcel_new_from_file(file_spec); const char *name = CFCParcel_get_name(parcel); CFCParcel *existing = CFCParcel_fetch(name); if (existing) { CFCUtil_die("Parcel '%s' defined twice in %s and %s", CFCParcel_get_name(parcel), CFCParcel_get_cfp_path(existing), path); } // Make sure to register prereq parcels first, so that prereq // parent classes precede subclasses. S_find_prereqs(self, parcel); CFCParcel_register(parcel); CFCBase_decref((CFCBase*)parcel); CFCBase_decref((CFCBase*)file_spec); FREEMEM(path_part); } CFCUtil_free_string_array(context.paths); }
static void S_find_prereq(CFCHierarchy *self, CFCParcel *parent, CFCPrereq *prereq) { const char *name = CFCPrereq_get_name(prereq); CFCVersion *min_version = CFCPrereq_get_version(prereq); // Check whether prereq was processed already. CFCParcel **parcels = CFCParcel_all_parcels(); for (int i = 0; parcels[i]; ++i) { CFCParcel *parcel = parcels[i]; const char *other_name = CFCParcel_get_name(parcel); if (strcmp(other_name, name) == 0) { CFCVersion *other_version = CFCParcel_get_version(parcel); CFCVersion *major_version = CFCParcel_get_major_version(parcel); if (CFCVersion_compare_to(major_version, min_version) <= 0 && CFCVersion_compare_to(min_version, other_version) <= 0 ) { // Compatible version found. return; } else { CFCUtil_die("Parcel %s %s required by %s not compatible with" " version %s required by %s", name, other_version, "[TODO]", CFCVersion_get_vstring(min_version), CFCParcel_get_name(parent)); } } } CFCParcel *parcel = NULL; // TODO: Decide whether to prefer higher versions from directories // that come later in the list of include dirs or stop processing once // a suitable version was found in a dir. for (size_t i = 0; self->includes[i] != NULL; i++) { char *name_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", self->includes[i], name); if (CFCUtil_is_dir(name_dir)) { void *dirhandle = CFCUtil_opendir(name_dir); const char *entry = NULL; while (NULL != (entry = CFCUtil_dirnext(dirhandle))) { if (!CFCVersion_is_vstring(entry)) { continue; } char *version_dir = CFCUtil_sprintf("%s" CHY_DIR_SEP "%s", name_dir, entry); if (CFCUtil_is_dir(version_dir)) { parcel = S_audition_parcel(version_dir, entry, min_version, parcel); } FREEMEM(version_dir); } CFCUtil_closedir(dirhandle, name_dir); } FREEMEM(name_dir); } if (parcel == NULL) { CFCUtil_die("Parcel %s %s required by %s not found", name, CFCVersion_get_vstring(min_version), CFCParcel_get_name(parent)); } // Make sure to register prereq parcels first, so that prereq // parent classes precede subclasses. S_find_prereqs(self, parcel); CFCParcel_register(parcel); CFCBase_decref((CFCBase*)parcel); }
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(); } }
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(); }
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(); }