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