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