xref: /4.0.0/couchstore/tests/testapp.c (revision 81a8545e)
1/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2#include "config.h"
3#include <libcouchstore/couch_db.h>
4#include "../src/fatbuf.h"
5#include "../src/internal.h"
6#include "../src/node_types.h"
7#include "../src/reduces.h"
8#include <errno.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include "macros.h"
13#include "file_tests.h"
14
15extern void mapreduce_tests();
16extern void view_tests();
17extern void purge_tests();
18
19#define ZERO(V) memset(&(V), 0, sizeof(V))
20/* Only use the macro SETDOC with constants!
21   it uses sizeof() */
22#define SETDOC(N, I, D, M)  \
23   setdoc(&testdocset.docs[N], &testdocset.infos[N], I, strlen(I), \
24         D, sizeof(D) - 1, M, strlen(M)); testdocset.datasize += sizeof(D) - 1;
25
26typedef struct _counterset {
27    int totaldocs;
28    int deleted;
29} counterset;
30
31typedef struct _docset {
32    Doc *docs;
33    DocInfo *infos;
34    int size;
35    int pos;
36    uint64_t datasize;
37    counterset counters;
38} docset;
39
40counterset counters;
41docset testdocset;
42fatbuf *docsetbuf = NULL;
43char testfilepath[1024] = "testfile.couch";
44
45static void print_os_err(Db *db) {
46    char emsg[512];
47    couchstore_error_t ret;
48    ret = couchstore_last_os_error(db, emsg, 512);
49
50    if (ret == COUCHSTORE_SUCCESS) {
51        fprintf(stderr, "OS Error: %s\n", emsg);
52    } else {
53        fprintf(stderr, "error: %s\n", couchstore_strerror(ret));
54    }
55}
56
57static void test_raw_08(uint8_t value)
58{
59    raw_08 raw;
60    raw = encode_raw08(value);
61    cb_assert(decode_raw08(raw) == value);
62}
63
64static void test_raw_16(uint16_t value)
65{
66    raw_16 raw;
67    raw = encode_raw16(value);
68    cb_assert(decode_raw16(raw) == value);
69}
70
71static void test_raw_32(uint32_t value)
72{
73    raw_32 raw;
74    raw = encode_raw32(value);
75    cb_assert(decode_raw32(raw) == value);
76}
77
78static void test_raw_40(uint64_t value, const uint8_t expected[8])
79{
80    union {
81        raw_40 raw;
82        uint8_t bytes[8];
83    } data;
84    memset(&data, 0, sizeof(data));
85    encode_raw40(value, &data.raw);
86    cb_assert(memcmp(data.bytes, expected, 8) == 0);
87    cb_assert(decode_raw40(data.raw) == value);
88}
89
90static void test_raw_48(uint64_t value, const uint8_t expected[8])
91{
92    union {
93        raw_48 raw;
94        uint8_t bytes[8];
95    } data;
96    memset(&data, 0, sizeof(data));
97    encode_raw48(value, &data.raw);
98    cb_assert(memcmp(data.bytes, expected, 8) == 0);
99    cb_assert(decode_raw48(data.raw) == value);
100}
101
102static void test_bitfield_fns(void)
103{
104    uint8_t expected1[8] = {0x12, 0x34, 0x56, 0x78, 0x90};
105    uint8_t expected2[8] = {0x09, 0x87, 0x65, 0x43, 0x21};
106    uint8_t expected3[8] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB};
107    uint8_t expected4[8] = {0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
108    struct {
109        raw_08 a;
110        raw_48 b;
111        raw_16 c;
112        raw_40 d;
113        raw_32 e;
114        raw_08 f;
115    } packed;
116    raw_kv_length kv;
117    uint32_t klen, vlen;
118
119    cb_assert(sizeof(cs_off_t) == 8);
120
121    cb_assert(sizeof(raw_08) == 1);
122    cb_assert(sizeof(raw_16) == 2);
123    cb_assert(sizeof(raw_32) == 4);
124    cb_assert(sizeof(raw_40) == 5);
125    cb_assert(sizeof(raw_48) == 6);
126
127    cb_assert(sizeof(packed) == 19);
128
129    cb_assert(sizeof(kv) == 5);
130    kv = encode_kv_length(1234, 123456);
131    decode_kv_length(&kv, &klen, &vlen);
132    cb_assert(klen == 1234);
133    cb_assert(vlen == 123456);
134
135    test_raw_08(0);
136    test_raw_08(UINT8_MAX);
137    test_raw_16(0);
138    test_raw_16(12345);
139    test_raw_16(UINT16_MAX);
140    test_raw_32(0);
141    test_raw_32(12345678);
142    test_raw_32(UINT32_MAX);
143
144    test_raw_40(INT64_C(0x1234567890), expected1);
145    test_raw_40(INT64_C(0x0987654321), expected2);
146    test_raw_48(INT64_C(0x1234567890AB), expected3);
147    test_raw_48(INT64_C(0xBA9876543210), expected4);
148}
149
150static void setdoc(Doc *doc, DocInfo *info, char *id, size_t idlen,
151                   char *data, size_t datalen, char *meta, size_t metalen)
152{
153    memset(doc, 0, sizeof(Doc));
154    memset(info, 0, sizeof(DocInfo));
155    doc->id.buf = id;
156    doc->id.size = idlen;
157    doc->data.buf = data;
158    doc->data.size = datalen;
159    info->rev_meta.buf = meta;
160    info->rev_meta.size = metalen;
161    info->id = doc->id;
162}
163
164static void docset_init(int numdocs)
165{
166    testdocset.size = numdocs;
167    testdocset.pos = 0;
168    testdocset.datasize = 0;
169    if (docsetbuf) {
170        fatbuf_free(docsetbuf);
171        docsetbuf = NULL;
172    }
173
174    docsetbuf = fatbuf_alloc(numdocs * (sizeof(Doc) + sizeof(DocInfo)));
175    testdocset.docs = fatbuf_get(docsetbuf, numdocs * sizeof(Doc));
176    testdocset.infos = fatbuf_get(docsetbuf, numdocs * sizeof(DocInfo));
177    ZERO(testdocset.counters);
178}
179
180static int counter_inc(Db *db, DocInfo *info, void *ctx)
181{
182    counterset *ctr = ctx;
183    (void)db;
184    ctr->totaldocs++;
185    if (info->deleted) {
186        ctr->deleted++;
187    }
188    return 0;
189}
190
191#define EQUAL_DOC_BUF(f) cb_assert(memcmp(doc-> f .buf, testdocset.docs[testdocset.pos]. f .buf, doc-> f .size) == 0)
192#define EQUAL_INFO_BUF(f) cb_assert(memcmp(info-> f  .buf, testdocset.infos[testdocset.pos]. f .buf, info-> f .size) == 0)
193static int docset_check(Db *db, DocInfo *info, void *ctx)
194{
195    int errcode = 0;
196    docset *ds = ctx;
197    counterset *ctr = &ds->counters;
198    Doc *doc;
199
200    ctr->totaldocs++;
201    if (info->deleted) {
202        ctr->deleted++;
203    }
204    EQUAL_INFO_BUF(id);
205    EQUAL_INFO_BUF(rev_meta);
206    try(couchstore_open_doc_with_docinfo(db, info, &doc, DECOMPRESS_DOC_BODIES));
207    if (testdocset.docs[testdocset.pos].data.size > 0) {
208        cb_assert(doc);
209        EQUAL_DOC_BUF(data);
210        EQUAL_DOC_BUF(id);
211    }
212    testdocset.pos++;
213    couchstore_free_document(doc);
214cleanup:
215    cb_assert(errcode == 0);
216    return 0;
217}
218
219static int dociter_check(Db *db, DocInfo *info, void *ctx)
220{
221    int errcode = 0;
222    docset *ds = ctx;
223    counterset *ctr = &ds->counters;
224    Doc *doc;
225
226    ctr->totaldocs++;
227    if (info->deleted) {
228        ctr->deleted++;
229    }
230    try(couchstore_open_doc_with_docinfo(db, info, &doc, DECOMPRESS_DOC_BODIES));
231    cb_assert(doc);
232    couchstore_free_document(doc);
233cleanup:
234    cb_assert(errcode == 0);
235    return 0;
236}
237
238static int dump_count(Db *db)
239{
240    int errcode = 0;
241    ZERO(counters);
242    try(couchstore_changes_since(db, 0, 0, counter_inc, &counters));
243cleanup:
244    cb_assert(errcode == 0);
245    return errcode;
246}
247
248static char zerometa[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3};
249
250static void test_save_docs(int count, const char *doc_tpl)
251{
252    int errcode = 0;
253    int i;
254    char *idBuf, *valueBuf;
255    Doc **docptrs;
256    DocInfo **nfoptrs;
257    sized_buf *ids = NULL;
258    uint64_t idtreesize = 0;
259    uint64_t seqtreesize = 0;
260    uint64_t docssize = 0;
261    uint64_t dbfilesize = 0;
262    uint64_t *sequences = NULL;
263    const raw_by_id_reduce *reduce;
264    Db *db;
265
266    fprintf(stderr, "save_docs (doc count %d)... ", count);
267    fflush(stderr);
268
269    docset_init(count);
270    srand(0xdeadbeef); /* doc IDs should be consistent across runs */
271    for (i = 0; i < count; ++i) {
272        int idsize;
273        int valsize;
274
275        idBuf = (char *) malloc(sizeof(char) * 32);
276        cb_assert(idBuf != NULL);
277        idsize = sprintf(idBuf, "doc%d-%lu", i, (unsigned long)rand());
278        valueBuf = (char *) malloc(sizeof(char) * (strlen(doc_tpl) + 20));
279        cb_assert(valueBuf != NULL);
280        valsize = sprintf(valueBuf, doc_tpl, i + 1);
281        setdoc(&testdocset.docs[i], &testdocset.infos[i],
282                idBuf, idsize, valueBuf, valsize, zerometa, sizeof(zerometa));
283        testdocset.datasize += valsize;
284    }
285
286    docptrs = (Doc **) malloc(sizeof(Doc*) * count);
287    cb_assert(docptrs != NULL);
288    for (i = 0; i < count; ++i) {
289        docptrs[i] = &testdocset.docs[i];
290    }
291
292    nfoptrs = (DocInfo **) malloc(sizeof(DocInfo*) * count);
293    cb_assert(nfoptrs != NULL);
294    for (i = 0; i < count; ++i) {
295        nfoptrs[i] = &testdocset.infos[i];
296    }
297
298    remove(testfilepath);
299    try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
300    cb_assert(strcmp(couchstore_get_db_filename(db), testfilepath) == 0);
301    try(couchstore_save_documents(db, docptrs, nfoptrs, count, 0));
302    try(couchstore_commit(db));
303    couchstore_close_db(db);
304
305    try(couchstore_open_db(testfilepath, 0, &db));
306
307    /* Read back by doc ID: */
308    fprintf(stderr, "get by ID... ");
309    testdocset.pos = 0;
310    for (i = 0; i < count; ++i) {
311        DocInfo* out_info;
312        try(couchstore_docinfo_by_id(db, testdocset.docs[i].id.buf, testdocset.docs[i].id.size,
313                                     &out_info));
314        docset_check(db, out_info, &testdocset);
315        couchstore_free_docinfo(out_info);
316    }
317
318    /* Read back in bulk by doc ID: */
319    fprintf(stderr, "bulk IDs... ");
320    ids = malloc(count * sizeof(sized_buf));
321    for (i = 0; i < count; ++i) {
322        ids[i] = docptrs[i]->id;
323    }
324    ZERO(testdocset.counters);
325    try(couchstore_docinfos_by_id(db, ids, count, 0, dociter_check, &testdocset));
326    cb_assert(testdocset.counters.totaldocs == count);
327    cb_assert(testdocset.counters.deleted == 0);
328
329    /* Read back by sequence: */
330    fprintf(stderr, "get by sequence... ");
331    sequences = malloc(count * sizeof(*sequences));
332    testdocset.pos = 0;
333    for (i = 0; i < count; ++i) {
334        DocInfo* out_info;
335        sequences[i] = testdocset.infos[i].db_seq;
336        cb_assert(sequences[i] == (uint64_t)i + 1);
337        try(couchstore_docinfo_by_sequence(db, testdocset.infos[i].db_seq, &out_info));
338        docset_check(db, out_info, &testdocset);
339        couchstore_free_docinfo(out_info);
340    }
341
342    /* Read back in bulk by sequence: */
343    fprintf(stderr, "bulk sequences... ");
344    testdocset.pos = 0;
345    ZERO(testdocset.counters);
346    try(couchstore_docinfos_by_sequence(db, sequences, count, 0, docset_check, &testdocset));
347    cb_assert(testdocset.counters.totaldocs == count);
348    cb_assert(testdocset.counters.deleted == 0);
349
350    /* Read back using changes_since: */
351    fprintf(stderr, "changes_since... ");
352    testdocset.pos = 0;
353    ZERO(testdocset.counters);
354    try(couchstore_changes_since(db, 0, 0, docset_check, &testdocset));
355    cb_assert(testdocset.counters.totaldocs == count);
356    cb_assert(testdocset.counters.deleted == 0);
357
358    idtreesize = db->header.by_id_root->subtreesize;
359    seqtreesize = db->header.by_seq_root->subtreesize;
360    reduce = (const raw_by_id_reduce*)db->header.by_id_root->reduce_value.buf;
361    docssize = decode_raw48(reduce->size);
362    dbfilesize = db->file.pos;
363
364    cb_assert(dbfilesize > 0);
365    cb_assert(idtreesize > 0);
366    cb_assert(seqtreesize > 0);
367    cb_assert(docssize > 0);
368    cb_assert(idtreesize < dbfilesize);
369    cb_assert(seqtreesize < dbfilesize);
370    cb_assert(docssize < dbfilesize);
371    cb_assert(db->header.local_docs_root == NULL);
372    cb_assert((idtreesize + seqtreesize + docssize) < dbfilesize);
373
374    couchstore_close_db(db);
375cleanup:
376    free(ids);
377    free(sequences);
378    for (i = 0; i < count; ++i) {
379        free(docptrs[i]->id.buf);
380        free(docptrs[i]->data.buf);
381    }
382    free(docptrs);
383    free(nfoptrs);
384    cb_assert(errcode == 0);
385}
386
387static void test_save_doc(void)
388{
389    int errcode = 0;
390    Db *db;
391    unsigned i;
392    DbInfo info;
393    fprintf(stderr, "save_doc... ");
394    fflush(stderr);
395    docset_init(4);
396    SETDOC(0, "doc1", "{\"test_doc_index\":1}", zerometa);
397    SETDOC(1, "doc2", "{\"test_doc_index\":2}", zerometa);
398    SETDOC(2, "doc3", "{\"test_doc_index\":3}", zerometa);
399    SETDOC(3, "doc4", "{\"test_doc_index\":4}", zerometa);
400    remove(testfilepath);
401    try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
402    try(couchstore_save_document(db, &testdocset.docs[0],
403                                     &testdocset.infos[0], 0));
404    try(couchstore_save_document(db, &testdocset.docs[1],
405                                     &testdocset.infos[1], 0));
406    try(couchstore_save_document(db, &testdocset.docs[2],
407                                     &testdocset.infos[2], 0));
408    try(couchstore_save_document(db, &testdocset.docs[3],
409                                     &testdocset.infos[3], 0));
410    try(couchstore_commit(db));
411    couchstore_close_db(db);
412
413    /* Check that sequence numbers got filled in */
414    for (i = 0; i < 4; ++i) {
415        cb_assert(testdocset.infos[i].db_seq == i + 1);
416    }
417
418    /* Read back */
419    try(couchstore_open_db(testfilepath, 0, &db));
420    try(couchstore_changes_since(db, 0, 0, docset_check, &testdocset));
421    cb_assert(testdocset.counters.totaldocs == 4);
422    cb_assert(testdocset.counters.deleted == 0);
423
424    cb_assert(couchstore_db_info(db, &info) == COUCHSTORE_SUCCESS);
425    cb_assert(info.last_sequence == 4);
426    cb_assert(info.doc_count == 4);
427    cb_assert(info.deleted_count == 0);
428    cb_assert(info.header_position == 4096);
429
430    couchstore_close_db(db);
431cleanup:
432    cb_assert(errcode == 0);
433}
434
435static void test_compressed_doc_body(void)
436{
437    Db *db;
438    Doc *docptrs[2];
439    DocInfo *nfoptrs[2];
440    int errcode = 0;
441
442    fprintf(stderr, "compressed bodies... ");
443    fflush(stderr);
444    docset_init(2);
445    SETDOC(0, "doc1", "{\"test_doc_index\":1, \"val\":\"blah blah blah blah blah blah\"}", zerometa);
446    SETDOC(1, "doc2", "{\"test_doc_index\":2, \"val\":\"blah blah blah blah blah blah\"}", zerometa);
447
448    docptrs[0] = &testdocset.docs[0];
449    docptrs[1] = &testdocset.docs[1];
450    nfoptrs[0] = &testdocset.infos[0];
451    nfoptrs[1] = &testdocset.infos[1];
452
453    /* Mark doc2 as to be snappied. */
454    testdocset.infos[1].content_meta = COUCH_DOC_IS_COMPRESSED;
455    remove(testfilepath);
456    try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
457    try(couchstore_save_documents(db, docptrs, nfoptrs, 2,
458                                      COMPRESS_DOC_BODIES));
459    try(couchstore_commit(db));
460    couchstore_close_db(db);
461    /* Read back */
462    try(couchstore_open_db(testfilepath, 0, &db));
463    try(couchstore_changes_since(db, 0, 0, docset_check, &testdocset));
464    cb_assert(testdocset.counters.totaldocs == 2);
465    cb_assert(testdocset.counters.deleted == 0);
466    couchstore_close_db(db);
467cleanup:
468    cb_assert(errcode == 0);
469}
470
471static void test_dump_empty_db(void)
472{
473    Db *db;
474    couchstore_error_t errcode;
475    DbInfo info;
476
477    fprintf(stderr, "dump empty db... ");
478    fflush(stderr);
479    remove(testfilepath);
480
481    try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
482    try(couchstore_close_db(db));
483    try(couchstore_open_db(testfilepath, 0, &db));
484    dump_count(db);
485    cb_assert(counters.totaldocs == 0);
486    cb_assert(counters.deleted == 0);
487
488    cb_assert(couchstore_db_info(db, &info) == COUCHSTORE_SUCCESS);
489    cb_assert(strcmp(info.filename, testfilepath) == 0);
490    cb_assert(info.last_sequence == 0);
491    cb_assert(info.doc_count == 0);
492    cb_assert(info.deleted_count == 0);
493    cb_assert(info.space_used == 0);
494    cb_assert(info.header_position == 0);
495
496    couchstore_close_db(db);
497cleanup:
498    cb_assert(errcode == 0);
499}
500
501static void test_local_docs(void)
502{
503    int errcode = 0;
504    Db *db;
505    LocalDoc lDocWrite;
506    LocalDoc *lDocRead = NULL;
507    fprintf(stderr, "local docs... ");
508    fflush(stderr);
509    remove(testfilepath);
510    try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
511    lDocWrite.id.buf = "_local/testlocal";
512    lDocWrite.id.size = 16;
513    lDocWrite.json.buf = "{\"test\":true}";
514    lDocWrite.json.size = 13;
515    lDocWrite.deleted = 0;
516    couchstore_save_local_document(db, &lDocWrite);
517    couchstore_commit(db);
518    couchstore_close_db(db);
519    couchstore_open_db(testfilepath, 0, &db);
520    couchstore_open_local_document(db, "_local/testlocal", 16, &lDocRead);
521    cb_assert(lDocRead);
522    cb_assert(lDocRead->json.size == 13);
523    cb_assert(memcmp(lDocRead->json.buf, "{\"test\":true}", 13) == 0);
524    couchstore_free_local_document(lDocRead);
525    couchstore_close_db(db);
526cleanup:
527    cb_assert(errcode == 0);
528}
529
530static void test_open_file_error(void)
531{
532    Db *db = NULL;
533    int errcode;
534
535    fprintf(stderr, "opening nonexistent file errors... ");
536    fflush(stderr);
537    remove(testfilepath);
538    errcode = couchstore_open_db(testfilepath, 0, &db);
539
540    if (errcode != 0) {
541        print_os_err(db);
542    }
543
544    cb_assert(errcode == COUCHSTORE_ERROR_NO_SUCH_FILE);
545
546    /* make sure os.c didn't accidentally call close(0): */
547#ifndef WIN32
548    cb_assert(lseek(0, 0, SEEK_CUR) >= 0 || errno != EBADF);
549#endif
550}
551
552static void shuffle(Doc **docs, DocInfo **docinfos, size_t n)
553{
554    if (n > 1) {
555        size_t i;
556        for (i = 0; i < n - 1; i++) {
557          size_t j = i + rand() / (RAND_MAX / (n - i) + 1);
558          DocInfo *docinfo;
559          Doc *doc = docs[j];
560          docs[j] = docs[i];
561          docs[i] = doc;
562
563          docinfo = docinfos[j];
564          docinfos[j] = docinfos[i];
565          docinfos[i] = docinfo;
566        }
567    }
568}
569
570static int docmap_check(Db *db, DocInfo *info, void *ctx)
571{
572    char* docmap = (char*)ctx;
573    int i;
574    char buffer[100];
575    (void)db;
576    memcpy(buffer, info->id.buf, info->id.size);
577    buffer[info->id.size] = 0; /* null terminate */
578    sscanf(buffer, "doc%d", &i);
579    cb_assert(docmap[i] == 0);
580    docmap[i] = 1;
581    return 0;
582}
583
584static void test_changes_no_dups(void)
585{
586    int errcode = 0;
587    int i;
588    const int numdocs = 10000;
589    int updatebatch = 1000;
590    Doc **docptrs;
591    DocInfo **nfoptrs;
592    char *docmap;
593    Db *db;
594    DbInfo info;
595    fprintf(stderr, "changes no dupes... ");
596    fflush(stderr);
597
598    docset_init(numdocs);
599    for (i=0; i < numdocs; i++) {
600        char* id = malloc(100);
601        char* body = malloc(100);
602        sprintf(id, "doc%d", i);
603        sprintf(body, "{\"test_doc_index\":%d}", i);
604        setdoc(&testdocset.docs[i], &testdocset.infos[i],
605                id, strlen(id),
606                body, strlen(body),
607                zerometa, sizeof(zerometa));
608    }
609    docptrs = malloc(numdocs * sizeof(Doc*));
610    nfoptrs = malloc(numdocs * sizeof(DocInfo*));
611    docmap = malloc(numdocs);
612    for (i=0; i < numdocs; i++) {
613        docptrs[i] = &testdocset.docs[i];
614        nfoptrs[i] = &testdocset.infos[i];
615    }
616    remove(testfilepath);
617    try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
618    /* only save half the docs at first. */
619    try(couchstore_save_documents(db, docptrs, nfoptrs, numdocs/2, 0));
620    try(couchstore_commit(db));
621    couchstore_close_db(db);
622
623    for (i=0; i < numdocs/2; i++) {
624        /* increment the rev for already added docs */
625        nfoptrs[i]->rev_seq++;
626    }
627    srand(10); /* make deterministic */
628    /* now shuffle so some bulk updates contain previous docs and new docs */
629    shuffle(docptrs, nfoptrs, numdocs);
630    try(couchstore_open_db(testfilepath, 0, &db));
631    for (i=0; i < numdocs; i += updatebatch) {
632        /* now do bulk updates and check the changes for dups */
633        try(couchstore_save_documents(db, docptrs + i, nfoptrs + i, updatebatch, 0));
634        try(couchstore_commit(db));
635        memset(docmap, 0, numdocs);
636        try(couchstore_changes_since(db, 0, 0, docmap_check, docmap));
637    }
638
639    cb_assert(couchstore_db_info(db, &info) == COUCHSTORE_SUCCESS);
640    cb_assert(info.last_sequence == (uint64_t)(numdocs + numdocs/2));
641    cb_assert(info.doc_count == (uint64_t)numdocs);
642    cb_assert(info.deleted_count == 0);
643
644    couchstore_close_db(db);
645cleanup:
646    for (i=0; i < numdocs; i++) {
647        free(docptrs[i]->id.buf);
648        free(docptrs[i]->data.buf);
649    }
650    free(docptrs);
651    free(nfoptrs);
652    free(docmap);
653    cb_assert(errcode == 0);
654}
655
656
657static void mb5086(void)
658{
659    Db *db;
660    Doc d;
661    DocInfo i;
662    couchstore_error_t err;
663
664    fprintf(stderr, "regression mb-5086.... ");
665    fflush(stderr);
666
667    setdoc(&d, &i, "hi", 2, "foo", 3, NULL, 0);
668    err = couchstore_open_db("mb5085.couch", COUCHSTORE_OPEN_FLAG_CREATE, &db);
669    cb_assert(err == COUCHSTORE_SUCCESS);
670    cb_assert(couchstore_save_document(db, &d, &i, 0) == COUCHSTORE_SUCCESS);
671    cb_assert(couchstore_commit(db) == COUCHSTORE_SUCCESS);
672    cb_assert(couchstore_close_db(db) == COUCHSTORE_SUCCESS);
673    cb_assert(remove("mb5085.couch") == 0);
674}
675
676static void mb11104(void) {
677    int errcode = 0;
678    Db *db;
679    unsigned i;
680    DbInfo info;
681    sized_buf *ids = NULL;
682
683    fprintf(stderr, "regression mb-11104 ");
684    fflush(stderr);
685    docset_init(4);
686    SETDOC(0, "doc1", "{\"test_doc_index\":1}", zerometa);
687    SETDOC(1, "doc3", "{\"test_doc_index\":3}", zerometa);
688    SETDOC(2, "doc5", "{\"test_doc_index\":5}", zerometa);
689    SETDOC(3, "doc7", "{\"test_doc_index\":7}", zerometa);
690    remove(testfilepath);
691
692    try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
693    try(couchstore_save_document(db, &testdocset.docs[0],
694                                     &testdocset.infos[0], 0));
695    try(couchstore_save_document(db, &testdocset.docs[1],
696                                     &testdocset.infos[1], 0));
697    try(couchstore_save_document(db, &testdocset.docs[2],
698                                     &testdocset.infos[2], 0));
699    try(couchstore_save_document(db, &testdocset.docs[3],
700                                     &testdocset.infos[3], 0));
701    try(couchstore_commit(db));
702
703    cb_assert(couchstore_close_db(db) == COUCHSTORE_SUCCESS);
704
705    try(couchstore_open_db(testfilepath, 0, &db));
706    /* Read back in bulk by doc IDs, some of which are not existent */
707    fprintf(stderr, "bulk IDs... ");
708    char *keys[10] = {"doc1", "doc2", "doc3", "doc4", "doc5", "doc6", "doc7",
709                      "doc8", "doc9"};
710    int count = 3;
711    ids = malloc(count * sizeof(sized_buf));
712    for (i = 0; i < count; ++i) { // "doc1", "doc2", "doc3"
713        ids[i].size = strlen(keys[i]);
714        ids[i].buf = keys[i];
715    }
716    ZERO(testdocset.counters);
717    try(couchstore_docinfos_by_id(db, ids, count, 0, dociter_check, &testdocset));
718    cb_assert(testdocset.counters.totaldocs == 2);
719    cb_assert(testdocset.counters.deleted == 0);
720
721    for (i = 0; i < count; ++i) { // "doc2", "doc4", "doc6"
722        int idx = i * 2 + 1;
723        ids[i].size = strlen(keys[idx]);
724        ids[i].buf = keys[idx];
725    }
726    ZERO(testdocset.counters);
727    try(couchstore_docinfos_by_id(db, ids, count, 0, dociter_check, &testdocset));
728    cb_assert(testdocset.counters.totaldocs == 0);
729    cb_assert(testdocset.counters.deleted == 0);
730
731    for (i = 0; i < count; ++i) { // "doc3", "doc6", "doc9"
732        int idx = i * 3 + 2;
733        ids[i].size = strlen(keys[idx]);
734        ids[i].buf = keys[idx];
735    }
736    ZERO(testdocset.counters);
737    try(couchstore_docinfos_by_id(db, ids, count, 0, dociter_check, &testdocset));
738    cb_assert(testdocset.counters.totaldocs == 1);
739    cb_assert(testdocset.counters.deleted == 0);
740
741    cb_assert(couchstore_db_info(db, &info) == COUCHSTORE_SUCCESS);
742    cb_assert(info.last_sequence == 4);
743    cb_assert(info.doc_count == 4);
744    cb_assert(info.deleted_count == 0);
745    cb_assert(info.header_position == 4096);
746
747    cb_assert(couchstore_close_db(db) == COUCHSTORE_SUCCESS);
748
749cleanup:
750    free(ids);
751}
752
753static void test_asis_seqs(void)
754{
755   Db *db = NULL;
756   Doc d;
757   DocInfo i;
758   DocInfo *ir;
759   couchstore_error_t errcode;
760
761   fprintf(stderr, "as-is seqs.... ");
762   fflush(stderr);
763
764   try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
765   setdoc(&d, &i, "test", 4, "foo", 3, NULL, 0);
766   i.db_seq = 1;
767   try(couchstore_save_document(db, &d, &i, COUCHSTORE_SEQUENCE_AS_IS));
768   cb_assert(db->header.update_seq == 1);
769
770   setdoc(&d, &i, "test_two", 8, "foo", 3, NULL, 0);
771   i.db_seq = 12;
772   try(couchstore_save_document(db, &d, &i, COUCHSTORE_SEQUENCE_AS_IS));
773   cb_assert(db->header.update_seq == 12);
774
775   setdoc(&d, &i, "test_foo", 8, "foo", 3, NULL, 0);
776   i.db_seq = 6;
777   try(couchstore_save_document(db, &d, &i, COUCHSTORE_SEQUENCE_AS_IS));
778   cb_assert(db->header.update_seq == 12);
779
780   try(couchstore_docinfo_by_id(db, "test", 4, &ir));
781   cb_assert(ir->db_seq == 1);
782   couchstore_free_docinfo(ir);
783
784   try(couchstore_docinfo_by_id(db, "test_two", 8, &ir));
785   cb_assert(ir->db_seq == 12);
786   couchstore_free_docinfo(ir);
787
788   try(couchstore_docinfo_by_id(db, "test_foo", 8, &ir));
789   cb_assert(ir->db_seq == 6);
790   couchstore_free_docinfo(ir);
791
792cleanup:
793   if (db != NULL) {
794       couchstore_close_db(db);
795   }
796   cb_assert(errcode == COUCHSTORE_SUCCESS);
797}
798
799static void test_huge_revseq(void)
800{
801    Db *db;
802    Doc d;
803    DocInfo i;
804    DocInfo *i2;
805    couchstore_error_t err;
806
807    fprintf(stderr, "huge rev_seq.... ");
808    fflush(stderr);
809
810    setdoc(&d, &i, "hi", 2, "foo", 3, NULL, 0);
811    i.rev_seq = 5294967296;
812
813    err = couchstore_open_db("bigrevseq.couch", COUCHSTORE_OPEN_FLAG_CREATE, &db);
814    cb_assert(err == COUCHSTORE_SUCCESS);
815    cb_assert(couchstore_save_document(db, &d, &i, 0) == COUCHSTORE_SUCCESS);
816    cb_assert(couchstore_commit(db) == COUCHSTORE_SUCCESS);
817    cb_assert(couchstore_docinfo_by_id(db, "hi", 2, &i2) == COUCHSTORE_SUCCESS);
818    cb_assert(i2->rev_seq == 5294967296);
819    couchstore_free_docinfo(i2);
820    cb_assert(couchstore_close_db(db) == COUCHSTORE_SUCCESS);
821    cb_assert(remove("bigrevseq.couch") == 0);
822}
823
824static void test_dropped_handle(void)
825{
826   couchstore_error_t errcode;
827   Db* db = NULL;
828   Doc d;
829   DocInfo i;
830   Doc* rd;
831
832   fprintf(stderr, "drop file handle.... ");
833   fflush(stderr);
834
835   try(couchstore_open_db(testfilepath, COUCHSTORE_OPEN_FLAG_CREATE, &db));
836   setdoc(&d, &i, "test", 4, "foo", 3, NULL, 0);
837   try(couchstore_save_document(db, &d, &i, 0));
838   try(couchstore_commit(db));
839
840#ifndef _MSC_VER
841   /*
842    * This currently doesn't work windows. The reopen path
843    * fails with a read error
844    */
845   try(couchstore_drop_file(db));
846   cb_assert(couchstore_save_document(db, &d, &i, 0) == COUCHSTORE_ERROR_FILE_CLOSED);
847
848   try(couchstore_reopen_file(db, testfilepath, 0));
849
850   try(couchstore_open_document(db, "test", 4, &rd, 0));
851   couchstore_free_document(rd);
852#endif
853
854cleanup:
855   if (db != NULL) {
856       couchstore_close_db(db);
857   }
858   cb_assert(errcode == COUCHSTORE_SUCCESS);
859}
860
861int main(int argc, const char *argv[])
862{
863    int doc_counts[] = { 4, 69, 666, 9090 };
864    unsigned i;
865    const char *small_doc_tpl = "{\"test_doc_index\":%d}";
866    const char *large_doc_tpl =
867        "{"
868        "\"test_doc_index\":%d,"
869        "\"field1\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\","
870        "\"field2\": \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\","
871        "\"field3\": \"cccccccccccccccccccccccccccccccccccccccccccccccccc"
872        "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
873        "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
874        "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
875        "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
876        "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
877        "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\""
878        "}";
879
880
881    file_merger_tests();
882    file_deduper_tests();
883    file_sorter_tests();
884
885    if (argc > 1)
886        strcpy(testfilepath, argv[1]);
887    printf("Using test database at %s\n", testfilepath);
888
889    test_bitfield_fns();
890
891    test_open_file_error();
892    fprintf(stderr, "OK \n");
893    test_dump_empty_db();
894    fprintf(stderr, " OK\n");
895    test_save_doc();
896    fprintf(stderr, " OK\n");
897    for (i = 0; i < (sizeof(doc_counts) / sizeof(int)); ++i) {
898        test_save_docs(doc_counts[i], small_doc_tpl);
899        fprintf(stderr, " OK\n");
900        test_save_docs(doc_counts[i], large_doc_tpl);
901        fprintf(stderr, " OK\n");
902    }
903    test_local_docs();
904    fprintf(stderr, " OK\n");
905    test_compressed_doc_body();
906    fprintf(stderr, " OK\n");
907    test_changes_no_dups();
908    fprintf(stderr, " OK\n");
909
910    mb5086();
911    fprintf(stderr, " OK\n");
912    mb11104();
913    fprintf(stderr, " OK\n");
914
915    remove(testfilepath);
916    test_huge_revseq();
917    fprintf(stderr, " OK\n");
918    remove(testfilepath);
919    test_asis_seqs();
920    fprintf(stderr, " OK\n");
921    remove(testfilepath);
922    test_dropped_handle();
923    fprintf(stderr, " OK\n");
924    remove(testfilepath);
925
926    /* make sure os.c didn't accidentally call close(0): */
927#ifndef WIN32
928    cb_assert(lseek(0, 0, SEEK_CUR) >= 0 || errno != EBADF);
929#endif
930
931    mapreduce_tests();
932    view_tests();
933    purge_tests();
934
935    return 0;
936}
937