src/test-driver.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Feb 09 20:45:27 2012 +0000 (2012-02-09)
changeset 418 33b825d3128d
parent 369 f8c27fe9fe63
child 444 a2908416b7cc
permissions -rw-r--r--
Add transaction barriers
These allow packages to be installed and removed which have scripts
that depend on each other when atomic transactions are involved.
Note that yum supports pre, but not other requires flags. post will
need similar support to the post scripts themselves pulling in the
requires flags from the rpms. Likewise preun and postun will need
similar handling to those scrips since the requires flags will need
to be stored in the razor database.
     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_unref(ctx->system_set);
   145 		ctx->system_set = NULL;
   146 	}
   147 	if (ctx->repo_set) {
   148 		razor_set_unref(ctx->repo_set);
   149 		ctx->repo_set = NULL;
   150 	}
   151 	if (ctx->result_set) {
   152 		razor_set_unref(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 }