src/test-driver.c
author James Bowes <jbowes@redhat.com>
Wed Jul 09 10:11:13 2008 -0400 (2008-07-09)
changeset 318 829d6711b316
parent 306 cd3954499086
child 339 159067260aad
permissions -rw-r--r--
Use strings to identify section types in the on-disk repo format.

Previously, a given razor file type had a fixed number of sections in a
fixed order, identified by an integer type. Now, sections are identified
by a named string (stored in a string pool after the section lists).

This will allow for razor files to contain arbitrary sections.

For bonus points, also drop the 4k section alignment and change the
magic byte string to "RZDB".

committer: Kristian H?gsberg <krh@redhat.com>
     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 }