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