richard@300: /* richard@300: * Copyright (C) 2008 Kristian Høgsberg richard@300: * Copyright (C) 2008 Red Hat, Inc richard@300: * richard@300: * This program is free software; you can redistribute it and/or modify richard@300: * it under the terms of the GNU General Public License as published by richard@300: * the Free Software Foundation; either version 2 of the License, or richard@300: * (at your option) any later version. richard@300: * richard@300: * This program is distributed in the hope that it will be useful, richard@300: * but WITHOUT ANY WARRANTY; without even the implied warranty of richard@300: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the richard@300: * GNU General Public License for more details. richard@300: * richard@300: * You should have received a copy of the GNU General Public License along richard@300: * with this program; if not, write to the Free Software Foundation, Inc., richard@300: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. richard@300: */ richard@300: rhughes@241: #include rhughes@241: #include rhughes@241: #include rhughes@241: #include rhughes@241: #include rhughes@241: #include rhughes@241: #include rhughes@241: rhughes@241: #include "razor.h" rhughes@241: rhughes@241: #define XML_BUFFER_SIZE 4096 rhughes@241: rhughes@241: static void rhughes@241: parse_xml_file(const char *filename, rhughes@241: XML_StartElementHandler start, rhughes@241: XML_EndElementHandler end, rhughes@241: void *data) rhughes@241: { rhughes@241: XML_Parser parser; rhughes@241: char *buffer; rhughes@241: int fd, len, err; rhughes@241: rhughes@241: parser = XML_ParserCreate(NULL); rhughes@241: XML_SetElementHandler(parser, start, end); rhughes@241: XML_SetUserData(parser, data); rhughes@241: rhughes@241: fd = open(filename, O_RDONLY); rhughes@241: if (fd < 0) { ali@339: fprintf(stderr, "failed to open %s: %s\n", filename, ali@339: strerror(errno)); rhughes@241: exit(-1); rhughes@241: } rhughes@241: rhughes@241: while (1) { rhughes@241: buffer = XML_GetBuffer(parser, XML_BUFFER_SIZE); rhughes@241: len = read(fd, buffer, XML_BUFFER_SIZE); rhughes@241: if (len == 0) rhughes@241: break; rhughes@241: err = XML_ParseBuffer(parser, len, len == 0); rhughes@241: if (err == XML_STATUS_ERROR) { rhughes@241: fprintf(stderr, "parse error at line %lu:\n%s\n", rhughes@241: XML_GetCurrentLineNumber(parser), rhughes@241: XML_ErrorString(XML_GetErrorCode(parser))); rhughes@241: exit(-1); rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: if (fd < 0) { ali@339: perror("read"); rhughes@241: exit(-1); rhughes@241: } rhughes@241: rhughes@241: close(fd); rhughes@241: } rhughes@241: rhughes@241: struct test_context { rhughes@241: struct razor_set *system_set, *repo_set, *result_set; rhughes@241: rhughes@241: struct razor_importer *importer; rhughes@241: struct razor_set **importer_set; rhughes@241: rhughes@241: struct razor_transaction *trans; rhughes@241: rhughes@241: char *install_pkgs[3], *remove_pkgs[3]; rhughes@241: int n_install_pkgs, n_remove_pkgs; rhughes@241: rhughes@241: int unsat; rhughes@241: int in_result; rhughes@241: rhughes@241: int debug, errors; rhughes@241: }; rhughes@241: rhughes@241: static void rhughes@241: get_atts(const char **atts, ...) rhughes@241: { rhughes@241: va_list ap; rhughes@241: const char *name, **ptr; rhughes@241: int i; rhughes@241: rhughes@241: va_start(ap, atts); rhughes@241: while (name = va_arg(ap, const char *), name != NULL) { rhughes@241: ptr = va_arg(ap, const char **); rhughes@241: *ptr = NULL; rhughes@241: for (i = 0; atts[i]; i += 2) { rhughes@241: if (strcmp(atts[i], name) == 0) rhughes@241: *ptr = atts[i + 1]; rhughes@241: } rhughes@241: } rhughes@241: va_end(ap); rhughes@241: } rhughes@241: jbowes@284: static enum razor_property_flags rhughes@241: parse_relation (const char *rel_str) rhughes@241: { rhughes@241: if (!rel_str) rhughes@241: return -1; rhughes@241: if (rel_str[0] == 'L') jbowes@284: return rel_str[1] == 'E' ? RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL : RAZOR_PROPERTY_LESS; rhughes@241: else if (rel_str[0] == 'G') jbowes@284: return rel_str[1] == 'E' ? RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL : RAZOR_PROPERTY_GREATER; rhughes@241: else if (rel_str[0] == 'E' || rel_str[1] == 'Q') jbowes@284: return RAZOR_PROPERTY_EQUAL; rhughes@241: else rhughes@241: return -1; rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_test(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: const char *name = NULL; rhughes@241: rhughes@241: get_atts(atts, "name", &name, NULL); rhughes@241: if (!name) { rhughes@241: fprintf(stderr, "Test with no name\n"); rhughes@241: exit(1); rhughes@241: } rhughes@241: printf("%s\n", name); rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: end_test(struct test_context *ctx) rhughes@241: { rhughes@241: if (ctx->system_set) { rhughes@241: razor_set_destroy(ctx->system_set); rhughes@241: ctx->system_set = NULL; rhughes@241: } rhughes@241: if (ctx->repo_set) { rhughes@241: razor_set_destroy(ctx->repo_set); rhughes@241: ctx->repo_set = NULL; rhughes@241: } rhughes@241: if (ctx->result_set) { rhughes@241: razor_set_destroy(ctx->result_set); rhughes@241: ctx->result_set = NULL; rhughes@241: } rhughes@241: if (ctx->trans) { rhughes@241: razor_transaction_destroy(ctx->trans); rhughes@241: ctx->trans = NULL; rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_set(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: const char *name = NULL; rhughes@241: jbowes@284: ctx->importer = razor_importer_create(); rhughes@241: get_atts(atts, "name", &name, NULL); rhughes@241: if (!name) rhughes@241: ctx->importer_set = &ctx->result_set; rhughes@241: else if (!strcmp(name, "system")) rhughes@241: ctx->importer_set = &ctx->system_set; rhughes@241: else if (!strcmp(name, "repo")) rhughes@241: ctx->importer_set = &ctx->repo_set; rhughes@241: else { rhughes@241: fprintf(stderr, " bad set name '%s'\n", name); rhughes@241: exit(1); rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: end_set(struct test_context *ctx) rhughes@241: { rhughes@241: *ctx->importer_set = razor_importer_finish(ctx->importer); rhughes@241: ctx->importer = NULL; rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_package(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: const char *name = NULL, *version = NULL, *arch = NULL; rhughes@241: rhughes@241: get_atts(atts, "name", &name, rhughes@241: "version", &version, rhughes@241: "arch", &arch, rhughes@241: NULL); rhughes@241: rhughes@241: if (!name) { rhughes@241: fprintf(stderr, " package with no name\n"); rhughes@241: exit(1); rhughes@241: } rhughes@241: rhughes@241: razor_importer_begin_package(ctx->importer, name, version, arch); rhughes@241: razor_importer_add_property(ctx->importer, name, jbowes@284: RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_PROVIDES, jbowes@284: version); rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: end_package(struct test_context *ctx) rhughes@241: { rhughes@241: razor_importer_finish_package(ctx->importer); rhughes@241: } rhughes@241: rhughes@241: static void jbowes@284: add_property(struct test_context *ctx, enum razor_property_flags type, const char *name, enum razor_property_flags rel, const char *version) rhughes@241: { rhughes@241: razor_importer_add_property(ctx->importer, name, jbowes@284: rel | type, version); jbowes@284: } jbowes@284: jbowes@284: static const char* jbowes@284: razor_property_flags_relation_to_string(enum razor_property_flags rel) jbowes@284: { jbowes@284: if (rel == RAZOR_PROPERTY_LESS) jbowes@284: return "<"; jbowes@284: if (rel == (RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_LESS)) jbowes@284: return "<="; jbowes@284: if (rel == RAZOR_PROPERTY_EQUAL) jbowes@284: return "="; jbowes@284: if (rel == (RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_GREATER)) jbowes@284: return ">="; jbowes@284: if (rel == RAZOR_PROPERTY_GREATER) jbowes@284: return ">"; jbowes@284: jbowes@284: return ""; rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: check_unsatisfiable_property(struct test_context *ctx, jbowes@284: enum razor_property_flags type, rhughes@241: const char *name, jbowes@284: enum razor_property_flags rel, rhughes@241: const char *version) rhughes@241: { rhughes@241: if (!version) rhughes@241: version = ""; rhughes@241: rhughes@241: if (razor_transaction_unsatisfied_property(ctx->trans, jbowes@284: name, rel | type, version)) rhughes@241: return; rhughes@241: rhughes@241: fprintf(stderr, " didn't get unsatisfiable '%s %s %s'\n", jbowes@284: name, razor_property_flags_relation_to_string(rel), version); rhughes@241: ctx->errors++; rhughes@241: } rhughes@241: rhughes@241: static void jbowes@284: start_property(struct test_context *ctx, enum razor_property_flags type, const char **atts) rhughes@241: { rhughes@241: const char *name = NULL, *rel_str = NULL, *version = NULL; jbowes@284: enum razor_property_flags rel; rhughes@241: rhughes@241: get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL); rhughes@241: if (name == NULL) { rhughes@241: fprintf(stderr, " no name specified for property\n"); rhughes@241: exit(1); rhughes@241: } rhughes@241: if (version) { rhughes@241: rel = parse_relation(rel_str); rhughes@241: if (rel == -1) { rhughes@241: fprintf(stderr, " bad or missing version relation for property %s\n", name); rhughes@241: exit(1); rhughes@241: } rhughes@241: } else jbowes@284: rel = RAZOR_PROPERTY_EQUAL; rhughes@241: rhughes@241: if (ctx->unsat) rhughes@241: check_unsatisfiable_property(ctx, type, name, rel, version); rhughes@241: else rhughes@241: add_property(ctx, type, name, rel, version); rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_transaction(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: ctx->n_install_pkgs = 0; rhughes@241: ctx->n_remove_pkgs = 0; rhughes@241: } rhughes@241: krh@306: static struct razor_package * krh@306: get_package(struct razor_set *set, const char *package) krh@306: { krh@306: struct razor_package_iterator *pi; krh@306: struct razor_package *p; krh@306: const char *name, *version, *arch; krh@306: krh@306: pi = razor_package_iterator_create(set); richard@307: while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_NAME, &name, richard@307: RAZOR_DETAIL_VERSION, &version, richard@307: RAZOR_DETAIL_ARCH, &arch, richard@307: RAZOR_DETAIL_LAST)) { krh@306: if (strcmp(package, name) == 0) krh@306: break; krh@306: } krh@306: razor_package_iterator_destroy(pi); krh@306: krh@306: return p; krh@306: } krh@306: rhughes@241: static void rhughes@241: end_transaction(struct test_context *ctx) rhughes@241: { rhughes@241: struct razor_package *pkg; rhughes@241: int errors, i; rhughes@241: rhughes@241: ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set); rhughes@241: for (i = 0; i < ctx->n_install_pkgs; i++) { krh@306: pkg = get_package(ctx->repo_set, ctx->install_pkgs[i]); rhughes@241: razor_transaction_install_package(ctx->trans, pkg); rhughes@241: } rhughes@241: for (i = 0; i < ctx->n_remove_pkgs; i++) { krh@306: pkg = get_package(ctx->system_set, ctx->remove_pkgs[i]); jbowes@258: if (!pkg) krh@306: pkg = get_package(ctx->repo_set, ctx->remove_pkgs[i]); jbowes@258: rhughes@241: razor_transaction_remove_package(ctx->trans, pkg); rhughes@241: } rhughes@241: jbowes@284: razor_transaction_resolve(ctx->trans); jbowes@284: errors = razor_transaction_describe(ctx->trans); rhughes@241: printf("\n"); rhughes@241: rhughes@241: while (ctx->n_install_pkgs--) rhughes@241: free(ctx->install_pkgs[ctx->n_install_pkgs]); rhughes@241: while (ctx->n_remove_pkgs--) rhughes@241: free(ctx->remove_pkgs[ctx->n_remove_pkgs]); rhughes@241: rhughes@241: if (!errors) { rhughes@241: struct razor_set *new; rhughes@241: new = razor_transaction_finish(ctx->trans); jbowes@258: ctx->trans = NULL; rhughes@241: ctx->system_set = new; rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_install_or_update(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: const char *name = NULL; rhughes@241: rhughes@241: get_atts(atts, "name", &name, NULL); rhughes@241: if (!name) { rhughes@241: fprintf(stderr, " install/update with no name\n"); rhughes@241: exit(1); rhughes@241: } rhughes@241: rhughes@241: ctx->install_pkgs[ctx->n_install_pkgs++] = strdup(name); rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_remove(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: const char *name = NULL; rhughes@241: rhughes@241: get_atts(atts, "name", &name, NULL); rhughes@241: if (!name) { rhughes@241: fprintf(stderr, " remove with no name\n"); rhughes@241: exit(1); rhughes@241: } rhughes@241: rhughes@241: ctx->remove_pkgs[ctx->n_remove_pkgs++] = strdup(name); rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_result(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: ctx->in_result = 1; rhughes@241: } rhughes@241: rhughes@241: static void jbowes@284: diff_callback(enum razor_diff_action action, jbowes@284: struct razor_package *package, jbowes@284: const char *name, jbowes@284: const char *version, rhughes@241: const char *arch, rhughes@241: void *data) rhughes@241: { rhughes@241: struct test_context *ctx = data; rhughes@241: rhughes@241: ctx->errors++; jbowes@284: if (action == RAZOR_DIFF_ACTION_REMOVE) { rhughes@241: fprintf(stderr, " result set should not contain %s %s\n", jbowes@284: name, version); rhughes@241: } else { rhughes@241: fprintf(stderr, " result set should contain %s %s\n", jbowes@284: name, version); rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: end_result(struct test_context *ctx) rhughes@241: { rhughes@241: ctx->in_result = 0; rhughes@241: rhughes@241: if (ctx->result_set) { rhughes@241: if (!ctx->system_set) rhughes@241: ctx->system_set = razor_set_create(); rhughes@241: razor_set_diff(ctx->system_set, ctx->result_set, rhughes@241: diff_callback, ctx); rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_unsatisfiable(struct test_context *ctx, const char **atts) rhughes@241: { rhughes@241: if (ctx->result_set) { rhughes@241: fprintf(stderr, "Expected to fail, but didn't\n"); rhughes@241: exit(1); rhughes@241: } rhughes@241: rhughes@241: ctx->unsat = 1; rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: end_unsatisfiable(struct test_context *ctx) rhughes@241: { rhughes@241: ctx->unsat = 0; rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: start_test_element(void *data, const char *element, const char **atts) rhughes@241: { rhughes@241: struct test_context *ctx = data; rhughes@241: rhughes@241: if (strcmp(element, "tests") == 0) { rhughes@241: ; rhughes@241: } else if (strcmp(element, "test") == 0) { rhughes@241: start_test(ctx, atts); rhughes@241: } else if (strcmp(element, "set") == 0) { rhughes@241: start_set(ctx, atts); rhughes@241: } else if (strcmp(element, "transaction") == 0) { rhughes@241: start_transaction(ctx, atts); rhughes@241: } else if (strcmp(element, "install") == 0) { rhughes@241: start_install_or_update(ctx, atts); rhughes@241: } else if (strcmp(element, "install") == 0) { rhughes@241: start_install_or_update(ctx, atts); rhughes@241: } else if (strcmp(element, "remove") == 0) { rhughes@241: start_remove(ctx, atts); rhughes@241: } else if (strcmp(element, "result") == 0) { rhughes@241: start_result(ctx, atts); rhughes@241: } else if (strcmp(element, "unsatisfiable") == 0) { rhughes@241: start_unsatisfiable(ctx, atts); rhughes@241: } else if (strcmp(element, "package") == 0) { rhughes@241: start_package(ctx, atts); rhughes@241: } else if (strcmp(element, "requires") == 0) { rhughes@241: start_property(ctx, RAZOR_PROPERTY_REQUIRES, atts); rhughes@241: } else if (strcmp(element, "provides") == 0) { rhughes@241: start_property(ctx, RAZOR_PROPERTY_PROVIDES, atts); rhughes@241: } else if (strcmp(element, "conflicts") == 0) { rhughes@241: start_property(ctx, RAZOR_PROPERTY_CONFLICTS, atts); rhughes@241: } else if (strcmp(element, "obsoletes") == 0) { rhughes@241: start_property(ctx, RAZOR_PROPERTY_OBSOLETES, atts); rhughes@241: } else { rhughes@241: fprintf(stderr, "Unrecognized element '%s'\n", element); rhughes@241: exit(1); rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: static void rhughes@241: end_test_element (void *data, const char *element) rhughes@241: { rhughes@241: struct test_context *ctx = data; rhughes@241: rhughes@241: if (strcmp(element, "test") == 0) { rhughes@241: end_test(ctx); rhughes@241: } else if (strcmp(element, "set") == 0) { rhughes@241: end_set(ctx); rhughes@241: } else if (strcmp(element, "package") == 0) { rhughes@241: end_package(ctx); rhughes@241: } else if (strcmp(element, "transaction") == 0) { rhughes@241: end_transaction(ctx); rhughes@241: } else if (strcmp(element, "result") == 0) { rhughes@241: end_result(ctx); rhughes@241: } else if (strcmp(element, "unsatisfiable") == 0) { rhughes@241: end_unsatisfiable(ctx); rhughes@241: } rhughes@241: } rhughes@241: rhughes@241: int main(int argc, char *argv[]) rhughes@241: { rhughes@241: struct test_context ctx; rhughes@241: const char *test_file; rhughes@241: rhughes@241: memset(&ctx, 0, sizeof ctx); rhughes@241: rhughes@241: if (argc > 3) { rhughes@241: fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]); rhughes@241: exit(-1); rhughes@241: } rhughes@241: rhughes@241: if (argc >= 2 && !strcmp (argv[1], "-d")) { rhughes@241: ctx.debug = 1; rhughes@241: argc--; rhughes@241: argv++; rhughes@241: } rhughes@241: if (argc == 2) rhughes@241: test_file = argv[1]; rhughes@241: else rhughes@241: test_file = "test.xml"; rhughes@241: rhughes@241: parse_xml_file(test_file, start_test_element, end_test_element, &ctx); rhughes@241: rhughes@241: if (ctx.errors) { rhughes@241: fprintf(stderr, "\n%d errors\n", ctx.errors); rhughes@241: return 1; rhughes@241: } else rhughes@241: return 0; rhughes@241: }