src/test-driver.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Jan 07 18:00:06 2009 +0000 (2009-01-07)
changeset 326 e2ddf497b914
parent 306 cd3954499086
child 339 159067260aad
permissions -rw-r--r--
Support platforms where mkdir only takes one argument (eg., MS-Windows)
     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: %m\n", filename);
    49 		exit(-1);
    50 	}
    51 
    52 	while (1) {
    53 		buffer = XML_GetBuffer(parser, XML_BUFFER_SIZE);
    54 		len = read(fd, buffer, XML_BUFFER_SIZE);
    55 		if (len == 0)
    56 			break;
    57 		err = XML_ParseBuffer(parser, len, len == 0);
    58 		if (err == XML_STATUS_ERROR) {
    59 			fprintf(stderr, "parse error at line %lu:\n%s\n",
    60 				XML_GetCurrentLineNumber(parser),
    61 				XML_ErrorString(XML_GetErrorCode(parser)));
    62 			exit(-1);
    63 		}
    64 	}
    65 
    66 	if (fd < 0) {
    67 		fprintf(stderr, "read: %m\n");
    68 		exit(-1);
    69 	}
    70 
    71 	close(fd);
    72 }
    73 
    74 struct test_context {
    75 	struct razor_set *system_set, *repo_set, *result_set;
    76 
    77 	struct razor_importer *importer;
    78 	struct razor_set **importer_set;
    79 
    80 	struct razor_transaction *trans;
    81 
    82 	char *install_pkgs[3], *remove_pkgs[3];
    83 	int n_install_pkgs, n_remove_pkgs;
    84 
    85 	int unsat;
    86 	int in_result;
    87 
    88 	int debug, errors;
    89 };
    90 
    91 static void
    92 get_atts(const char **atts, ...)
    93 {
    94 	va_list ap;
    95 	const char *name, **ptr;
    96 	int i;
    97 
    98 	va_start(ap, atts);
    99 	while (name = va_arg(ap, const char *), name != NULL) {
   100 		ptr = va_arg(ap, const char **);
   101 		*ptr = NULL;
   102 		for (i = 0; atts[i]; i += 2) {
   103 			if (strcmp(atts[i], name) == 0)
   104 				*ptr = atts[i + 1];
   105 		}
   106 	}
   107 	va_end(ap);
   108 }
   109 
   110 static enum razor_property_flags
   111 parse_relation (const char *rel_str)
   112 {
   113 	if (!rel_str)
   114 		return -1;
   115 	if (rel_str[0] == 'L')
   116 		return rel_str[1] == 'E' ? RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL : RAZOR_PROPERTY_LESS;
   117 	else if (rel_str[0] == 'G')
   118 		return rel_str[1] == 'E' ? RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL : RAZOR_PROPERTY_GREATER;
   119 	else if (rel_str[0] == 'E' || rel_str[1] == 'Q')
   120 		return RAZOR_PROPERTY_EQUAL;
   121 	else
   122 		return -1;
   123 }
   124 
   125 static void
   126 start_test(struct test_context *ctx, const char **atts)
   127 {
   128 	const char *name = NULL;
   129 
   130 	get_atts(atts, "name", &name, NULL);
   131 	if (!name) {
   132 		fprintf(stderr, "Test with no name\n");
   133 		exit(1);
   134 	}
   135 	printf("%s\n", name);
   136 }
   137 
   138 static void
   139 end_test(struct test_context *ctx)
   140 {
   141 	if (ctx->system_set) {
   142 		razor_set_destroy(ctx->system_set);
   143 		ctx->system_set = NULL;
   144 	}
   145 	if (ctx->repo_set) {
   146 		razor_set_destroy(ctx->repo_set);
   147 		ctx->repo_set = NULL;
   148 	}
   149 	if (ctx->result_set) {
   150 		razor_set_destroy(ctx->result_set);
   151 		ctx->result_set = NULL;
   152 	}
   153 	if (ctx->trans) {
   154 		razor_transaction_destroy(ctx->trans);
   155 		ctx->trans = NULL;
   156 	}
   157 }
   158 
   159 static void
   160 start_set(struct test_context *ctx, const char **atts)
   161 {
   162 	const char *name = NULL;
   163 
   164 	ctx->importer = razor_importer_create();
   165 	get_atts(atts, "name", &name, NULL);
   166 	if (!name)
   167 		ctx->importer_set = &ctx->result_set;
   168 	else if (!strcmp(name, "system"))
   169 		ctx->importer_set = &ctx->system_set;
   170 	else if (!strcmp(name, "repo"))
   171 		ctx->importer_set = &ctx->repo_set;
   172 	else {
   173 		fprintf(stderr, "  bad set name '%s'\n", name);
   174 		exit(1);
   175 	}
   176 }
   177 
   178 static void
   179 end_set(struct test_context *ctx)
   180 {
   181 	*ctx->importer_set = razor_importer_finish(ctx->importer);
   182 	ctx->importer = NULL;
   183 }
   184 
   185 static void
   186 start_package(struct test_context *ctx, const char **atts)
   187 {
   188 	const char *name = NULL, *version = NULL, *arch = NULL;
   189 
   190 	get_atts(atts, "name", &name,
   191 		 "version", &version,
   192 		 "arch", &arch,
   193 		 NULL);
   194 
   195 	if (!name) {
   196 		fprintf(stderr, "  package with no name\n");
   197 		exit(1);
   198 	}
   199 
   200 	razor_importer_begin_package(ctx->importer, name, version, arch);
   201 	razor_importer_add_property(ctx->importer, name,
   202 				    RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_PROVIDES,
   203 				    version);
   204 }
   205 
   206 static void
   207 end_package(struct test_context *ctx)
   208 {
   209 	razor_importer_finish_package(ctx->importer);
   210 }
   211 
   212 static void
   213 add_property(struct test_context *ctx, enum razor_property_flags type, const char *name, enum razor_property_flags rel, const char *version)
   214 {
   215 	razor_importer_add_property(ctx->importer, name,
   216 				    rel | type, version);
   217 }
   218 
   219 static const char*
   220 razor_property_flags_relation_to_string(enum razor_property_flags rel)
   221 {
   222 	if (rel == RAZOR_PROPERTY_LESS)
   223 		return "<";
   224 	if (rel == (RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_LESS))
   225 		return "<=";
   226 	if (rel == RAZOR_PROPERTY_EQUAL)
   227 		return "=";
   228 	if (rel == (RAZOR_PROPERTY_EQUAL | RAZOR_PROPERTY_GREATER))
   229 		return ">=";
   230 	if (rel == RAZOR_PROPERTY_GREATER)
   231 		return ">";
   232 
   233 	return "";
   234 }
   235 
   236 static void
   237 check_unsatisfiable_property(struct test_context *ctx,
   238 			     enum razor_property_flags type,
   239 			     const char *name,
   240 			     enum razor_property_flags rel,
   241 			     const char *version)
   242 {
   243 	if (!version)
   244 		version = "";
   245 
   246 	if (razor_transaction_unsatisfied_property(ctx->trans,
   247 						   name, rel | type, version))
   248 		return;
   249 
   250 	fprintf(stderr, "  didn't get unsatisfiable '%s %s %s'\n",
   251 		name, razor_property_flags_relation_to_string(rel), version);
   252 	ctx->errors++;
   253 }
   254 
   255 static void
   256 start_property(struct test_context *ctx, enum razor_property_flags type, const char **atts)
   257 {
   258 	const char *name = NULL, *rel_str = NULL, *version = NULL;
   259 	enum razor_property_flags rel;
   260 
   261 	get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL);
   262 	if (name == NULL) {
   263 		fprintf(stderr, "  no name specified for property\n");
   264 		exit(1);
   265 	}
   266 	if (version) {
   267 		rel = parse_relation(rel_str);
   268 		if (rel == -1) {
   269 			fprintf(stderr, "  bad or missing version relation for property %s\n", name);
   270 			exit(1);
   271 		}
   272 	} else
   273 		rel = RAZOR_PROPERTY_EQUAL;
   274 
   275 	if (ctx->unsat)
   276 		check_unsatisfiable_property(ctx, type, name, rel, version);
   277 	else
   278 		add_property(ctx, type, name, rel, version);
   279 }
   280 
   281 static void
   282 start_transaction(struct test_context *ctx, const char **atts)
   283 {
   284 	ctx->n_install_pkgs = 0;
   285 	ctx->n_remove_pkgs = 0;
   286 }
   287 
   288 static struct razor_package *
   289 get_package(struct razor_set *set, const char *package)
   290 {
   291 	struct razor_package_iterator *pi;
   292 	struct razor_package *p;
   293 	const char *name, *version, *arch;
   294 
   295 	pi = razor_package_iterator_create(set);
   296 	while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_NAME, &name,
   297 					   RAZOR_DETAIL_VERSION, &version,
   298 					   RAZOR_DETAIL_ARCH, &arch,
   299 					   RAZOR_DETAIL_LAST)) {
   300 		if (strcmp(package, name) == 0)
   301 			break;
   302 	}
   303 	razor_package_iterator_destroy(pi);
   304 
   305 	return p;
   306 }
   307 
   308 static void
   309 end_transaction(struct test_context *ctx)
   310 {
   311 	struct razor_package *pkg;
   312 	int errors, i;
   313 
   314 	ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set);
   315 	for (i = 0; i < ctx->n_install_pkgs; i++) {
   316 		pkg = get_package(ctx->repo_set, ctx->install_pkgs[i]);
   317 		razor_transaction_install_package(ctx->trans, pkg);
   318 	}
   319 	for (i = 0; i < ctx->n_remove_pkgs; i++) {
   320 		pkg = get_package(ctx->system_set, ctx->remove_pkgs[i]);
   321 		if (!pkg)
   322 			pkg = get_package(ctx->repo_set, ctx->remove_pkgs[i]);
   323 
   324 		razor_transaction_remove_package(ctx->trans, pkg);
   325 	}
   326 
   327 	razor_transaction_resolve(ctx->trans);
   328 	errors = razor_transaction_describe(ctx->trans);
   329 	printf("\n");
   330 
   331 	while (ctx->n_install_pkgs--)
   332 		free(ctx->install_pkgs[ctx->n_install_pkgs]);
   333 	while (ctx->n_remove_pkgs--)
   334 		free(ctx->remove_pkgs[ctx->n_remove_pkgs]);
   335 
   336 	if (!errors) {
   337 		struct razor_set *new;
   338 		new = razor_transaction_finish(ctx->trans);
   339 		ctx->trans = NULL;
   340 		ctx->system_set = new;
   341 	}
   342 }
   343 
   344 static void
   345 start_install_or_update(struct test_context *ctx, const char **atts)
   346 {
   347 	const char *name = NULL;
   348 
   349 	get_atts(atts, "name", &name, NULL);
   350 	if (!name) {
   351 		fprintf(stderr, "  install/update with no name\n");
   352 		exit(1);
   353 	}
   354 
   355 	ctx->install_pkgs[ctx->n_install_pkgs++] = strdup(name);
   356 }
   357 
   358 static void
   359 start_remove(struct test_context *ctx, const char **atts)
   360 {
   361 	const char *name = NULL;
   362 
   363 	get_atts(atts, "name", &name, NULL);
   364 	if (!name) {
   365 		fprintf(stderr, "  remove with no name\n");
   366 		exit(1);
   367 	}
   368 
   369 	ctx->remove_pkgs[ctx->n_remove_pkgs++] = strdup(name);
   370 }
   371 
   372 static void
   373 start_result(struct test_context *ctx, const char **atts)
   374 {
   375 	ctx->in_result = 1;
   376 }
   377 
   378 static void
   379 diff_callback(enum razor_diff_action action,
   380 	      struct razor_package *package,
   381 	      const char *name,
   382 	      const char *version,
   383 	      const char *arch,
   384 	      void *data)
   385 {
   386 	struct test_context *ctx = data;
   387 
   388 	ctx->errors++;
   389 	if (action == RAZOR_DIFF_ACTION_REMOVE) {
   390 		fprintf(stderr, "  result set should not contain %s %s\n",
   391 			name, version);
   392 	} else {
   393 		fprintf(stderr, "  result set should contain %s %s\n",
   394 			name, version);
   395 	}
   396 }
   397 
   398 static void
   399 end_result(struct test_context *ctx)
   400 {
   401 	ctx->in_result = 0;
   402 
   403 	if (ctx->result_set) {
   404 		if (!ctx->system_set)
   405 			ctx->system_set = razor_set_create();
   406 		razor_set_diff(ctx->system_set, ctx->result_set,
   407 			       diff_callback, ctx);
   408 	}
   409 }
   410 
   411 static void
   412 start_unsatisfiable(struct test_context *ctx, const char **atts)
   413 {
   414 	if (ctx->result_set) {
   415 		fprintf(stderr, "Expected to fail, but didn't\n");
   416 		exit(1);
   417 	}
   418 
   419 	ctx->unsat = 1;
   420 }
   421 
   422 static void
   423 end_unsatisfiable(struct test_context *ctx)
   424 {
   425 	ctx->unsat = 0;
   426 }
   427 
   428 static void
   429 start_test_element(void *data, const char *element, const char **atts)
   430 {
   431 	struct test_context *ctx = data;
   432 
   433 	if (strcmp(element, "tests") == 0) {
   434 		;
   435 	} else if (strcmp(element, "test") == 0) {
   436 		start_test(ctx, atts);
   437 	} else if (strcmp(element, "set") == 0) {
   438 		start_set(ctx, atts);
   439 	} else if (strcmp(element, "transaction") == 0) {
   440 		start_transaction(ctx, atts);
   441 	} else if (strcmp(element, "install") == 0) {
   442 		start_install_or_update(ctx, atts);
   443 	} else if (strcmp(element, "install") == 0) {
   444 		start_install_or_update(ctx, atts);
   445 	} else if (strcmp(element, "remove") == 0) {
   446 		start_remove(ctx, atts);
   447 	} else if (strcmp(element, "result") == 0) {
   448 		start_result(ctx, atts);
   449 	} else if (strcmp(element, "unsatisfiable") == 0) {
   450 		start_unsatisfiable(ctx, atts);
   451 	} else if (strcmp(element, "package") == 0) {
   452 		start_package(ctx, atts);
   453 	} else if (strcmp(element, "requires") == 0) {
   454 		start_property(ctx, RAZOR_PROPERTY_REQUIRES, atts);
   455 	} else if (strcmp(element, "provides") == 0) {
   456 		start_property(ctx, RAZOR_PROPERTY_PROVIDES, atts);
   457 	} else if (strcmp(element, "conflicts") == 0) {
   458 		start_property(ctx, RAZOR_PROPERTY_CONFLICTS, atts);
   459 	} else if (strcmp(element, "obsoletes") == 0) {
   460 		start_property(ctx, RAZOR_PROPERTY_OBSOLETES, atts);
   461 	} else {
   462 		fprintf(stderr, "Unrecognized element '%s'\n", element);
   463 		exit(1);
   464 	}
   465 }
   466 
   467 static void
   468 end_test_element (void *data, const char *element)
   469 {
   470 	struct test_context *ctx = data;
   471 
   472 	if (strcmp(element, "test") == 0) {
   473 		end_test(ctx);
   474 	} else if (strcmp(element, "set") == 0) {
   475 		end_set(ctx);
   476 	} else if (strcmp(element, "package") == 0) {
   477 		end_package(ctx);
   478 	} else if (strcmp(element, "transaction") == 0) {
   479 		end_transaction(ctx);
   480 	} else if (strcmp(element, "result") == 0) {
   481 		end_result(ctx);
   482 	} else if (strcmp(element, "unsatisfiable") == 0) {
   483 		end_unsatisfiable(ctx);
   484 	}
   485 }
   486 
   487 int main(int argc, char *argv[])
   488 {
   489 	struct test_context ctx;
   490 	const char *test_file;
   491 
   492 	memset(&ctx, 0, sizeof ctx);
   493 
   494 	if (argc > 3) {
   495 		fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]);
   496 		exit(-1);
   497 	}
   498 
   499 	if (argc >= 2 && !strcmp (argv[1], "-d")) {
   500 		ctx.debug = 1;
   501 		argc--;
   502 		argv++;
   503 	}
   504 	if (argc == 2)
   505 		test_file = argv[1];
   506 	else
   507 		test_file = "test.xml";
   508 
   509 	parse_xml_file(test_file, start_test_element, end_test_element, &ctx);
   510 
   511 	if (ctx.errors) {
   512 		fprintf(stderr, "\n%d errors\n", ctx.errors);
   513 		return 1;
   514 	} else
   515 		return 0;
   516 }