diff options
Diffstat (limited to 'src/test-driver.c')
-rw-r--r-- | src/test-driver.c | 463 |
1 files changed, 463 insertions, 0 deletions
diff --git a/src/test-driver.c b/src/test-driver.c new file mode 100644 index 0000000..91bc62c --- /dev/null +++ b/src/test-driver.c @@ -0,0 +1,463 @@ +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <expat.h> + +#include "razor.h" + +#define XML_BUFFER_SIZE 4096 + +static void +parse_xml_file(const char *filename, + XML_StartElementHandler start, + XML_EndElementHandler end, + void *data) +{ + XML_Parser parser; + char *buffer; + int fd, len, err; + + parser = XML_ParserCreate(NULL); + XML_SetElementHandler(parser, start, end); + XML_SetUserData(parser, data); + + fd = open(filename, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "failed to open %s: %m\n", filename); + exit(-1); + } + + while (1) { + buffer = XML_GetBuffer(parser, XML_BUFFER_SIZE); + len = read(fd, buffer, XML_BUFFER_SIZE); + if (len == 0) + break; + err = XML_ParseBuffer(parser, len, len == 0); + if (err == XML_STATUS_ERROR) { + fprintf(stderr, "parse error at line %lu:\n%s\n", + XML_GetCurrentLineNumber(parser), + XML_ErrorString(XML_GetErrorCode(parser))); + exit(-1); + } + } + + if (fd < 0) { + fprintf(stderr, "read: %m\n"); + exit(-1); + } + + close(fd); +} + +struct test_context { + struct razor_set *system_set, *repo_set, *result_set; + + struct razor_importer *importer; + struct razor_set **importer_set; + + struct razor_transaction *trans; + + char *install_pkgs[3], *remove_pkgs[3]; + int n_install_pkgs, n_remove_pkgs; + + int unsat; + int in_result; + + int debug, errors; +}; + +static void +get_atts(const char **atts, ...) +{ + va_list ap; + const char *name, **ptr; + int i; + + va_start(ap, atts); + while (name = va_arg(ap, const char *), name != NULL) { + ptr = va_arg(ap, const char **); + *ptr = NULL; + for (i = 0; atts[i]; i += 2) { + if (strcmp(atts[i], name) == 0) + *ptr = atts[i + 1]; + } + } + va_end(ap); +} + +static enum razor_version_relation +parse_relation (const char *rel_str) +{ + if (!rel_str) + return -1; + if (rel_str[0] == 'L') + return rel_str[1] == 'E' ? RAZOR_VERSION_LESS_OR_EQUAL : RAZOR_VERSION_LESS; + else if (rel_str[0] == 'G') + return rel_str[1] == 'E' ? RAZOR_VERSION_GREATER_OR_EQUAL : RAZOR_VERSION_GREATER; + else if (rel_str[0] == 'E' || rel_str[1] == 'Q') + return RAZOR_VERSION_EQUAL; + else + return -1; +} + +static void +start_test(struct test_context *ctx, const char **atts) +{ + const char *name = NULL; + + get_atts(atts, "name", &name, NULL); + if (!name) { + fprintf(stderr, "Test with no name\n"); + exit(1); + } + printf("%s\n", name); +} + +static void +end_test(struct test_context *ctx) +{ + if (ctx->system_set) { + razor_set_destroy(ctx->system_set); + ctx->system_set = NULL; + } + if (ctx->repo_set) { + razor_set_destroy(ctx->repo_set); + ctx->repo_set = NULL; + } + if (ctx->result_set) { + razor_set_destroy(ctx->result_set); + ctx->result_set = NULL; + } + if (ctx->trans) { + razor_transaction_destroy(ctx->trans); + ctx->trans = NULL; + } +} + +static void +start_set(struct test_context *ctx, const char **atts) +{ + const char *name = NULL; + + ctx->importer = razor_importer_new(); + get_atts(atts, "name", &name, NULL); + if (!name) + ctx->importer_set = &ctx->result_set; + else if (!strcmp(name, "system")) + ctx->importer_set = &ctx->system_set; + else if (!strcmp(name, "repo")) + ctx->importer_set = &ctx->repo_set; + else { + fprintf(stderr, " bad set name '%s'\n", name); + exit(1); + } +} + +static void +end_set(struct test_context *ctx) +{ + *ctx->importer_set = razor_importer_finish(ctx->importer); + ctx->importer = NULL; +} + +static void +start_package(struct test_context *ctx, const char **atts) +{ + const char *name = NULL, *version = NULL, *arch = NULL; + + get_atts(atts, "name", &name, + "version", &version, + "arch", &arch, + NULL); + + if (!name) { + fprintf(stderr, " package with no name\n"); + exit(1); + } + + razor_importer_begin_package(ctx->importer, name, version, arch); + razor_importer_add_property(ctx->importer, name, + RAZOR_VERSION_EQUAL, version, + RAZOR_PROPERTY_PROVIDES); +} + +static void +end_package(struct test_context *ctx) +{ + razor_importer_finish_package(ctx->importer); +} + +static void +add_property(struct test_context *ctx, enum razor_property_type type, const char *name, enum razor_version_relation rel, const char *version) +{ + razor_importer_add_property(ctx->importer, name, + rel, version, type); +} + +static void +check_unsatisfiable_property(struct test_context *ctx, + enum razor_property_type type, + const char *name, + enum razor_version_relation rel, + const char *version) +{ + static const char *relation_string[] = { "<", "<=", "=", ">=", ">" }; + + if (!version) + version = ""; + + if (razor_transaction_unsatisfied_property(ctx->trans, + name, rel, version, type)) + return; + + fprintf(stderr, " didn't get unsatisfiable '%s %s %s'\n", + name, relation_string[rel], version); + ctx->errors++; +} + +static void +start_property(struct test_context *ctx, enum razor_property_type type, const char **atts) +{ + const char *name = NULL, *rel_str = NULL, *version = NULL; + enum razor_version_relation rel; + + get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL); + if (name == NULL) { + fprintf(stderr, " no name specified for property\n"); + exit(1); + } + if (version) { + rel = parse_relation(rel_str); + if (rel == -1) { + fprintf(stderr, " bad or missing version relation for property %s\n", name); + exit(1); + } + } else + rel = RAZOR_VERSION_EQUAL; + + if (ctx->unsat) + check_unsatisfiable_property(ctx, type, name, rel, version); + else + add_property(ctx, type, name, rel, version); +} + +static void +start_transaction(struct test_context *ctx, const char **atts) +{ + ctx->n_install_pkgs = 0; + ctx->n_remove_pkgs = 0; +} + +static void +end_transaction(struct test_context *ctx) +{ + struct razor_package *pkg; + int errors, i; + + ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set); + for (i = 0; i < ctx->n_install_pkgs; i++) { + pkg = razor_set_get_package(ctx->repo_set, + ctx->install_pkgs[i]); + razor_transaction_install_package(ctx->trans, pkg); + } + for (i = 0; i < ctx->n_remove_pkgs; i++) { + pkg = razor_set_get_package(ctx->system_set, + ctx->remove_pkgs[i]); + if (!pkg) + pkg = razor_set_get_package(ctx->repo_set, + ctx->remove_pkgs[i]); + + razor_transaction_remove_package(ctx->trans, pkg); + } + + errors = razor_transaction_resolve(ctx->trans); + printf("\n"); + + while (ctx->n_install_pkgs--) + free(ctx->install_pkgs[ctx->n_install_pkgs]); + while (ctx->n_remove_pkgs--) + free(ctx->remove_pkgs[ctx->n_remove_pkgs]); + + if (!errors) { + struct razor_set *new; + new = razor_transaction_finish(ctx->trans); + ctx->trans = NULL; + ctx->system_set = new; + } +} + +static void +start_install_or_update(struct test_context *ctx, const char **atts) +{ + const char *name = NULL; + + get_atts(atts, "name", &name, NULL); + if (!name) { + fprintf(stderr, " install/update with no name\n"); + exit(1); + } + + ctx->install_pkgs[ctx->n_install_pkgs++] = strdup(name); +} + +static void +start_remove(struct test_context *ctx, const char **atts) +{ + const char *name = NULL; + + get_atts(atts, "name", &name, NULL); + if (!name) { + fprintf(stderr, " remove with no name\n"); + exit(1); + } + + ctx->remove_pkgs[ctx->n_remove_pkgs++] = strdup(name); +} + +static void +start_result(struct test_context *ctx, const char **atts) +{ + ctx->in_result = 1; +} + +static void +diff_callback(const char *name, + const char *old_version, + const char *new_version, + const char *arch, + void *data) +{ + struct test_context *ctx = data; + + ctx->errors++; + if (old_version) { + fprintf(stderr, " result set should not contain %s %s\n", + name, old_version); + } else { + fprintf(stderr, " result set should contain %s %s\n", + name, new_version); + } +} + +static void +end_result(struct test_context *ctx) +{ + ctx->in_result = 0; + + if (ctx->result_set) { + if (!ctx->system_set) + ctx->system_set = razor_set_create(); + razor_set_diff(ctx->system_set, ctx->result_set, + diff_callback, ctx); + } +} + +static void +start_unsatisfiable(struct test_context *ctx, const char **atts) +{ + if (ctx->result_set) { + fprintf(stderr, "Expected to fail, but didn't\n"); + exit(1); + } + + ctx->unsat = 1; +} + +static void +end_unsatisfiable(struct test_context *ctx) +{ + ctx->unsat = 0; +} + +static void +start_test_element(void *data, const char *element, const char **atts) +{ + struct test_context *ctx = data; + + if (strcmp(element, "tests") == 0) { + ; + } else if (strcmp(element, "test") == 0) { + start_test(ctx, atts); + } else if (strcmp(element, "set") == 0) { + start_set(ctx, atts); + } else if (strcmp(element, "transaction") == 0) { + start_transaction(ctx, atts); + } else if (strcmp(element, "install") == 0) { + start_install_or_update(ctx, atts); + } else if (strcmp(element, "install") == 0) { + start_install_or_update(ctx, atts); + } else if (strcmp(element, "remove") == 0) { + start_remove(ctx, atts); + } else if (strcmp(element, "result") == 0) { + start_result(ctx, atts); + } else if (strcmp(element, "unsatisfiable") == 0) { + start_unsatisfiable(ctx, atts); + } else if (strcmp(element, "package") == 0) { + start_package(ctx, atts); + } else if (strcmp(element, "requires") == 0) { + start_property(ctx, RAZOR_PROPERTY_REQUIRES, atts); + } else if (strcmp(element, "provides") == 0) { + start_property(ctx, RAZOR_PROPERTY_PROVIDES, atts); + } else if (strcmp(element, "conflicts") == 0) { + start_property(ctx, RAZOR_PROPERTY_CONFLICTS, atts); + } else if (strcmp(element, "obsoletes") == 0) { + start_property(ctx, RAZOR_PROPERTY_OBSOLETES, atts); + } else { + fprintf(stderr, "Unrecognized element '%s'\n", element); + exit(1); + } +} + +static void +end_test_element (void *data, const char *element) +{ + struct test_context *ctx = data; + + if (strcmp(element, "test") == 0) { + end_test(ctx); + } else if (strcmp(element, "set") == 0) { + end_set(ctx); + } else if (strcmp(element, "package") == 0) { + end_package(ctx); + } else if (strcmp(element, "transaction") == 0) { + end_transaction(ctx); + } else if (strcmp(element, "result") == 0) { + end_result(ctx); + } else if (strcmp(element, "unsatisfiable") == 0) { + end_unsatisfiable(ctx); + } +} + +int main(int argc, char *argv[]) +{ + struct test_context ctx; + const char *test_file; + + memset(&ctx, 0, sizeof ctx); + + if (argc > 3) { + fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]); + exit(-1); + } + + if (argc >= 2 && !strcmp (argv[1], "-d")) { + ctx.debug = 1; + argc--; + argv++; + } + if (argc == 2) + test_file = argv[1]; + else + test_file = "test.xml"; + + parse_xml_file(test_file, start_test_element, end_test_element, &ctx); + + if (ctx.errors) { + fprintf(stderr, "\n%d errors\n", ctx.errors); + return 1; + } else + return 0; +} |