src/test-driver.c
author J. Ali Harlow <ali@juiblex.co.uk>
Mon Jan 12 16:37:55 2009 +0000 (2009-01-12)
changeset 344 1b54aa2137d3
parent 307 95b6bcadd6c4
child 369 f8c27fe9fe63
permissions -rw-r--r--
Generate razor.pc in line with details determined during config
     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 }