1 /* $Id: api-t.c 10308 2018-12-02 14:33:59Z iulius $ */
2 /* Test suite for overview API. */
3 
4 #include "config.h"
5 #include "clibrary.h"
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 
10 #include "inn/innconf.h"
11 #include "inn/hashtab.h"
12 #include "inn/messages.h"
13 #include "inn/overview.h"
14 #include "inn/vector.h"
15 #include "inn/libinn.h"
16 #include "tap/basic.h"
17 #include "inn/storage.h"
18 
19 /* Used as the artificial token for all articles inserted into overview. */
20 static const TOKEN faketoken = { 1, 1, "" };
21 
22 struct group {
23     char *group;
24     unsigned long count;
25     unsigned long low;
26     unsigned long high;
27 };
28 
29 /* Used for walking the hash table and verifying all the group data. */
30 struct verify {
31     struct overview *overview;
32     bool status;
33 };
34 
35 static const void *
group_key(const void * entry)36 group_key(const void *entry)
37 {
38     const struct group *group = (const struct group *) entry;
39     return group->group;
40 }
41 
42 static bool
group_eq(const void * key,const void * entry)43 group_eq(const void *key, const void *entry)
44 {
45     const char *first = key;
46     const char *second;
47 
48     second = ((const struct group *) entry)->group;
49     return !strcmp(first, second);
50 }
51 
52 static void
group_del(void * entry)53 group_del(void *entry)
54 {
55     struct group *group = (struct group *) entry;
56 
57     free(group->group);
58     free(group);
59 }
60 
61 /* Build a stripped-down innconf struct that contains only those settings that
62    the overview backend cares about.  (May still be missing additional bits
63    for ovdb.)
64    innconf->icdsynccount defines OVBUFF_SYNC_COUNT. */
65 static void
fake_innconf(void)66 fake_innconf(void)
67 {
68     if (innconf != NULL) {
69         free(innconf->ovmethod);
70         free(innconf->pathdb);
71         free(innconf->pathetc);
72         free(innconf->pathoverview);
73         free(innconf->pathrun);
74         free(innconf);
75     }
76     innconf = xmalloc(sizeof(*innconf));
77     memset(innconf, 0, sizeof(*innconf));
78     innconf->enableoverview = true;
79     innconf->groupbaseexpiry = true;
80     innconf->icdsynccount = 10;
81     innconf->keepmmappedthreshold = 1024;
82     innconf->nfsreader = false;
83     innconf->overcachesize = 20;
84     innconf->ovgrouppat = NULL;
85     innconf->pathdb = xstrdup("ov-tmp");
86     innconf->pathetc = xstrdup("etc");
87     innconf->pathoverview = xstrdup("ov-tmp");
88     innconf->pathrun = xstrdup("ov-tmp");
89 }
90 
91 /* Initialize an empty buffindexed buffer. */
92 static void
overview_init_buffindexed(void)93 overview_init_buffindexed(void)
94 {
95     int fd, i;
96     char zero[1024];
97 
98     fd = open("ov-tmp/buffer", O_CREAT | O_TRUNC | O_WRONLY, 0666);
99     if (fd < 0)
100         sysdie("Cannot create ov-tmp/buffer");
101     memset(zero, 0, sizeof(zero));
102     for (i = 0; i < 1024; i++)
103         if (write(fd, zero, sizeof(zero)) < (ssize_t) sizeof(zero))
104             sysdie("Cannot write to ov-tmp/buffer");
105     close(fd);
106 }
107 
108 /* Initialize the overview database. */
109 static struct overview *
overview_init(void)110 overview_init(void)
111 {
112     if (system("/bin/rm -rf ov-tmp") < 0)
113         sysdie("Cannot rm ov-tmp");
114     if (mkdir("ov-tmp", 0755))
115         sysdie("Cannot mkdir ov-tmp");
116     if (strcmp(innconf->ovmethod, "buffindexed") == 0)
117         overview_init_buffindexed();
118     return overview_open(OV_READ | OV_WRITE);
119 }
120 
121 /* Check to be sure that the line wasn't too long, and then parse the
122    beginning of the line from one of our data files, setting the article
123    number (via the passed pointer) and returning a pointer to the beginning of
124    the real overview data.  This function nul-terminates the group name and
125    leaves it at the beginning of the buffer.  (Ugly interface, but it's just a
126    test suite.) */
127 static char *
overview_data_parse(char * data,unsigned long * artnum)128 overview_data_parse(char *data, unsigned long *artnum)
129 {
130     char *start;
131     size_t length;
132 
133     length = strlen(data);
134     if (data[length - 1] != '\n')
135         die("Line too long in input data");
136     data[length - 1] = '\0';
137 
138     start = strchr(data, ':');
139     if (start == NULL)
140         die("No colon found in input data");
141     *start = '\0';
142     start++;
143     *artnum = strtoul(start, NULL, 10);
144     if (*artnum == 0)
145         die("Cannot parse article number in input data");
146     return start;
147 }
148 
149 /* Load an empty overview database from a file, in the process populating a
150    hash table with each group, the high water mark, and the count of messages
151    that should be in the group.  Returns the hash table on success and dies on
152    failure.  Takes the name of the data file to load and the overview
153    struct. */
154 static struct hash *
overview_load(const char * data,struct overview * overview)155 overview_load(const char *data, struct overview *overview)
156 {
157     struct hash *groups;
158     struct group *group;
159     FILE *overdata;
160     char buffer[4096];
161     char *start;
162     unsigned long artnum;
163     struct overview_group stats = { 0, 0, 0, NF_FLAG_OK };
164     struct overview_data article;
165 
166     /* Run through the overview data.  Each time we see a group, we update our
167        stored information about that group, which we'll use for verification
168        later.  We store that in a local hash table. */
169     groups = hash_create(32, hash_string, group_key, group_eq, group_del);
170     if (groups == NULL)
171         die("Cannot create a hash table");
172     overdata = fopen(data, "r");
173     if (overdata == NULL)
174         sysdie("Cannot open %s for reading", data);
175     while (fgets(buffer, sizeof(buffer), overdata) != NULL) {
176         start = overview_data_parse(buffer, &artnum);
177 
178         /* The overview API adds the article number, so strip that out before
179            storing the data (overview_data_parse leaves it in because we want
180            it in for data validation). */
181         start = strchr(start, '\t');
182         if (start == NULL)
183             die("No tab found after number in input data");
184         *start = '\0';
185         start++;
186 
187         /* See if we've already seen this group.  If not, create it in the
188            overview and the hash table; otherwise, update our local hash table
189            entry. */
190         group = hash_lookup(groups, buffer);
191         if (group == NULL) {
192             group = xmalloc(sizeof(struct group));
193             group->group = xstrdup(buffer);
194             group->count = 1;
195             group->low = artnum;
196             group->high = artnum;
197             if (!hash_insert(groups, group->group, group))
198                 die("Cannot insert %s into hash table", group->group);
199             if (!overview_group_add(overview, group->group, &stats))
200                 die("Cannot insert group %s into overview", group->group);
201         } else {
202             group->count++;
203             group->low = (artnum < group->low) ? artnum : group->low;
204             group->high = (artnum > group->high) ? artnum : group->high;
205         }
206 
207         /* Do the actual insert of the data.  Note that we set the arrival
208            time and expires time in a deterministic fashion so that we can
209            check later if that data is being stored properly. */
210         article.number = artnum;
211         article.overview = start;
212         article.overlen = strlen(start);
213         article.token = faketoken;
214         article.arrived = artnum * 10;
215         article.expires = (artnum % 5 == 0) ? artnum * 100 : artnum;
216         if (!overview_add(overview, group->group, &article))
217             die("Cannot insert %s:%lu into overview", group->group, artnum);
218     }
219     fclose(overdata);
220     return groups;
221 }
222 
223 /* Verify that all of the group data looks correct; this is low mark, high
224    mark, and article count.  Returns true if all the data is right, false
225    otherwise.  This function is meant to be called as a hash traversal
226    function, which means that it will be called for each element in our local
227    hash table of groups with the group struct as the first argument and a
228    pointer to a struct verify as the second argument. */
229 static void
overview_verify_groups(void * data,void * cookie)230 overview_verify_groups(void *data, void *cookie)
231 {
232     struct group *group = (struct group *) data;
233     struct verify *verify = cookie;
234     struct overview_group stats;
235 
236     if (!overview_group(verify->overview, group->group, &stats)) {
237         warn("Unable to get data for %s", group->group);
238         verify->status = false;
239         return;
240     }
241     if (stats.low != group->low) {
242         warn("Low article wrong for %s: %lu != %lu", group->group,
243              stats.low, group->low);
244         verify->status = false;
245     }
246     if (stats.high != group->high) {
247         warn("High article wrong for %s: %lu != %lu", group->group,
248              stats.high, group->high);
249         verify->status = false;
250     }
251     if (stats.count != group->count) {
252         warn("Article count wrong for %s: %lu != %lu", group->group,
253              stats.count, group->count);
254         verify->status = false;
255     }
256     if (stats.flag != NF_FLAG_OK) {
257         warn("Flag wrong for %s: %c != %c", group->group, stats.flag,
258              NF_FLAG_OK);
259         verify->status = false;
260     }
261 }
262 
263 /* Verify the components of the overview data for a particular entry. */
264 static bool
check_data(const char * group,ARTNUM artnum,const char * expected,struct overview_data * data)265 check_data(const char *group, ARTNUM artnum, const char *expected,
266            struct overview_data *data)
267 {
268     bool status = true;
269     time_t expires UNUSED; // See below why it is still unused.
270     char *saw;
271 
272     if (artnum != data->number) {
273         warn("Incorrect article number in search for %s:%lu: %lu != %lu",
274              group, artnum, data->number, artnum);
275         status = false;
276     }
277     if (strlen(expected) != data->overlen - 2) {
278         warn("Length wrong for %s:%lu: %lu != %lu", group, artnum,
279              (unsigned long) data->overlen, (unsigned long) strlen(expected));
280         status = false;
281     }
282     if (memcmp(&data->token, &faketoken, sizeof(faketoken)) != 0) {
283         warn("Token wrong for %s:%lu", group, artnum);
284         status = false;
285     }
286     if (memcmp(expected, data->overview, data->overlen - 2) != 0) {
287         warn("Data mismatch for %s:%lu", group, artnum);
288         saw = xstrndup(data->overview, data->overlen);
289         warn("====\n%s====\n%s====", expected, saw);
290         free(saw);
291         status = false;
292     }
293     if (memcmp("\r\n", data->overview + data->overlen - 2, 2) != 0) {
294         warn("Missing CRLF after data for %s:%lu", group, artnum);
295         status = false;
296     }
297     if (data->arrived != (time_t) artnum * 10) {
298         warn("Arrival time wrong for %s:%lu: %lu != %lu", group, artnum,
299              (unsigned long) data->arrived, artnum * 10);
300         status = false;
301     }
302 
303     /* expires is always 0 for right now because the underlying API doesn't
304        return it; this will change when the new API has been pushed all the
305        way down to the overview implementations.  */
306     expires = (artnum % 5 == 0) ? artnum * 100 : artnum;
307     if (data->expires != 0) {
308         warn("Expires time wrong for %s:%lu: %lu != %lu", group, artnum,
309              (unsigned long) data->expires, 0UL);
310         status = false;
311     }
312     return status;
313 }
314 
315 /* Read through the data again, looking up each article as we go and verifying
316    that the data stored in overview is the same as the data we put there.  Do
317    this two ways each time, once via overview_token and once via
318    overview_search.  Return true if everything checks out, false otherwise.
319    Takes the path to the data file and the overview struct. */
320 static bool
overview_verify_data(const char * data,struct overview * overview)321 overview_verify_data(const char *data, struct overview *overview)
322 {
323     FILE *overdata;
324     char buffer[4096];
325     char *start;
326     unsigned long artnum;
327     TOKEN token;
328     void *search;
329     struct overview_data article;
330     bool status = true;
331 
332     overdata = fopen(data, "r");
333     if (overdata == NULL)
334         sysdie("Cannot open %s for reading", data);
335     while (fgets(buffer, sizeof(buffer), overdata) != NULL) {
336         start = overview_data_parse(buffer, &artnum);
337 
338         /* Now check that the overview data is correct for that group. */
339         if (!overview_token(overview, buffer, artnum, &token)) {
340             warn("No overview data found for %s:%lu", buffer, artnum);
341             status = false;
342             continue;
343         }
344         if (memcmp(&token, &faketoken, sizeof(token)) != 0) {
345             warn("Token wrong for %s:%lu", buffer, artnum);
346             status = false;
347         }
348 
349         /* Do the same thing, except use search. */
350         search = overview_search_open(overview, buffer, artnum, artnum);
351         if (search == NULL) {
352             warn("Unable to open search for %s:%lu", buffer, artnum);
353             status = false;
354             continue;
355         }
356         if (!overview_search(overview, search, &article)) {
357             warn("No overview data found for %s:%lu", buffer, artnum);
358             status = false;
359             continue;
360         }
361         if (!check_data(buffer, artnum, start, &article))
362             status = false;
363         if (overview_search(overview, search, &article)) {
364             warn("Unexpected article found for %s:%lu", buffer, artnum);
365             status = false;
366         }
367         overview_search_close(overview, search);
368     }
369     fclose(overdata);
370     return status;
371 }
372 
373 /* Try an overview search and verify that all of the data is returned in the
374    right order.  The first group mentioned in the provided data file will be
375    the group the search is done in, and the search will cover all articles
376    from the second article to the second-to-the-last article in the group.
377    Returns true if everything checks out, false otherwise. */
378 static bool
overview_verify_search(const char * data,struct overview * overview)379 overview_verify_search(const char *data, struct overview *overview)
380 {
381     unsigned long artnum, i;
382     unsigned long start = 0;
383     unsigned long end = 0;
384     unsigned long last = 0;
385     struct vector *expected;
386     char *group, *line;
387     FILE *overdata;
388     char buffer[4096];
389     void *search;
390     struct overview_data article;
391     bool status = true;
392 
393     overdata = fopen(data, "r");
394     if (overdata == NULL)
395         sysdie("Cannot open %s for reading", data);
396     expected = vector_new();
397     if (fgets(buffer, sizeof(buffer), overdata) == NULL) {
398         fclose(overdata);
399         die("Unexpected end of file in %s", data);
400     }
401     overview_data_parse(buffer, &artnum);
402     group = xstrdup(buffer);
403     while (fgets(buffer, sizeof(buffer), overdata) != NULL) {
404         line = overview_data_parse(buffer, &artnum);
405         if (strcmp(group, buffer) != 0)
406             continue;
407         vector_add(expected, line);
408         if (start == 0)
409             start = artnum;
410         end = last;
411         last = artnum;
412     }
413     search = overview_search_open(overview, group, start, end);
414     if (search == NULL) {
415         warn("Unable to open search for %s:%lu", buffer, start);
416         free(group);
417         vector_free(expected);
418         fclose(overdata);
419         return false;
420     }
421     i = 0;
422     while (overview_search(overview, search, &article)) {
423         if (!check_data(group, article.number, expected->strings[i], &article))
424             status = false;
425         i++;
426     }
427     overview_search_close(overview, search);
428     if (article.number != end) {
429         warn("End of search in %s wrong: %lu != %lu", group, article.number,
430              end);
431         status = false;
432     }
433     if (i != expected->count - 1) {
434         warn("Didn't see all expected entries in %s", group);
435         status = false;
436     }
437     free(group);
438     vector_free(expected);
439     fclose(overdata);
440     return status;
441 }
442 
443 /* Try an overview search and verify that all of the data is returned in the
444    right order.  The search will cover everything from article 1 to the
445    highest numbered article plus one.  There were some problems with a search
446    low water mark lower than the base of the group.  Returns true if
447    everything checks out, false otherwise. */
448 static bool
overview_verify_full_search(const char * data,struct overview * overview)449 overview_verify_full_search(const char *data, struct overview *overview)
450 {
451     unsigned long artnum, i;
452     unsigned long end = 0;
453     struct vector *expected;
454     char *line;
455     char *group = NULL;
456     FILE *overdata;
457     char buffer[4096];
458     void *search;
459     struct overview_data article;
460     bool status = true;
461 
462     overdata = fopen(data, "r");
463     if (overdata == NULL)
464         sysdie("Cannot open %s for reading", data);
465     expected = vector_new();
466     while (fgets(buffer, sizeof(buffer), overdata) != NULL) {
467         line = overview_data_parse(buffer, &artnum);
468         if (group == NULL)
469             group = xstrdup(buffer);
470         vector_add(expected, line);
471         end = artnum;
472     }
473     search = overview_search_open(overview, group, 1, end + 1);
474     if (search == NULL) {
475         warn("Unable to open full search for %s", group);
476         free(group);
477         vector_free(expected);
478         fclose(overdata);
479         return false;
480     }
481     i = 0;
482     while (overview_search(overview, search, &article)) {
483         if (!check_data(group, article.number, expected->strings[i], &article))
484             status = false;
485         i++;
486     }
487     overview_search_close(overview, search);
488     if (article.number != end) {
489         warn("End of search in %s wrong: %lu != %lu", group, article.number,
490              end);
491         status = false;
492     }
493     if (i != expected->count) {
494         warn("Didn't see all expected entries in %s", group);
495         status = false;
496     }
497     free(group);
498     vector_free(expected);
499     fclose(overdata);
500     return status;
501 }
502 
503 /* Run the tests on a particular overview setup.  Expects to be called
504    multiple times with different inn.conf configurations to test the various
505    iterations of overview support.  Takes the current test number and returns
506    the next test number. */
507 static int
overview_tests(int n)508 overview_tests(int n)
509 {
510     struct hash *groups;
511     struct overview *overview;
512     struct verify verify;
513     void *search;
514     struct overview_data data;
515     TOKEN token;
516 
517     overview = overview_init();
518     if (overview == NULL)
519         die("Opening the overview database failed, cannot continue");
520     ok(n++, true);
521 
522     groups = overview_load("overview/basic", overview);
523     ok(n++, true);
524     verify.status = true;
525     verify.overview = overview;
526     hash_traverse(groups, overview_verify_groups, &verify);
527     ok(n++, verify.status);
528     ok(n++, overview_verify_data("overview/basic", overview));
529     ok(n++, overview_verify_search("overview/basic", overview));
530     hash_free(groups);
531     overview_close(overview);
532     ok(n++, true);
533 
534     overview = overview_init();
535     if (overview == NULL)
536         die("Opening the overview database failed, cannot continue");
537     ok(n++, true);
538 
539     groups = overview_load("overview/reversed", overview);
540     ok(n++, true);
541     verify.status = true;
542     verify.overview = overview;
543     hash_traverse(groups, overview_verify_groups, &verify);
544     ok(n++, verify.status);
545     ok(n++, overview_verify_data("overview/basic", overview));
546     ok(n++, overview_verify_search("overview/basic", overview));
547     hash_free(groups);
548     overview_close(overview);
549     ok(n++, true);
550 
551     overview = overview_init();
552     if (overview == NULL)
553         die("Opening the overview database failed, cannot continue");
554     ok(n++, true);
555 
556     groups = overview_load("overview/high-numbered", overview);
557     ok(n++, true);
558     ok(n++, overview_verify_data("overview/high-numbered", overview));
559     ok(n++, overview_verify_full_search("overview/high-numbered", overview));
560     if (strcmp(innconf->ovmethod, "buffindexed") == 0) {
561         skip_block(n, 6, "buffindexed doesn't support cancel");
562         n += 6;
563     } else {
564         ok(n++, overview_cancel(overview, "example.test", 7498));
565         ok(n++, !overview_token(overview, "example.test", 7498, &token));
566         search = overview_search_open(overview, "example.test", 7498, 7499);
567         ok(n++, search != NULL);
568         ok(n++, overview_search(overview, search, &data));
569         ok_int(n++, 7499, data.number);
570         ok(n++, !overview_search(overview, search, &data));
571         overview_search_close(overview, search);
572     }
573     hash_free(groups);
574     overview_close(overview);
575     ok(n++, true);
576 
577     overview = overview_init();
578     if (overview == NULL)
579         die("Opening the overview database failed, cannot continue");
580     ok(n++, true);
581 
582     groups = overview_load("overview/bogus", overview);
583     ok(n++, true);
584     ok(n++, overview_verify_data("overview/bogus", overview));
585     hash_free(groups);
586     overview_close(overview);
587     if (system("/bin/rm -rf ov-tmp") < 0)
588         sysdie("Cannot rm ov-tmp");
589     ok(n++, true);
590     return n;
591 }
592 
593 /* Run the tests on a particular overview setup.  This is a copy of
594    overview_tests that closes and reopens the overview without mmap to test
595    tradindexedmmap.  Takes the current test and returns the next test
596    number. */
597 static int
overview_mmap_tests(int n)598 overview_mmap_tests(int n)
599 {
600     struct hash *groups;
601     struct overview *overview;
602     struct verify verify;
603 
604     overview = overview_init();
605     if (overview == NULL)
606         die("Opening the overview database failed, cannot continue");
607     ok(n++, true);
608 
609     groups = overview_load("overview/basic", overview);
610     ok(n++, true);
611     overview_close(overview);
612     innconf->tradindexedmmap = false;
613     overview = overview_open(OV_READ);
614     if (overview == NULL)
615         die("Opening the overview database failed, cannot continue");
616     verify.status = true;
617     verify.overview = overview;
618     hash_traverse(groups, overview_verify_groups, &verify);
619     ok(n++, verify.status);
620     ok(n++, overview_verify_data("overview/basic", overview));
621     ok(n++, overview_verify_search("overview/basic", overview));
622     hash_free(groups);
623     overview_close(overview);
624     ok(n++, true);
625 
626     innconf->tradindexedmmap = true;
627     overview = overview_init();
628     if (overview == NULL)
629         die("Opening the overview database failed, cannot continue");
630     ok(n++, true);
631 
632     groups = overview_load("overview/reversed", overview);
633     ok(n++, true);
634     overview_close(overview);
635     innconf->tradindexedmmap = false;
636     overview = overview_open(OV_READ);
637     if (overview == NULL)
638         die("Opening the overview database failed, cannot continue");
639     verify.status = true;
640     verify.overview = overview;
641     hash_traverse(groups, overview_verify_groups, &verify);
642     ok(n++, verify.status);
643     ok(n++, overview_verify_data("overview/basic", overview));
644     ok(n++, overview_verify_search("overview/basic", overview));
645     hash_free(groups);
646     overview_close(overview);
647     ok(n++, true);
648 
649     innconf->tradindexedmmap = true;
650     overview = overview_init();
651     if (overview == NULL)
652         die("Opening the overview database failed, cannot continue");
653     ok(n++, true);
654 
655     groups = overview_load("overview/high-numbered", overview);
656     ok(n++, true);
657     overview_close(overview);
658     innconf->tradindexedmmap = false;
659     overview = overview_open(OV_READ);
660     if (overview == NULL)
661         die("Opening the overview database failed, cannot continue");
662     ok(n++, overview_verify_data("overview/high-numbered", overview));
663     ok(n++, overview_verify_full_search("overview/high-numbered", overview));
664     hash_free(groups);
665     overview_close(overview);
666     ok(n++, true);
667 
668     innconf->tradindexedmmap = true;
669     overview = overview_init();
670     if (overview == NULL)
671         die("Opening the overview database failed, cannot continue");
672     ok(n++, true);
673 
674     groups = overview_load("overview/bogus", overview);
675     ok(n++, true);
676     overview_close(overview);
677     innconf->tradindexedmmap = false;
678     overview = overview_open(OV_READ);
679     if (overview == NULL)
680         die("Opening the overview database failed, cannot continue");
681     ok(n++, overview_verify_data("overview/bogus", overview));
682     hash_free(groups);
683     overview_close(overview);
684     if (system("/bin/rm -rf ov-tmp") < 0)
685         sysdie("Cannot rm ov-tmp");
686     ok(n++, true);
687     return n;
688 }
689 
690 int
main(void)691 main(void)
692 {
693     int n = 1;
694 
695     if (access("../data/overview/basic", F_OK) == 0) {
696         if (chdir("../data") < 0) {
697             sysbail("cannot chdir to ../data");
698         }
699     } else if (access("data/overview/basic", F_OK) == 0) {
700         if (chdir("data") < 0) {
701             sysbail("cannot chdir to data");
702         }
703     } else if (access("tests/data/overview/basic", F_OK) == 0) {
704         if (chdir("tests/data") < 0) {
705             sysbail("cannot chdir to tests/data");
706         }
707     }
708 
709     /* Cancels can't be tested with mmap, so there are only 21 tests there. */
710     test_init(27 * 2 + 21);
711 
712     fake_innconf();
713     innconf->ovmethod = xstrdup("tradindexed");
714     innconf->tradindexedmmap = true;
715     diag("tradindexed with mmap");
716     n = overview_tests(1);
717 
718     diag("tradindexed without mmap");
719     n = overview_mmap_tests(n);
720 
721     free(innconf->ovmethod);
722     innconf->ovmethod = xstrdup("buffindexed");
723     diag("buffindexed");
724     overview_tests(n);
725 
726     return 0;
727 }
728