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();
}
Esempio n. 2
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();
}
Esempio n. 3
0
void
CFCBindFile_write_h(CFCFile *file, const char *dest, const char *header,
                    const char *footer) {
    CFCUTIL_NULL_CHECK(file);
    CFCUTIL_NULL_CHECK(dest);
    CFCUTIL_NULL_CHECK(header);
    CFCUTIL_NULL_CHECK(footer);

    // Make directories.
    char *h_path = CFCFile_h_path(file, dest);
    char *h_dir  = CFCUtil_strdup(h_path);
    for (size_t len = strlen(h_dir); len--;) {
        if (h_dir[len] == CHY_DIR_SEP_CHAR) {
            h_dir[len] = 0;
            break;
        }
    }
    if (!CFCUtil_is_dir(h_dir)) {
        CFCUtil_make_path(h_dir);
        if (!CFCUtil_is_dir(h_dir)) {
            CFCUtil_die("Can't make path %s", h_dir);
        }
    }
    FREEMEM(h_dir);

    // Create the include-guard strings.
    const char *include_guard_start = CFCFile_guard_start(file);
    const char *include_guard_close = CFCFile_guard_close(file);

    // Aggregate block content.
    char *content = CFCUtil_strdup("");
    CFCBase **blocks = CFCFile_blocks(file);
    for (int i = 0; blocks[i] != NULL; i++) {
        const char *cfc_class = CFCBase_get_cfc_class(blocks[i]);

        if (strcmp(cfc_class, "Clownfish::CFC::Model::Parcel") == 0) {
            CFCParcel *parcel = (CFCParcel*)blocks[i];
            const char *prefix = CFCParcel_get_prefix(parcel);
            content = CFCUtil_cat(content, "#include \"", prefix,
                                  "parcel.h\"\n\n", NULL);
        }
        else if (strcmp(cfc_class, "Clownfish::CFC::Model::Class") == 0) {
            CFCBindClass *class_binding
                = CFCBindClass_new((CFCClass*)blocks[i]);
            char *c_header = CFCBindClass_to_c_header(class_binding);
            content = CFCUtil_cat(content, c_header, "\n", NULL);
            FREEMEM(c_header);
            CFCBase_decref((CFCBase*)class_binding);
        }
        else if (strcmp(cfc_class, "Clownfish::CFC::Model::CBlock") == 0) {
            const char *block_contents 
                = CFCCBlock_get_contents((CFCCBlock*)blocks[i]);
            content = CFCUtil_cat(content, block_contents, "\n", NULL);
        }
        else {
            CFCUtil_die("Unexpected class: %s", cfc_class);
        }
    }

    char pattern[] =
        "%s\n"
        "\n"
        "%s\n"
        "\n"
        "#ifdef __cplusplus\n"
        "extern \"C\" {\n"
        "#endif\n"
        "\n"
        "%s\n"
        "\n"
        "#ifdef __cplusplus\n"
        "}\n"
        "#endif\n"
        "\n"
        "%s\n"
        "\n"
        "%s\n"
        "\n";
    char *file_content
        = CFCUtil_sprintf(pattern, header, include_guard_start, content,
                          include_guard_close, footer);

    // Unlink then write file.
    remove(h_path);
    CFCUtil_write_file(h_path, file_content, strlen(file_content));

    FREEMEM(content);
    FREEMEM(file_content);
    FREEMEM(h_path);
}