src/test-driver.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Apr 29 17:00:01 2009 +0100 (2009-04-29)
changeset 361 2523d03a840e
parent 307 95b6bcadd6c4
child 369 f8c27fe9fe63
permissions -rw-r--r--
Add support for preloading lua modules. This is useful both when
providing lua bindings to applications based on librazor and when
producing static binaries using librazor (where otherwise the lua
POSIX library would need to be included as an additional dynamic
object).
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation; either version 2 of the License, or
     8  * (at your option) any later version.
     9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along
    16  * with this program; if not, write to the Free Software Foundation, Inc.,
    17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    18  */
    19 
    20 #include <stdio.h>
    21 #include <string.h>
    22 #include <stdarg.h>
    23 #include <unistd.h>
    24 #include <fcntl.h>
    25 #include <errno.h>
    26 #include <expat.h>
    27 
    28 #include "razor.h"
    29 
    30 #define XML_BUFFER_SIZE 4096
    31 
    32 static void
    33 parse_xml_file(const char *filename,
    34 	       XML_StartElementHandler start,
    35 	       XML_EndElementHandler end,
    36 	       void *data)
    37 {
    38 	XML_Parser parser;
    39 	char *buffer;
    40 	int fd, len, err;
    41 
    42 	parser = XML_ParserCreate(NULL);
    43 	XML_SetElementHandler(parser, start, end);
    44 	XML_SetUserData(parser, data);
    45 
    46 	fd = open(filename, O_RDONLY);
    47 	if (fd < 0) {
    48 		fprintf(stderr, "failed to open %s: %s\n", filename,
    49 			strerror(errno));
    50 		exit(-1);
    51 	}
    52 
    53 	while (1) {
    54 		buffer = XML_GetBuffer(parser, XML_BUFFER_SIZE);
    55 		len = read(fd, buffer, XML_BUFFER_SIZE);
    56 		if (len == 0)
    57 			break;
    58 		err = XML_ParseBuffer(parser, len, len == 0);
    59 		if (err == XML_STATUS_ERROR) {
    60 			fprintf(stderr, "parse error at line %lu:\n%s\n",
    61 				XML_GetCurrentLineNumber(parser),
    62 				XML_ErrorString(XML_GetErrorCode(parser)));
    63 			exit(-1);
    64 		}
    65 	}
    66 
    67 	if (fd < 0) {
    68 		perror("read");
    69 		exit(-1);
    70 	}
    71 
    72 	close(fd);
    73 }
    74 
    75 struct test_context {
    76 	struct razor_set *system_set, *repo_set, *result_set;
    77 
    78 	struct razor_importer *importer;
    79 	struct razor_set **importer_set;
    80 
    81 	struct razor_transaction *trans;
    82 
    83 	char *install_pkgs[3], *remove_pkgs[3];
    84 	int n_install_pkgs, n_remove_pkgs;
    85 
    86 	int unsat;
    87 	int in_result;
    88 
    89 	int debug, errors;
    90 };
    91 
    92 static void
    93 get_atts(const char **atts, ...)
    94 {
    95 	va_list ap;
    96 	const char *name, **ptr;
    97 	int i;
    98 
    99 	va_start(ap, atts);
   100 	while (name = va_arg(ap, const char *), name != NULL) {
   101 		ptr = va_arg(ap, const char **);
   102 		*ptr = NULL;
   103 		for (i = 0; atts[i]; i += 2) {
   104 			if (strcmp(atts[i], name) == 0)
   105 				*ptr = atts[i + 1];
   106 		}
   107 	}
   108 	va_end(ap);
   109 }
   110 
   111 static enum razor_property_flags
   112 parse_relation (const char *rel_str)
   113 {
   114 	if (!rel_str)
   115 		return -1;
   116 	if (rel_str[0] == 'L')
   117 		return rel_str[1] == 'E' ? RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL : RAZOR_PROPERTY_LESS;
   118 	else if (rel_str[0] == 'G')
   119 		return rel_str[1] == 'E' ? RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL : RAZOR_PROPERTY_GREATER;
   120 	else if (rel_str[0] == 'E' || rel_str[1] == 'Q')
   121 		return RAZOR_PROPERTY_EQUAL;
   122 	else
   123 		return -1;
   124 }
   125 
   126 static void
   127 start_test(struct test_context *ctx, const char **atts)
   128 {
   129 	const char *name = NULL;
   130 
   131 	get_atts(atts, "name", &name, NULL);
   132 	if (!name) {
   133 		fprintf(stderr, "Test with no name\n");
   134 		exit(1);
   135 	}
   136 	printf("%s\n", name);
   137 }
   138 
   139 static void
   140 end_test(struct test_context *ctx)
   141 {
   142 	if (ctx->system_set) {
   143 		razor_set_destroy(ctx->system_set);
   144 		ctx->system_set = NULL;
   145 	}
   146 	if (ctx->repo_set) {
   147 		razor_set_destroy(ctx->repo_set);
   148 		ctx->repo_set = NULL;
   149 	}
   150 	if (ctx->result_set) {
   151 		razor_set_destroy(ctx->result_set);
   152 		ctx->result_set = NULL;
   153 	}
   154 	if (ctx->trans) {
   155 		razor_transaction_destroy(ctx->trans);
   156 		ctx->trans = NULL;
   157 	}
   158 }
   159 
   160 static void
   161 start_set(struct test_context *ctx, const char **atts)
   162 {
   163 	const char *name = NULL;
   164 
   165 	ctx->importer = razor_importer_create();
   166 	get_atts(atts, "name", &name, NULL);
   167 	if (!name)
   168 		ctx->importer_set = &ctx->result_set;
   169 	else if (!strcmp(name, "system"))
   170 		ctx->importer_set = &ctx->system_set;
   171 	else if (!strcmp(name, "repo"))
   172 		ctx->importer_set = &ctx->repo_set;
   173 	else {
   174 		fprintf(stderr, "  bad set name '%s'\n", name);
   175 		exit(1);
   176 	}
   177 }
   178 
   179 static void
   180 end_set(struct test_context *ctx)
   181 {
   182 	*ctx->importer_set = razor_importer_finish(ctx->importer);
   183 	ctx->importer = NULL;
   184 }
   185 
   186 static void
   187 start_package(struct test_context *ctx, const char **atts)
   188 {
   189 	const char *name = NULL, *version = NULL, *arch = NULL;
   190 
   191 	get_atts(atts, "name", &name,
   192 		 "version", &version,
   193 		 "arch", &arch,
   194 		 NULL);
   195 
   196 	if (!name) {
   197 		fprintf(stderr, "  package with no name\n");
   198 		exit(1);
   199 	}
   200 
   201 	razor_importer_begin_package(ctx->importer, name, version, arch);
   202 	razor_importer_add_property(ctx->importer, name,
   203 				    RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_PROVIDES,
   204 				    version);
   205 }
   206 
   207 static void
   208 end_package(struct test_context *ctx)
   209 {
   210 	razor_importer_finish_package(ctx->importer);
   211 }
   212 
   213 static void
   214 add_property(struct test_context *ctx, enum razor_property_flags type, const char *name, enum razor_property_flags rel, const char *version)
   215 {
   216 	razor_importer_add_property(ctx->importer, name,
   217 				    rel | type, version);
   218 }
   219 
   220 static const char*
   221 razor_property_flags_relation_to_string(enum razor_property_flags rel)
   222 {
   223 	if (rel == RAZOR_PROPERTY_LESS)
   224 		return "<";
   225 	if (rel == (RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_LESS))
   226 		return "<=";
   227 	if (rel == RAZOR_PROPERTY_EQUAL)
   228 		return "=";
   229 	if (rel == (RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_GREATER))
   230 		return ">=";
   231 	if (rel == RAZOR_PROPERTY_GREATER)
   232 		return ">";
   233 
   234 	return "";
   235 }
   236 
   237 static void
   238 check_unsatisfiable_property(struct test_context *ctx,
   239 			     enum razor_property_flags type,
   240 			     const char *name,
   241 			     enum razor_property_flags rel,
   242 			     const char *version)
   243 {
   244 	if (!version)
   245 		version = "";
   246 
   247 	if (razor_transaction_unsatisfied_property(ctx->trans,
   248 						   name, rel | type, version))
   249 		return;
   250 
   251 	fprintf(stderr, "  didn't get unsatisfiable '%s %s %s'\n",
   252 		name, razor_property_flags_relation_to_string(rel), version);
   253 	ctx->errors++;
   254 }
   255 
   256 static void
   257 start_property(struct test_context *ctx, enum razor_property_flags type, const char **atts)
   258 {
   259 	const char *name = NULL, *rel_str = NULL, *version = NULL;
   260 	enum razor_property_flags rel;
   261 
   262 	get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL);
   263 	if (name == NULL) {
   264 		fprintf(stderr, "  no name specified for property\n");
   265 		exit(1);
   266 	}
   267 	if (version) {
   268 		rel = parse_relation(rel_str);
   269 		if (rel == -1) {
   270 			fprintf(stderr, "  bad or missing version relation for property %s\n", name);
   271 			exit(1);
   272 		}
   273 	} else
   274 		rel = RAZOR_PROPERTY_EQUAL;
   275 
   276 	if (ctx->unsat)
   277 		check_unsatisfiable_property(ctx, type, name, rel, version);
   278 	else
   279 		add_property(ctx, type, name, rel, version);
   280 }
   281 
   282 static void
   283 start_transaction(struct test_context *ctx, const char **atts)
   284 {
   285 	ctx->n_install_pkgs = 0;
   286 	ctx->n_remove_pkgs = 0;
   287 }
   288 
   289 static struct razor_package *
   290 get_package(struct razor_set *set, const char *package)
   291 {
   292 	struct razor_package_iterator *pi;
   293 	struct razor_package *p;
   294 	const char *name, *version, *arch;
   295 
   296 	pi = razor_package_iterator_create(set);
   297 	while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_NAME, &name,
   298 					   RAZOR_DETAIL_VERSION, &version,
   299 					   RAZOR_DETAIL_ARCH, &arch,
   300 					   RAZOR_DETAIL_LAST)) {
   301 		if (strcmp(package, name) == 0)
   302 			break;
   303 	}
   304 	razor_package_iterator_destroy(pi);
   305 
   306 	return p;
   307 }
   308 
   309 static void
   310 end_transaction(struct test_context *ctx)
   311 {
   312 	struct razor_package *pkg;
   313 	int errors, i;
   314 
   315 	ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set);
   316 	for (i = 0; i < ctx->n_install_pkgs; i++) {
   317 		pkg = get_package(ctx->repo_set, ctx->install_pkgs[i]);
   318 		razor_transaction_install_package(ctx->trans, pkg);
   319 	}
   320 	for (i = 0; i < ctx->n_remove_pkgs; i++) {
   321 		pkg = get_package(ctx->system_set, ctx->remove_pkgs[i]);
   322 		if (!pkg)
   323 			pkg = get_package(ctx->repo_set, ctx->remove_pkgs[i]);
   324 
   325 		razor_transaction_remove_package(ctx->trans, pkg);
   326 	}
   327 
   328 	razor_transaction_resolve(ctx->trans);
   329 	errors = razor_transaction_describe(ctx->trans);
   330 	printf("\n");
   331 
   332 	while (ctx->n_install_pkgs--)
   333 		free(ctx->install_pkgs[ctx->n_install_pkgs]);
   334 	while (ctx->n_remove_pkgs--)
   335 		free(ctx->remove_pkgs[ctx->n_remove_pkgs]);
   336 
   337 	if (!errors) {
   338 		struct razor_set *new;
   339 		new = razor_transaction_finish(ctx->trans);
   340 		ctx->trans = NULL;
   341 		ctx->system_set = new;
   342 	}
   343 }
   344 
   345 static void
   346 start_install_or_update(struct test_context *ctx, const char **atts)
   347 {
   348 	const char *name = NULL;
   349 
   350 	get_atts(atts, "name", &name, NULL);
   351 	if (!name) {
   352 		fprintf(stderr, "  install/update with no name\n");
   353 		exit(1);
   354 	}
   355 
   356 	ctx->install_pkgs[ctx->n_install_pkgs++] = strdup(name);
   357 }
   358 
   359 static void
   360 start_remove(struct test_context *ctx, const char **atts)
   361 {
   362 	const char *name = NULL;
   363 
   364 	get_atts(atts, "name", &name, NULL);
   365 	if (!name) {
   366 		fprintf(stderr, "  remove with no name\n");
   367 		exit(1);
   368 	}
   369 
   370 	ctx->remove_pkgs[ctx->n_remove_pkgs++] = strdup(name);
   371 }
   372 
   373 static void
   374 start_result(struct test_context *ctx, const char **atts)
   375 {
   376 	ctx->in_result = 1;
   377 }
   378 
   379 static void
   380 diff_callback(enum razor_diff_action action,
   381 	      struct razor_package *package,
   382 	      const char *name,
   383 	      const char *version,
   384 	      const char *arch,
   385 	      void *data)
   386 {
   387 	struct test_context *ctx = data;
   388 
   389 	ctx->errors++;
   390 	if (action == RAZOR_DIFF_ACTION_REMOVE) {
   391 		fprintf(stderr, "  result set should not contain %s %s\n",
   392 			name, version);
   393 	} else {
   394 		fprintf(stderr, "  result set should contain %s %s\n",
   395 			name, version);
   396 	}
   397 }
   398 
   399 static void
   400 end_result(struct test_context *ctx)
   401 {
   402 	ctx->in_result = 0;
   403 
   404 	if (ctx->result_set) {
   405 		if (!ctx->system_set)
   406 			ctx->system_set = razor_set_create();
   407 		razor_set_diff(ctx->system_set, ctx->result_set,
   408 			       diff_callback, ctx);
   409 	}
   410 }
   411 
   412 static void
   413 start_unsatisfiable(struct test_context *ctx, const char **atts)
   414 {
   415 	if (ctx->result_set) {
   416 		fprintf(stderr, "Expected to fail, but didn't\n");
   417 		exit(1);
   418 	}
   419 
   420 	ctx->unsat = 1;
   421 }
   422 
   423 static void
   424 end_unsatisfiable(struct test_context *ctx)
   425 {
   426 	ctx->unsat = 0;
   427 }
   428 
   429 static void
   430 start_test_element(void *data, const char *element, const char **atts)
   431 {
   432 	struct test_context *ctx = data;
   433 
   434 	if (strcmp(element, "tests") == 0) {
   435 		;
   436 	} else if (strcmp(element, "test") == 0) {
   437 		start_test(ctx, atts);
   438 	} else if (strcmp(element, "set") == 0) {
   439 		start_set(ctx, atts);
   440 	} else if (strcmp(element, "transaction") == 0) {
   441 		start_transaction(ctx, atts);
   442 	} else if (strcmp(element, "install") == 0) {
   443 		start_install_or_update(ctx, atts);
   444 	} else if (strcmp(element, "install") == 0) {
   445 		start_install_or_update(ctx, atts);
   446 	} else if (strcmp(element, "remove") == 0) {
   447 		start_remove(ctx, atts);
   448 	} else if (strcmp(element, "result") == 0) {
   449 		start_result(ctx, atts);
   450 	} else if (strcmp(element, "unsatisfiable") == 0) {
   451 		start_unsatisfiable(ctx, atts);
   452 	} else if (strcmp(element, "package") == 0) {
   453 		start_package(ctx, atts);
   454 	} else if (strcmp(element, "requires") == 0) {
   455 		start_property(ctx, RAZOR_PROPERTY_REQUIRES, atts);
   456 	} else if (strcmp(element, "provides") == 0) {
   457 		start_property(ctx, RAZOR_PROPERTY_PROVIDES, atts);
   458 	} else if (strcmp(element, "conflicts") == 0) {
   459 		start_property(ctx, RAZOR_PROPERTY_CONFLICTS, atts);
   460 	} else if (strcmp(element, "obsoletes") == 0) {
   461 		start_property(ctx, RAZOR_PROPERTY_OBSOLETES, atts);
   462 	} else {
   463 		fprintf(stderr, "Unrecognized element '%s'\n", element);
   464 		exit(1);
   465 	}
   466 }
   467 
   468 static void
   469 end_test_element (void *data, const char *element)
   470 {
   471 	struct test_context *ctx = data;
   472 
   473 	if (strcmp(element, "test") == 0) {
   474 		end_test(ctx);
   475 	} else if (strcmp(element, "set") == 0) {
   476 		end_set(ctx);
   477 	} else if (strcmp(element, "package") == 0) {
   478 		end_package(ctx);
   479 	} else if (strcmp(element, "transaction") == 0) {
   480 		end_transaction(ctx);
   481 	} else if (strcmp(element, "result") == 0) {
   482 		end_result(ctx);
   483 	} else if (strcmp(element, "unsatisfiable") == 0) {
   484 		end_unsatisfiable(ctx);
   485 	}
   486 }
   487 
   488 int main(int argc, char *argv[])
   489 {
   490 	struct test_context ctx;
   491 	const char *test_file;
   492 
   493 	memset(&ctx, 0, sizeof ctx);
   494 
   495 	if (argc > 3) {
   496 		fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]);
   497 		exit(-1);
   498 	}
   499 
   500 	if (argc >= 2 && !strcmp (argv[1], "-d")) {
   501 		ctx.debug = 1;
   502 		argc--;
   503 		argv++;
   504 	}
   505 	if (argc == 2)
   506 		test_file = argv[1];
   507 	else
   508 		test_file = "test.xml";
   509 
   510 	parse_xml_file(test_file, start_test_element, end_test_element, &ctx);
   511 
   512 	if (ctx.errors) {
   513 		fprintf(stderr, "\n%d errors\n", ctx.errors);
   514 		return 1;
   515 	} else
   516 		return 0;
   517 }