src/test-driver.c
author Kristian H?gsberg <krh@redhat.com>
Fri Jun 20 21:56:43 2008 -0400 (2008-06-20)
changeset 254 ccb1c11968ab
child 258 29d5002bd17f
permissions -rw-r--r--
Introduce install/remove iterators.

These iterator constructors lets you pass in two sets and creates
an iterator for the packages to remove or the packages to install.
The iterators will step through the packages in a sequence that respects
the pre, post, preun and postun modifiers.

Right now, the install order isn't actually implemented, this patch just
implements the API changes and updates the applications.
     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_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,
   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_VERSION_EQUAL, version,
   184 				    RAZOR_PROPERTY_PROVIDES);
   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_type type, const char *name, enum razor_version_relation rel, const char *version)
   195 {
   196 	razor_importer_add_property(ctx->importer, name,
   197 				    rel, version, type);
   198 }
   199 
   200 static void
   201 check_unsatisfiable_property(struct test_context *ctx,
   202 			     enum razor_property_type type,
   203 			     const char *name,
   204 			     enum razor_version_relation rel,
   205 			     const char *version)
   206 {
   207 	static const char *relation_string[] = { "<", "<=", "=", ">=", ">" };
   208 
   209 	if (!version)
   210 		version = "";
   211 
   212 	if (razor_transaction_unsatisfied_property(ctx->trans,
   213 						   name, rel, version, type))
   214 		return;
   215 
   216 	fprintf(stderr, "  didn't get unsatisfiable '%s %s %s'\n",
   217 		name, relation_string[rel], version);
   218 	ctx->errors++;
   219 }
   220 
   221 static void
   222 start_property(struct test_context *ctx, enum razor_property_type type, const char **atts)
   223 {
   224 	const char *name = NULL, *rel_str = NULL, *version = NULL;
   225 	enum razor_version_relation rel;
   226 
   227 	get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL);
   228 	if (name == NULL) {
   229 		fprintf(stderr, "  no name specified for property\n");
   230 		exit(1);
   231 	}
   232 	if (version) {
   233 		rel = parse_relation(rel_str);
   234 		if (rel == -1) {
   235 			fprintf(stderr, "  bad or missing version relation for property %s\n", name);
   236 			exit(1);
   237 		}
   238 	} else
   239 		rel = RAZOR_VERSION_EQUAL;
   240 
   241 	if (ctx->unsat)
   242 		check_unsatisfiable_property(ctx, type, name, rel, version);
   243 	else
   244 		add_property(ctx, type, name, rel, version);
   245 }
   246 
   247 static void
   248 start_transaction(struct test_context *ctx, const char **atts)
   249 {
   250 	ctx->n_install_pkgs = 0;
   251 	ctx->n_remove_pkgs = 0;
   252 }
   253 
   254 static void
   255 end_transaction(struct test_context *ctx)
   256 {
   257 	struct razor_package *pkg;
   258 	int errors, i;
   259 
   260 	ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set);
   261 	for (i = 0; i < ctx->n_install_pkgs; i++) {
   262 		pkg = razor_set_get_package(ctx->repo_set,
   263 					    ctx->install_pkgs[i]);
   264 		razor_transaction_install_package(ctx->trans, pkg);
   265 	}
   266 	for (i = 0; i < ctx->n_remove_pkgs; i++) {
   267 		pkg = razor_set_get_package(ctx->repo_set,
   268 					    ctx->remove_pkgs[i]);
   269 		razor_transaction_remove_package(ctx->trans, pkg);
   270 	}
   271 
   272 	errors = razor_transaction_resolve(ctx->trans);
   273 	printf("\n");
   274 
   275 	while (ctx->n_install_pkgs--)
   276 		free(ctx->install_pkgs[ctx->n_install_pkgs]);
   277 	while (ctx->n_remove_pkgs--)
   278 		free(ctx->remove_pkgs[ctx->n_remove_pkgs]);
   279 
   280 	if (!errors) {
   281 		struct razor_set *new;
   282 		new = razor_transaction_finish(ctx->trans);
   283 		ctx->system_set = new;
   284 	}
   285 }
   286 
   287 static void
   288 start_install_or_update(struct test_context *ctx, const char **atts)
   289 {
   290 	const char *name = NULL;
   291 
   292 	get_atts(atts, "name", &name, NULL);
   293 	if (!name) {
   294 		fprintf(stderr, "  install/update with no name\n");
   295 		exit(1);
   296 	}
   297 
   298 	ctx->install_pkgs[ctx->n_install_pkgs++] = strdup(name);
   299 }
   300 
   301 static void
   302 start_remove(struct test_context *ctx, const char **atts)
   303 {
   304 	const char *name = NULL;
   305 
   306 	get_atts(atts, "name", &name, NULL);
   307 	if (!name) {
   308 		fprintf(stderr, "  remove with no name\n");
   309 		exit(1);
   310 	}
   311 
   312 	ctx->remove_pkgs[ctx->n_remove_pkgs++] = strdup(name);
   313 }
   314 
   315 static void
   316 start_result(struct test_context *ctx, const char **atts)
   317 {
   318 	ctx->in_result = 1;
   319 }
   320 
   321 static void
   322 diff_callback(const char *name,
   323 	      const char *old_version,
   324 	      const char *new_version,
   325 	      const char *arch,
   326 	      void *data)
   327 {
   328 	struct test_context *ctx = data;
   329 
   330 	ctx->errors++;
   331 	if (old_version) {
   332 		fprintf(stderr, "  result set should not contain %s %s\n",
   333 			name, old_version);
   334 	} else {
   335 		fprintf(stderr, "  result set should contain %s %s\n",
   336 			name, new_version);
   337 	}
   338 }
   339 
   340 static void
   341 end_result(struct test_context *ctx)
   342 {
   343 	ctx->in_result = 0;
   344 
   345 	if (ctx->result_set) {
   346 		if (!ctx->system_set)
   347 			ctx->system_set = razor_set_create();
   348 		razor_set_diff(ctx->system_set, ctx->result_set,
   349 			       diff_callback, ctx);
   350 	}
   351 }
   352 
   353 static void
   354 start_unsatisfiable(struct test_context *ctx, const char **atts)
   355 {
   356 	if (ctx->result_set) {
   357 		fprintf(stderr, "Expected to fail, but didn't\n");
   358 		exit(1);
   359 	}
   360 
   361 	ctx->unsat = 1;
   362 }
   363 
   364 static void
   365 end_unsatisfiable(struct test_context *ctx)
   366 {
   367 	ctx->unsat = 0;
   368 }
   369 
   370 static void
   371 start_test_element(void *data, const char *element, const char **atts)
   372 {
   373 	struct test_context *ctx = data;
   374 
   375 	if (strcmp(element, "tests") == 0) {
   376 		;
   377 	} else if (strcmp(element, "test") == 0) {
   378 		start_test(ctx, atts);
   379 	} else if (strcmp(element, "set") == 0) {
   380 		start_set(ctx, atts);
   381 	} else if (strcmp(element, "transaction") == 0) {
   382 		start_transaction(ctx, atts);
   383 	} else if (strcmp(element, "install") == 0) {
   384 		start_install_or_update(ctx, atts);
   385 	} else if (strcmp(element, "install") == 0) {
   386 		start_install_or_update(ctx, atts);
   387 	} else if (strcmp(element, "remove") == 0) {
   388 		start_remove(ctx, atts);
   389 	} else if (strcmp(element, "result") == 0) {
   390 		start_result(ctx, atts);
   391 	} else if (strcmp(element, "unsatisfiable") == 0) {
   392 		start_unsatisfiable(ctx, atts);
   393 	} else if (strcmp(element, "package") == 0) {
   394 		start_package(ctx, atts);
   395 	} else if (strcmp(element, "requires") == 0) {
   396 		start_property(ctx, RAZOR_PROPERTY_REQUIRES, atts);
   397 	} else if (strcmp(element, "provides") == 0) {
   398 		start_property(ctx, RAZOR_PROPERTY_PROVIDES, atts);
   399 	} else if (strcmp(element, "conflicts") == 0) {
   400 		start_property(ctx, RAZOR_PROPERTY_CONFLICTS, atts);
   401 	} else if (strcmp(element, "obsoletes") == 0) {
   402 		start_property(ctx, RAZOR_PROPERTY_OBSOLETES, atts);
   403 	} else {
   404 		fprintf(stderr, "Unrecognized element '%s'\n", element);
   405 		exit(1);
   406 	}
   407 }
   408 
   409 static void
   410 end_test_element (void *data, const char *element)
   411 {
   412 	struct test_context *ctx = data;
   413 
   414 	if (strcmp(element, "test") == 0) {
   415 		end_test(ctx);
   416 	} else if (strcmp(element, "set") == 0) {
   417 		end_set(ctx);
   418 	} else if (strcmp(element, "package") == 0) {
   419 		end_package(ctx);
   420 	} else if (strcmp(element, "transaction") == 0) {
   421 		end_transaction(ctx);
   422 	} else if (strcmp(element, "result") == 0) {
   423 		end_result(ctx);
   424 	} else if (strcmp(element, "unsatisfiable") == 0) {
   425 		end_unsatisfiable(ctx);
   426 	}
   427 }
   428 
   429 int main(int argc, char *argv[])
   430 {
   431 	struct test_context ctx;
   432 	const char *test_file;
   433 
   434 	memset(&ctx, 0, sizeof ctx);
   435 
   436 	if (argc > 3) {
   437 		fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]);
   438 		exit(-1);
   439 	}
   440 
   441 	if (argc >= 2 && !strcmp (argv[1], "-d")) {
   442 		ctx.debug = 1;
   443 		argc--;
   444 		argv++;
   445 	}
   446 	if (argc == 2)
   447 		test_file = argv[1];
   448 	else
   449 		test_file = "test.xml";
   450 
   451 	parse_xml_file(test_file, start_test_element, end_test_element, &ctx);
   452 
   453 	if (ctx.errors) {
   454 		fprintf(stderr, "\n%d errors\n", ctx.errors);
   455 		return 1;
   456 	} else
   457 		return 0;
   458 }