1 /* $Id: tradindexed-t.c 6388 2003-07-12 19:07:56Z rra $ */
2 /* tradindexed test suite. */
9 #include "inn/innconf.h"
10 #include "inn/hashtab.h"
11 #include "inn/messages.h"
12 #include "inn/vector.h"
18 #include "../storage/tradindexed/tradindexed.h"
20 /* Used as the artificial token for all articles inserted into overview. */
21 static const TOKEN faketoken = { 1, 1, "" };
31 group_key(const void *entry)
33 const struct group *group = (const struct group *) entry;
38 group_eq(const void *key, const void *entry)
40 const char *first = key;
43 second = ((const struct group *) entry)->group;
44 return !strcmp(first, second);
48 group_del(void *entry)
50 struct group *group = (struct group *) entry;
56 /* Build a stripped-down innconf struct that contains only those settings that
57 the tradindexed overview method cares about. */
61 innconf = xmalloc(sizeof(*innconf));
62 innconf->pathoverview = xstrdup("tdx-tmp");
63 innconf->overcachesize = 20;
64 innconf->groupbaseexpiry = true;
65 innconf->tradindexedmmap = true;
68 /* Initialize the overview database. */
73 if (access("data/basic", F_OK) < 0)
74 if (access("overview/data/basic", F_OK) == 0)
75 if (chdir("overview") != 0)
76 sysdie("Cannot cd to overview");
77 if (mkdir("tdx-tmp", 0755) != 0 && errno != EEXIST)
78 sysdie("Cannot mkdir tdx-tmp");
79 return tradindexed_open(OV_READ | OV_WRITE);
82 /* Check to be sure that the line wasn't too long, and then parse the
83 beginning of the line from one of our data files, setting the article
84 number (via the passed pointer) and returning a pointer to the beginning of
85 the real overview data. This function nul-terminates the group name and
86 leaves it at the beginning of the buffer. (Ugly interface, but it's just a
89 overview_data_parse(char *data, unsigned long *artnum)
93 if (data[strlen(data) - 1] != '\n')
94 die("Line too long in input data");
96 start = strchr(data, ':');
98 die("No colon found in input data");
101 *artnum = strtoul(start, NULL, 10);
103 die("Cannot parse article number in input data");
107 /* Load an empty overview database from a file, in the process populating a
108 hash table with each group, the high water mark, and the count of messages
109 that should be in the group. Returns the hash table on success and dies on
110 failure. Takes the name of the data file to load. */
112 overview_load(const char *data)
120 unsigned long artnum;
122 /* Run through the overview data. Each time we see a group, we update our
123 stored information about that group, which we'll use for verification
124 later. We store that in a local hash table. */
125 groups = hash_create(32, hash_string, group_key, group_eq, group_del);
127 die("Cannot create a hash table");
128 overview = fopen(data, "r");
129 if (overview == NULL)
130 sysdie("Cannot open %s for reading", data);
131 while (fgets(buffer, sizeof(buffer), overview) != NULL) {
132 start = overview_data_parse(buffer, &artnum);
134 /* See if we've already seen this group. If not, create it in the
135 overview and the hash table; otherwise, update our local hash table
137 group = hash_lookup(groups, buffer);
139 group = xmalloc(sizeof(struct group));
140 group->group = xstrdup(buffer);
143 group->high = artnum;
144 if (!hash_insert(groups, group->group, group))
145 die("Cannot insert %s into hash table", group->group);
146 if (!tradindexed_groupadd(group->group, 0, 0, flag))
147 die("Cannot insert group %s into overview", group->group);
150 group->low = (artnum < group->low) ? artnum : group->low;
151 group->high = (artnum > group->high) ? artnum : group->high;
154 /* Do the actual insert of the data. Note that we set the arrival
155 time and expires time in a deterministic fashion so that we can
156 check later if that data is being stored properly. */
157 if (!tradindexed_add(group->group, artnum, faketoken, start,
158 strlen(start), artnum * 10,
159 (artnum % 5 == 0) ? artnum * 100 : artnum))
160 die("Cannot insert %s:%lu into overview", group->group, artnum);
166 /* Verify that all of the group data looks correct; this is low mark, high
167 mark, and article count. Returns true if all the data is right, false
168 otherwise. This function is meant to be called as a hash traversal
169 function, which means that it will be called for each element in our local
170 hash table of groups with the group struct as the first argument and a
171 pointer to a status as the second argument. */
173 overview_verify_groups(void *data, void *cookie)
175 struct group *group = (struct group *) data;
176 bool *status = (bool *) cookie;
177 int low, high, count, flag;
179 if (!tradindexed_groupstats(group->group, &low, &high, &count, &flag)) {
180 warn("Unable to get data for %s", group->group);
184 if ((unsigned long) low != group->low) {
185 warn("Low article wrong for %s: %lu != %lu", group->group,
186 (unsigned long) low, group->low);
189 if ((unsigned long) high != group->high) {
190 warn("High article wrong for %s: %lu != %lu", group->group,
191 (unsigned long) high, group->high);
194 if ((unsigned long) count != group->count) {
195 warn("Article count wrong for %s: %lu != %lu", group->group,
196 (unsigned long) count, group->count);
200 warn("Flag wrong for %s: %c != y", group->group, (char) flag);
205 /* Verify the components of the overview data for a particular entry. */
207 check_data(const char *group, unsigned long artnum, const char *expected,
208 const char *seen, int length, TOKEN token)
212 if (strlen(expected) != (size_t) length) {
213 warn("Length wrong for %s:%lu: %d != %lu", group, artnum, length,
214 (unsigned long) strlen(expected));
217 if (memcmp(&token, &faketoken, sizeof(token)) != 0) {
218 warn("Token wrong for %s:%lu", group, artnum);
221 if (memcmp(expected, seen, length) != 0) {
222 warn("Data mismatch for %s:%lu", group, artnum);
223 warn("====\n%s\n====\n%s\n====", expected, seen);
229 /* Read through the data again, looking up each article as we go and verifying
230 that the data stored in overview is the same as the data we put there. Do
231 this two ways each time, once via getartinfo and once via opensearch.
232 Return true if everything checks out, false otherwise. Takes the path to
235 overview_verify_data(const char *data)
240 unsigned long artnum, overnum;
248 overdata = fopen(data, "r");
249 if (overdata == NULL)
250 sysdie("Cannot open %s for reading", data);
251 while (fgets(buffer, sizeof(buffer), overdata) != NULL) {
252 start = overview_data_parse(buffer, &artnum);
254 /* Now check that the overview data is correct for that group. */
255 if (!tradindexed_getartinfo(buffer, artnum, &token)) {
256 warn("No overview data found for %s:%lu", buffer, artnum);
260 if (memcmp(&token, &faketoken, sizeof(token)) != 0) {
261 warn("Token wrong for %s:%lu", buffer, artnum);
265 /* Do the same thing, except use search. */
266 search = tradindexed_opensearch(buffer, artnum, artnum);
267 if (search == NULL) {
268 warn("Unable to open search for %s:%lu", buffer, artnum);
272 if (!tradindexed_search(search, &overnum, &overview, &length, &token,
274 warn("No overview data found for %s:%lu", buffer, artnum);
278 if (overnum != artnum) {
279 warn("Incorrect article number in search for %s:%lu: %lu != %lu",
280 buffer, artnum, overnum, artnum);
283 if (!check_data(buffer, artnum, start, overview, length, token))
285 if ((unsigned long) arrived != artnum * 10) {
286 warn("Arrival time wrong for %s:%lu: %lu != %lu", buffer, artnum,
287 (unsigned long) arrived, artnum * 10);
290 if (tradindexed_search(search, &overnum, &overview, &length, &token,
292 warn("Unexpected article found for %s:%lu", buffer, artnum);
295 tradindexed_closesearch(search);
301 /* Try an overview search and verify that all of the data is returned in the
302 right order. The first group mentioned in the provided data file will be
303 the group the search is done in, and the search will cover all articles
304 from the second article to the second-to-the-last article in the group.
305 Returns true if everything checks out, false otherwise. */
307 overview_verify_search(const char *data)
309 unsigned long artnum, overnum, i;
310 unsigned long start = 0;
311 unsigned long end = 0;
312 unsigned long last = 0;
313 struct vector *expected;
323 overview = fopen(data, "r");
324 if (overview == NULL)
325 sysdie("Cannot open %s for reading", data);
326 expected = vector_new();
327 if (fgets(buffer, sizeof(buffer), overview) == NULL)
328 die("Unexpected end of file in %s", data);
329 overview_data_parse(buffer, &artnum);
330 group = xstrdup(buffer);
331 while (fgets(buffer, sizeof(buffer), overview) != NULL) {
332 line = overview_data_parse(buffer, &artnum);
333 if (strcmp(group, buffer) != 0)
335 vector_add(expected, line);
341 search = tradindexed_opensearch(group, start, end);
342 if (search == NULL) {
343 warn("Unable to open search for %s:%lu", buffer, start);
345 vector_free(expected);
349 while (tradindexed_search(search, &overnum, &line, &length, &token,
351 if (!check_data(group, overnum, expected->strings[i], line, length,
354 if ((unsigned long) arrived != overnum * 10) {
355 warn("Arrival time wrong for %s:%lu: %lu != %lu", group, overnum,
356 (unsigned long) arrived, overnum * 10);
361 tradindexed_closesearch(search);
362 if (overnum != end) {
363 warn("End of search in %s wrong: %lu != %lu", group, overnum, end);
366 if (i != expected->count - 1) {
367 warn("Didn't see all expected entries in %s", group);
371 vector_free(expected);
375 /* Try an overview search and verify that all of the data is returned in the
376 right order. The search will cover everything from article 1 to the
377 highest numbered article plus one. There were some problems with a search
378 low-water mark lower than the base of the group. Returns true if
379 everything checks out, false otherwise. */
381 overview_verify_full_search(const char *data)
383 unsigned long artnum, overnum, i;
384 unsigned long end = 0;
385 struct vector *expected;
396 overview = fopen(data, "r");
397 if (overview == NULL)
398 sysdie("Cannot open %s for reading", data);
399 expected = vector_new();
400 while (fgets(buffer, sizeof(buffer), overview) != NULL) {
401 line = overview_data_parse(buffer, &artnum);
403 group = xstrdup(buffer);
404 vector_add(expected, line);
407 search = tradindexed_opensearch(group, 1, end + 1);
408 if (search == NULL) {
409 warn("Unable to open full search for %s", group);
411 vector_free(expected);
415 while (tradindexed_search(search, &overnum, &line, &length, &token,
417 if (!check_data(group, overnum, expected->strings[i], line, length,
420 if ((unsigned long) arrived != overnum * 10) {
421 warn("Arrival time wrong for %s:%lu: %lu != %lu", group, overnum,
422 (unsigned long) arrived, overnum * 10);
427 tradindexed_closesearch(search);
428 if (overnum != end) {
429 warn("End of search in %s wrong: %lu != %lu", group, overnum, end);
432 if (i != expected->count) {
433 warn("Didn't see all expected entries in %s", group);
437 vector_free(expected);
449 if (!overview_init())
450 die("Opening the overview database failed, cannot continue");
453 groups = overview_load("data/basic");
456 hash_traverse(groups, overview_verify_groups, &status);
458 ok(4, overview_verify_data("data/basic"));
459 ok(5, overview_verify_search("data/basic"));
462 system("/bin/rm -r tdx-tmp");
465 if (!overview_init())
466 die("Opening the overview database failed, cannot continue");
469 groups = overview_load("data/reversed");
472 hash_traverse(groups, overview_verify_groups, &status);
474 ok(10, overview_verify_data("data/basic"));
475 ok(11, overview_verify_search("data/basic"));
478 system("/bin/rm -r tdx-tmp");
481 if (!overview_init())
482 die("Opening the overview database failed, cannot continue");
485 groups = overview_load("data/high-numbered");
487 ok(15, overview_verify_data("data/high-numbered"));
488 ok(16, overview_verify_full_search("data/high-numbered"));
491 system("/bin/rm -r tdx-tmp");
494 if (!overview_init())
495 die("Opening the overview database failed, cannot continue");
498 groups = overview_load("data/bogus");
500 ok(20, overview_verify_data("data/bogus"));
503 system("/bin/rm -r tdx-tmp");