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