test-driver.c
author Dan Winship <danw@gnome.org>
Fri Feb 29 12:45:08 2008 -0500 (2008-02-29)
changeset 138 49deac048d07
parent 131 1b5338bcb7d1
child 145 097f7b60b97a
permissions -rw-r--r--
implement file dependencies for installs

removes are trickier because there are no backlinks from the files array
the properties array, so there's currently no way to efficiently determine
what packages are affected by the removal of a particular file
     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 	struct razor_transaction_package *unsat;
    63 
    64 	char *install_pkgs[3], *remove_pkgs[3];
    65 	int n_install_pkgs, n_remove_pkgs;
    66 
    67 	int in_result, result_errors;
    68 
    69 	int debug;
    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_version_relation
    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_VERSION_LESS_OR_EQUAL : RAZOR_VERSION_LESS;
    98 	else if (rel_str[0] == 'G')
    99 		return rel_str[1] == 'E' ? RAZOR_VERSION_GREATER_OR_EQUAL : RAZOR_VERSION_GREATER;
   100 	else if (rel_str[0] == 'E' || rel_str[1] == 'Q')
   101 		return RAZOR_VERSION_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_new();
   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, "version", &version, "arch", &arch, NULL);
   172 	if (!name) {
   173 		fprintf(stderr, "  package with no name\n");
   174 		exit(1);
   175 	}
   176 
   177 	razor_importer_begin_package(ctx->importer, name, version);
   178 	razor_importer_add_property(ctx->importer, name,
   179 				    RAZOR_VERSION_EQUAL, version,
   180 				    RAZOR_PROPERTY_PROVIDES);
   181 }
   182 
   183 static void
   184 end_package(struct test_context *ctx)
   185 {
   186 	razor_importer_finish_package(ctx->importer);
   187 }
   188 
   189 static void
   190 add_property(struct test_context *ctx, enum razor_property_type type, const char *name, enum razor_version_relation rel, const char *version)
   191 {
   192 	razor_importer_add_property(ctx->importer, name,
   193 				    rel, version, type);
   194 }
   195 
   196 static void
   197 check_unsatisfiable_property(struct test_context *ctx, enum razor_property_type type, const char *name, enum razor_version_relation rel, const char *version)
   198 {
   199 	if (!version)
   200 		version = "";
   201 
   202 	for (; ctx->unsat < ctx->trans->packages + ctx->trans->package_count; ctx->unsat++) {
   203 		if (ctx->unsat->state != RAZOR_PACKAGE_INSTALL_UNSATISFIABLE)
   204 			continue;
   205 		if (strcmp(name, ctx->unsat->req_property) != 0 ||
   206 		    rel != ctx->unsat->req_relation ||
   207 		    strcmp(version, ctx->unsat->req_version) != 0)
   208 			continue;
   209 
   210 		/* OK, found it, so skip over it and continue */
   211 		ctx->unsat++;
   212 		return;
   213 	}
   214 
   215 	fprintf(stderr, "  didn't get unsatisfiable '%s %s %s'\n",
   216 		name, razor_version_relations[rel], version);
   217 	exit(1);
   218 }
   219 
   220 static void
   221 start_property(struct test_context *ctx, enum razor_property_type type, const char **atts)
   222 {
   223 	const char *name = NULL, *rel_str = NULL, *version = NULL;
   224 	enum razor_version_relation rel;
   225 
   226 	get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL);
   227 	if (name == NULL) {
   228 		fprintf(stderr, "  no name specified for property\n");
   229 		exit(1);
   230 	}
   231 	if (version) {
   232 		rel = parse_relation(rel_str);
   233 		if (rel == -1) {
   234 			fprintf(stderr, "  bad or missing version relation for property %s\n", name);
   235 			exit(1);
   236 		}
   237 	} else
   238 		rel = RAZOR_VERSION_EQUAL;
   239 	
   240 	if (ctx->unsat)
   241 		check_unsatisfiable_property(ctx, type, name, rel, version);
   242 	else
   243 		add_property(ctx, type, name, rel, version);
   244 }
   245 
   246 static void
   247 start_transaction(struct test_context *ctx, const char **atts)
   248 {
   249 	ctx->n_install_pkgs = 0;
   250 	ctx->n_remove_pkgs = 0;
   251 }
   252 
   253 static void
   254 end_transaction(struct test_context *ctx)
   255 {
   256 	ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set,
   257 					      ctx->n_install_pkgs,
   258 					      (const char **)ctx->install_pkgs,
   259 					      ctx->n_remove_pkgs,
   260 					      (const char **)ctx->remove_pkgs);
   261 	if (ctx->debug) {
   262 		razor_transaction_describe(ctx->trans);
   263 		printf("\n");
   264 	}
   265 
   266 	while (ctx->n_install_pkgs--)
   267 		free(ctx->install_pkgs[ctx->n_install_pkgs]);
   268 	while (ctx->n_remove_pkgs--)
   269 		free(ctx->remove_pkgs[ctx->n_remove_pkgs]);
   270 
   271 	if (!ctx->trans->errors) {
   272 		struct razor_set *new;
   273 		new = razor_transaction_run(ctx->trans);
   274 		razor_set_destroy(ctx->system_set);
   275 		ctx->system_set = new;
   276 	}
   277 }
   278 
   279 static void
   280 start_install_or_update(struct test_context *ctx, const char **atts)
   281 {
   282 	const char *name = NULL;
   283 
   284 	get_atts(atts, "name", &name, NULL);
   285 	if (!name) {
   286 		fprintf(stderr, "  install/update with no name\n");
   287 		exit(1);
   288 	}
   289 
   290 	ctx->install_pkgs[ctx->n_install_pkgs++] = strdup(name);
   291 }
   292 
   293 static void
   294 start_remove(struct test_context *ctx, const char **atts)
   295 {
   296 	const char *name = NULL;
   297 
   298 	get_atts(atts, "name", &name, NULL);
   299 	if (!name) {
   300 		fprintf(stderr, "  remove with no name\n");
   301 		exit(1);
   302 	}
   303 
   304 	ctx->remove_pkgs[ctx->n_remove_pkgs++] = strdup(name);
   305 }
   306 
   307 static void
   308 start_result(struct test_context *ctx, const char **atts)
   309 {
   310 	ctx->in_result = 1;
   311 }
   312 
   313 static void
   314 diff_callback(const char *name,
   315 	      const char *old_version, const char *new_version,
   316 	      void *data)
   317 {
   318 	struct test_context *ctx = data;
   319 
   320 	ctx->result_errors++;
   321 	if (old_version) {
   322 		fprintf(stderr, "  result set should not contain %s %s\n",
   323 			name, old_version);
   324 	} else {
   325 		fprintf(stderr, "  result set should contain %s %s\n",
   326 			name, new_version);
   327 	}
   328 }
   329 
   330 static void
   331 end_result(struct test_context *ctx)
   332 {
   333 	ctx->in_result = 0;
   334 
   335 	if (ctx->result_set) {
   336 		if (!ctx->system_set)
   337 			ctx->system_set = razor_set_create();
   338 		ctx->result_errors = 0;
   339 		razor_set_diff(ctx->system_set, ctx->result_set,
   340 			       diff_callback, ctx);
   341 		if (ctx->result_errors)
   342 			exit(1);
   343 	}
   344 }
   345 
   346 static void
   347 start_unsatisfiable(struct test_context *ctx, const char **atts)
   348 {
   349 	if (ctx->result_set) {
   350 		fprintf(stderr, "Expected to fail, but didn't\n");
   351 		exit(1);
   352 	}
   353 
   354 	ctx->unsat = ctx->trans->packages;
   355 }
   356 
   357 static void
   358 end_unsatisfiable(struct test_context *ctx)
   359 {
   360 	ctx->unsat = NULL;
   361 }
   362 
   363 static void
   364 start_test_element(void *data, const char *element, const char **atts)
   365 {
   366 	struct test_context *ctx = data;
   367 
   368 	if (strcmp(element, "tests") == 0) {
   369 		;
   370 	} else if (strcmp(element, "test") == 0) {
   371 		start_test(ctx, atts);
   372 	} else if (strcmp(element, "set") == 0) {
   373 		start_set(ctx, atts);
   374 	} else if (strcmp(element, "transaction") == 0) {
   375 		start_transaction(ctx, atts);
   376 	} else if (strcmp(element, "install") == 0) {
   377 		start_install_or_update(ctx, atts);
   378 	} else if (strcmp(element, "install") == 0) {
   379 		start_install_or_update(ctx, atts);
   380 	} else if (strcmp(element, "remove") == 0) {
   381 		start_remove(ctx, atts);
   382 	} else if (strcmp(element, "result") == 0) {
   383 		start_result(ctx, atts);
   384 	} else if (strcmp(element, "unsatisfiable") == 0) {
   385 		start_unsatisfiable(ctx, atts);
   386 	} else if (strcmp(element, "package") == 0) {
   387 		start_package(ctx, atts);
   388 	} else if (strcmp(element, "requires") == 0) {
   389 		start_property(ctx, RAZOR_PROPERTY_REQUIRES, atts);
   390 	} else if (strcmp(element, "provides") == 0) {
   391 		start_property(ctx, RAZOR_PROPERTY_PROVIDES, atts);
   392 	} else if (strcmp(element, "conflicts") == 0) {
   393 		start_property(ctx, RAZOR_PROPERTY_CONFLICTS, atts);
   394 	} else if (strcmp(element, "obsoletes") == 0) {
   395 		start_property(ctx, RAZOR_PROPERTY_OBSOLETES, atts);
   396 	} else {
   397 		fprintf(stderr, "Unrecognized element '%s'\n", element);
   398 		exit(1);
   399 	}
   400 }
   401 
   402 static void
   403 end_test_element (void *data, const char *element)
   404 {
   405 	struct test_context *ctx = data;
   406 
   407 	if (strcmp(element, "test") == 0) {
   408 		end_test(ctx);
   409 	} else if (strcmp(element, "set") == 0) {
   410 		end_set(ctx);
   411 	} else if (strcmp(element, "package") == 0) {
   412 		end_package(ctx);
   413 	} else if (strcmp(element, "transaction") == 0) {
   414 		end_transaction(ctx);
   415 	} else if (strcmp(element, "result") == 0) {
   416 		end_result(ctx);
   417 	} else if (strcmp(element, "unsatisfiable") == 0) {
   418 		end_unsatisfiable(ctx);
   419 	}
   420 }
   421 
   422 int main(int argc, char *argv[])
   423 {
   424 	struct test_context ctx;
   425 	const char *test_file;
   426 
   427 	memset(&ctx, 0, sizeof ctx);
   428 
   429 	if (argc > 3) {
   430 		fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]);
   431 		exit(-1);			
   432 	}
   433 
   434 	if (argc >= 2 && !strcmp (argv[1], "-d")) {
   435 		ctx.debug = 1;
   436 		argc--;
   437 		argv++;
   438 	}
   439 	if (argc == 2)
   440 		test_file = argv[1];
   441 	else
   442 		test_file = "test.xml";
   443 
   444 	parse_xml_file(test_file, start_test_element, end_test_element, &ctx);
   445 
   446 	return 0;
   447 }