src/test-driver.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Oct 09 17:27:41 2014 +0100 (2014-10-09)
changeset 455 df914f383f5c
parent 406 5ab137def3d1
permissions -rw-r--r--
Support downloading from local repository even without libcurl

Using the --url option of the razor executable, it is possible
to specify a yum repository on the local machine (eg., on installation
media) and import from there, eg.,:

C> razor --url file:///d:/ import-yum

This will be handled by libcurl if available but if not, an internal
copy routine will be used.

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