src/test-driver.c
author J. Ali Harlow <ali@juiblex.co.uk>
Fri Oct 17 10:10:57 2014 +0100 (2014-10-17)
changeset 458 3f841a46eab5
parent 406 5ab137def3d1
permissions -rw-r--r--
Fix multiple memory allocation problems (found with valgrind)
     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 }