xref: /5.5.2/couchstore/tests/gtest_tests.cc (revision 21c74097)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2
3//#include "config.h"
4
5
6
7#include "bitfield.h"
8#include "couchstoretest.h"
9#include "couchstoredoctest.h"
10#include "documents.h"
11#include "internal.h"
12#include "node_types.h"
13#include "reduces.h"
14
15#include <gtest/gtest.h>
16#include <libcouchstore/couch_db.h>
17
18#include <cstdint>
19#include <limits>
20#include <random>
21#include <thread>
22
23using ::testing::_;
24
25static void test_raw_08(uint8_t value)
26{
27    raw_08 raw;
28    raw = encode_raw08(value);
29    cb_assert(decode_raw08(raw) == value);
30}
31
32static void test_raw_16(uint16_t value)
33{
34    raw_16 raw;
35    raw = encode_raw16(value);
36    cb_assert(decode_raw16(raw) == value);
37}
38
39static void test_raw_32(uint32_t value)
40{
41    raw_32 raw;
42    raw = encode_raw32(value);
43    cb_assert(decode_raw32(raw) == value);
44}
45
46static void test_raw_40(uint64_t value, const uint8_t expected[8])
47{
48    union {
49        raw_40 raw;
50        uint8_t bytes[8];
51    } data;
52    memset(&data, 0, sizeof(data));
53    encode_raw40(value, &data.raw);
54    cb_assert(memcmp(data.bytes, expected, 8) == 0);
55    cb_assert(decode_raw40(data.raw) == value);
56}
57
58static void test_raw_48(uint64_t value, const uint8_t expected[8])
59{
60    union {
61        raw_48 raw;
62        uint8_t bytes[8];
63    } data;
64    memset(&data, 0, sizeof(data));
65    encode_raw48(value, &data.raw);
66    cb_assert(memcmp(data.bytes, expected, 8) == 0);
67    cb_assert(decode_raw48(data.raw) == value);
68}
69
70TEST_F(CouchstoreTest, bitfield_fns)
71{
72    uint8_t expected1[8] = {0x12, 0x34, 0x56, 0x78, 0x90};
73    uint8_t expected2[8] = {0x09, 0x87, 0x65, 0x43, 0x21};
74    uint8_t expected3[8] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB};
75    uint8_t expected4[8] = {0xBA, 0x98, 0x76, 0x54, 0x32, 0x10};
76    struct {
77        raw_08 a;
78        raw_48 b;
79        raw_16 c;
80        raw_40 d;
81        raw_32 e;
82        raw_08 f;
83    } packed;
84    raw_kv_length kv;
85    uint32_t klen, vlen;
86
87    EXPECT_EQ(sizeof(cs_off_t), 8ul);
88
89    EXPECT_EQ(sizeof(raw_08), 1ul);
90    EXPECT_EQ(sizeof(raw_16), 2ul);
91    EXPECT_EQ(sizeof(raw_32), 4ul);
92    EXPECT_EQ(sizeof(raw_40), 5ul);
93    EXPECT_EQ(sizeof(raw_48), 6ul);
94
95    EXPECT_EQ(sizeof(packed), 19ul);
96
97    EXPECT_EQ(sizeof(kv), 5ul);
98    kv = encode_kv_length(1234, 123456);
99    decode_kv_length(&kv, &klen, &vlen);
100    EXPECT_EQ(klen, 1234ul);
101    EXPECT_EQ(vlen, 123456ul);
102
103    test_raw_08(0);
104    test_raw_08(std::numeric_limits<std::uint8_t>::max());
105    test_raw_16(0);
106    test_raw_16(12345);
107    test_raw_16(std::numeric_limits<std::uint16_t>::max());
108    test_raw_32(0);
109    test_raw_32(12345678);
110    test_raw_32(std::numeric_limits<std::uint32_t>::max());
111
112    test_raw_40(0x1234567890ll, expected1);
113    test_raw_40(0x0987654321ll, expected2);
114    test_raw_48(0x1234567890ABll, expected3);
115    test_raw_48(0xBA9876543210ll, expected4);
116}
117
118TEST_P(CouchstoreDoctest, save_docs)
119{
120    bool smallData = std::get<0>(GetParam());
121    int  count = std::get<1>(GetParam());
122    std::string small_doc("{\"test_doc_index\":%d}");
123    std::string large_doc("{"
124                          "\"test_doc_index\":%d,"
125                          "\"field1\": \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\","
126                          "\"field2\": \"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\","
127                          "\"field3\": \"cccccccccccccccccccccccccccccccccccccccccccccccccc"
128                          "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
129                          "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
130                          "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
131                          "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
132                          "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
133                          "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\""
134                          "}");
135
136    Documents documents(count);
137    std::mt19937 twister(count);
138    std::uniform_int_distribution<> distribute(0, 99999); // controls the length of the key a little
139    for (int ii = 0; ii < count; ii++) {
140        std::string key = "doc" +
141                          std::to_string(ii) +
142                          "-" +
143                          std::to_string(distribute(twister));
144        documents.setDoc(ii, key, smallData ? small_doc : large_doc);
145    }
146
147    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
148    EXPECT_EQ(0, strcmp(couchstore_get_db_filename(db), filePath.c_str()));
149    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_documents(db,
150                                                            documents.getDocs(),
151                                                            documents.getDocInfos(),
152                                                            count,
153                                                            0));
154    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
155    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
156    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
157    db = nullptr;
158
159    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
160
161    /* Read back by doc ID: */
162    for (int ii = 0; ii < count; ++ii) {
163        DocInfo* out_info;
164        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfo_by_id(db,
165                                                               documents.getDoc(ii)->id.buf,
166                                                               documents.getDoc(ii)->id.size,
167                                                               &out_info));
168        // Re-use callback to validate the data.
169        SCOPED_TRACE("save_docs - doc by id");
170        Documents::checkCallback(db, out_info, &documents);
171        couchstore_free_docinfo(out_info);
172    }
173
174    /* Read back in bulk by doc ID: */
175    {
176        documents.resetCounters();
177        sized_buf* buf = new sized_buf[count];
178        for (int ii = 0; ii < count; ++ii) {
179            buf[ii] = documents.getDoc(ii)->id;
180        }
181        SCOPED_TRACE("save_docs - doc by id (bulk)");
182        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfos_by_id(db,
183                                                                buf,
184                                                                count,
185                                                                0,
186                                                                &Documents::docIterCheckCallback,
187                                                                &documents));
188        EXPECT_EQ(count, documents.getCallbacks());
189        EXPECT_EQ(0, documents.getDeleted());
190        delete [] buf;
191    }
192
193    /* Read back by sequence: */
194    uint64_t* sequences = new uint64_t[count];
195    for (int ii = 0; ii < count; ++ii) {
196        DocInfo* out_info;
197        sequences[ii] = documents.getDocInfo(ii)->db_seq;
198        EXPECT_EQ((uint64_t)ii + 1, sequences[ii]);
199        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfo_by_sequence(db, sequences[ii], &out_info));
200        // Re-use callback to validate the data.
201        SCOPED_TRACE("save_docs - doc by sequence");
202        Documents::checkCallback(db, out_info, &documents);
203        couchstore_free_docinfo(out_info);
204    }
205
206    /* Read back in bulk by sequence: */
207    {
208        documents.resetCounters();
209        SCOPED_TRACE("save_docs - doc by sequence (bulk)");
210        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfos_by_sequence(db,
211                                                                      sequences,
212                                                                      count,
213                                                                      0,
214                                                                      &Documents::checkCallback,
215                                                                      &documents));
216        EXPECT_EQ(count, documents.getCallbacks());
217        EXPECT_EQ(0, documents.getDeleted());
218    }
219
220    delete [] sequences;
221
222    /* Read back using changes_since: */
223    {
224        documents.resetCounters();
225        SCOPED_TRACE("save_docs - doc changes_since");
226        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_changes_since(db,
227                                                               0,
228                                                               0,
229                                                               &Documents::checkCallback,
230                                                               &documents));
231        EXPECT_EQ(count, documents.getCallbacks());
232        EXPECT_EQ(0, documents.getDeleted());
233    }
234
235    uint64_t idtreesize = db->header.by_id_root->subtreesize;
236    uint64_t seqtreesize = db->header.by_seq_root->subtreesize;
237    const raw_by_id_reduce * reduce = (const raw_by_id_reduce*)db->header.by_id_root->reduce_value.buf;
238    uint64_t docssize = decode_raw48(reduce->size);
239    uint64_t dbfilesize = db->file.pos;
240
241    EXPECT_GT(dbfilesize, 0ull);
242    EXPECT_GT(idtreesize, 0ull);
243    EXPECT_GT(seqtreesize, 0ull);
244    EXPECT_GT(docssize, 0ull);
245    EXPECT_LT(idtreesize, dbfilesize);
246    EXPECT_LT(seqtreesize,  dbfilesize);
247    EXPECT_LT(docssize, dbfilesize);
248    EXPECT_EQ(nullptr, db->header.local_docs_root);
249    EXPECT_LT((idtreesize + seqtreesize + docssize), dbfilesize);
250}
251
252TEST_F(CouchstoreTest, save_doc)
253{
254    DbInfo info;
255
256    const uint32_t docsInTest = 4;
257    Documents documents(docsInTest);
258    documents.setDoc(0, "doc1", "{\"test_doc_index\":1}");
259    documents.setDoc(1, "doc2", "{\"test_doc_index\":2}");
260    documents.setDoc(2, "doc3", "{\"test_doc_index\":3}");
261    documents.setDoc(3, "doc4", "{\"test_doc_index\":4}");
262
263    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
264
265    for (uint32_t ii = 0; ii < docsInTest; ii++) {
266         ASSERT_EQ(COUCHSTORE_SUCCESS,
267                   couchstore_save_document(db,
268                                            documents.getDoc(ii),
269                                            documents.getDocInfo(ii),
270                                            0));
271    }
272
273    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
274    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
275    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
276    db = nullptr;
277
278    /* Check that sequence numbers got filled in */
279    for (uint64_t ii = 0; ii < docsInTest; ++ii) {
280        EXPECT_EQ(ii+1, documents.getDocInfo(ii)->db_seq);
281    }
282
283    /* Read back */
284    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
285    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_changes_since(db,
286                                                           0,
287                                                           0,
288                                                           &Documents::checkCallback,
289                                                           &documents));
290
291    EXPECT_EQ(docsInTest, uint32_t(documents.getCallbacks()));
292    EXPECT_EQ(0, documents.getDeleted());
293
294    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_db_info(db, &info));
295
296    EXPECT_EQ(docsInTest, info.last_sequence);
297    EXPECT_EQ(docsInTest, info.doc_count);
298    EXPECT_EQ(0ul, info.deleted_count);
299    EXPECT_EQ(4096ll, info.header_position);
300}
301
302TEST_F(CouchstoreTest, compressed_doc_body)
303{
304    Documents documents(2);
305    documents.setDoc(0, "doc1", "{\"test_doc_index\":1, \"val\":\"blah blah blah blah blah blah\"}");
306    documents.setDoc(1, "doc2", "{\"test_doc_index\":2, \"val\":\"blah blah blah blah blah blah\"}");
307    documents.setContentMeta(1, COUCH_DOC_IS_COMPRESSED);/* Mark doc2 as to be snappied. */
308
309    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
310    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_documents(db,
311                                  documents.getDocs(),
312                                  documents.getDocInfos(),
313                                  2,
314                                  COMPRESS_DOC_BODIES));
315
316    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
317    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
318    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
319    db = nullptr;
320
321    /* Read back */
322    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
323    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_changes_since(db, 0, 0, &Documents::checkCallback, &documents));
324    EXPECT_EQ(2, documents.getCallbacks());
325    EXPECT_EQ(0, documents.getDeleted());
326}
327
328TEST_F(CouchstoreTest, dump_empty_db)
329{
330    DbInfo info;
331    Documents documents(0);
332
333    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
334    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
335    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
336    db = nullptr;
337
338    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
339    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_changes_since(db, 0, 0, &Documents::countCallback, &documents));
340    EXPECT_EQ(0, documents.getCallbacks());
341    EXPECT_EQ(0, documents.getDeleted());
342    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_db_info(db, &info));
343
344    EXPECT_STREQ(filePath.c_str(), info.filename);
345    EXPECT_EQ(0ull, info.last_sequence);
346    EXPECT_EQ(0ull, info.doc_count);
347    EXPECT_EQ(0ull, info.deleted_count);
348    EXPECT_EQ(0ull, info.space_used);
349    EXPECT_EQ(0ll, info.header_position);
350}
351
352TEST_F(CouchstoreTest, local_docs)
353{
354    LocalDoc lDocWrite;
355    LocalDoc *lDocRead = NULL;
356
357    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
358    lDocWrite.id.buf = const_cast<char*>("_local/testlocal");
359    lDocWrite.id.size = 16;
360    lDocWrite.json.buf = const_cast<char*>("{\"test\":true}");
361    lDocWrite.json.size = 13;
362    lDocWrite.deleted = 0;
363    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_local_document(db, &lDocWrite));
364    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
365    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
366    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
367    db = nullptr;
368    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
369    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_local_document(db, "_local/testlocal", 16, &lDocRead));
370    ASSERT_NE(nullptr, lDocRead);
371    EXPECT_EQ(13ull, lDocRead->json.size);
372
373    EXPECT_EQ(0, memcmp(lDocRead->json.buf, "{\"test\":true}", 13));
374    couchstore_free_local_document(lDocRead);
375}
376
377TEST_F(CouchstoreTest, open_file_error)
378{
379
380    int errcode;
381    errcode = couchstore_open_db(filePath.c_str(), 0, &db);
382
383    EXPECT_EQ(errcode, COUCHSTORE_ERROR_NO_SUCH_FILE);
384
385    /* make sure os.c didn't accidentally call close(0): */
386#ifndef WIN32
387    EXPECT_TRUE(lseek(0, 0, SEEK_CUR) >= 0 || errno != EBADF);
388#endif
389}
390
391TEST_F(CouchstoreTest, changes_no_dups)
392{
393    const size_t numdocs = 10000;
394    int updatebatch = 1000;
395    DbInfo info;
396
397    Documents documents(numdocs);
398    for (size_t ii = 0; ii < numdocs; ii++) {
399        std::string key = "doc" + std::to_string(ii);
400        std::string data = "{\"test_doc_index\":" + std::to_string(ii) + "}";
401        documents.setDoc(ii, key, data);
402    }
403
404    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
405    /* only save half the docs at first. */
406    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_documents(db,
407                                                            documents.getDocs(),
408                                                            documents.getDocInfos(),
409                                                            numdocs/2,
410                                                            0));
411    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
412    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
413    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
414    db = nullptr;
415
416    for (size_t ii = 0; ii < numdocs/2; ii++) {
417        /* increment the rev for already added docs */
418        documents.getDocInfo(ii)->rev_seq++;
419    }
420
421    /* now shuffle so some bulk updates contain previous docs and new docs */
422    documents.shuffle();
423
424    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
425
426    for (size_t ii=0; ii < numdocs; ii += updatebatch) {
427        /* now do bulk updates and check the changes for dups */
428        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_documents(db,
429                                                                documents.getDocs() + ii,
430                                                                documents.getDocInfos() + ii,
431                                                                updatebatch,
432                                                                0));
433        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
434        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_changes_since(db, 0, 0,
435                                                               &Documents::docMapUpdateCallback,
436                                                               &documents));
437        documents.clearDocumentMap();
438    }
439
440    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_db_info(db, &info));
441    EXPECT_EQ((uint64_t)(numdocs + numdocs/2), info.last_sequence);
442    EXPECT_EQ(numdocs, info.doc_count);
443    EXPECT_EQ(0ull, info.deleted_count);
444}
445
446TEST_F(CouchstoreTest, mb5086)
447{
448    Documents documents(1);
449    documents.setDoc(0, "hi", "foo");
450
451    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db("mb5085.couch", COUCHSTORE_OPEN_FLAG_CREATE, &db));
452    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_document(db,
453                                                           documents.getDoc(0),
454                                                           documents.getDocInfo(0),
455                                                           0));
456    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
457    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
458    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
459    ASSERT_EQ(0, remove("mb5085.couch"));
460    db = nullptr; // we've closed and deleted the test-case's file
461}
462
463TEST_F(CouchstoreTest, mb11104)
464{
465    DbInfo info;
466    const int batchSize = 3;
467    sized_buf ids[batchSize];
468    Documents documents(batchSize * 3);
469    for (int ii = 0; ii < batchSize*3; ii++) {
470        std::string key = "doc" + std::to_string(ii);
471        std::string data = "{\"test_doc_index\":" + std::to_string(ii) + "}";
472        documents.setDoc(ii, key, data);
473    }
474    int storeIndex[4] = {0, 2, 4, 6};
475
476    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
477    // store some of the documents
478    for (int ii = 0; ii < 4; ii++) {
479       ASSERT_EQ(COUCHSTORE_SUCCESS,
480                 couchstore_save_document(db,
481                                          documents.getDoc(storeIndex[ii]),
482                                          documents.getDocInfo(storeIndex[ii]),
483                                          0));
484    }
485
486    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
487    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
488    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
489    db = nullptr;
490    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
491
492    /* Read back in bulk by doc IDs, some of which are not existent */
493    {
494        Documents callbackCounter(0);
495        for (int ii = 0; ii < batchSize; ++ii) { // "doc1", "doc2", "doc3"
496            ids[ii].size = documents.getDoc(ii)->id.size;
497            ids[ii].buf = documents.getDoc(ii)->id.buf;
498        }
499
500        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfos_by_id(db,
501                                                                ids,
502                                                                batchSize,
503                                                                0,
504                                                                &Documents::docIterCheckCallback,
505                                                                &callbackCounter));
506        EXPECT_EQ(2, callbackCounter.getCallbacks());
507        EXPECT_EQ(0, callbackCounter.getDeleted());
508    }
509    {
510        Documents callbackCounter(0);
511        for (int ii = 0; ii < batchSize; ++ii) { // "doc2", "doc4", "doc6"
512            int idx = ii * 2 + 1;
513            ids[ii].size = documents.getDoc(idx)->id.size;
514            ids[ii].buf = documents.getDoc(idx)->id.buf;
515        }
516
517        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfos_by_id(db,
518                                                                ids,
519                                                                batchSize,
520                                                                0,
521                                                                &Documents::docIterCheckCallback,
522                                                                &callbackCounter));
523        EXPECT_EQ(0, callbackCounter.getCallbacks());
524        EXPECT_EQ(0, callbackCounter.getDeleted());
525    }
526    {
527        Documents callbackCounter(0);
528        for (int ii = 0; ii < batchSize; ++ii) { // "doc3", "doc6", "doc9"
529            int idx = ii * 3 + 2;
530            ids[ii].size = documents.getDoc(idx)->id.size;
531            ids[ii].buf = documents.getDoc(idx)->id.buf;
532        }
533
534        ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfos_by_id(db,
535                                                                ids,
536                                                                batchSize,
537                                                                0,
538                                                                &Documents::docIterCheckCallback,
539                                                                &callbackCounter));
540        EXPECT_EQ(1, callbackCounter.getCallbacks());
541        EXPECT_EQ(0, callbackCounter.getDeleted());
542    }
543
544
545    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_db_info(db, &info));
546    EXPECT_EQ(4ull, info.last_sequence);
547    EXPECT_EQ(4ull, info.doc_count);
548    EXPECT_EQ(0ull, info.deleted_count);
549    EXPECT_EQ(4096ll, info.header_position);
550}
551
552TEST_F(CouchstoreTest, asis_seqs)
553{
554    DocInfo *ir = nullptr;
555
556    Documents documents(3);
557    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
558    documents.setDoc(0, "test", "foo");
559    documents.getDocInfo(0)->db_seq = 1;
560    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_document(db,
561                                                           documents.getDoc(0),
562                                                           documents.getDocInfo(0),
563                                                           COUCHSTORE_SEQUENCE_AS_IS));
564    EXPECT_EQ(1ull, db->header.update_seq);
565
566    documents.setDoc(1, "test_two", "foo");
567    documents.getDocInfo(1)->db_seq = 12;
568    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_document(db,
569                                                           documents.getDoc(1),
570                                                           documents.getDocInfo(1),
571                                                           COUCHSTORE_SEQUENCE_AS_IS));
572    EXPECT_EQ(12ull, db->header.update_seq);
573
574    documents.setDoc(2, "test_foo", "foo");
575    documents.getDocInfo(2)->db_seq = 6;
576    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_document(db,
577                                                           documents.getDoc(2),
578                                                           documents.getDocInfo(2),
579                                                           COUCHSTORE_SEQUENCE_AS_IS));
580    EXPECT_EQ(12ull, db->header.update_seq);
581
582    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfo_by_id(db, "test", 4, &ir));
583    EXPECT_EQ(1ull, ir->db_seq);
584
585    couchstore_free_docinfo(ir);
586
587    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfo_by_id(db, "test_two", 8, &ir));
588    EXPECT_EQ(12ull, ir->db_seq);
589    couchstore_free_docinfo(ir);
590
591    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfo_by_id(db, "test_foo", 8, &ir));
592    EXPECT_EQ(6ull, ir->db_seq);
593    couchstore_free_docinfo(ir);
594
595}
596
597TEST_F(CouchstoreTest, huge_revseq)
598{
599    DocInfo *i2;
600    Documents documents(1);
601    documents.setDoc(0, "hi", "foo");
602    documents.getDocInfo(0)->rev_seq = 5294967296;
603
604    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db("bigrevseq.couch", COUCHSTORE_OPEN_FLAG_CREATE, &db));
605    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_document(db,
606                                                           documents.getDoc(0),
607                                                           documents.getDocInfo(0),
608                                                           0));
609    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
610    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfo_by_id(db, "hi", 2, &i2));
611    EXPECT_EQ(i2->rev_seq, 5294967296ull);
612    couchstore_free_docinfo(i2);
613    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
614    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
615    ASSERT_EQ(0, remove("bigrevseq.couch"));
616    db = nullptr; // mark as null, as we've cleaned up
617}
618
619// Create a new-file(s) and check crc is crc32-c
620TEST_F(CouchstoreTest, crc32c) {
621    ASSERT_EQ(COUCHSTORE_SUCCESS,
622              couchstore_open_db(filePath.c_str(),
623                                 COUCHSTORE_OPEN_FLAG_CREATE,
624                                 &db));
625    EXPECT_EQ(CRC32C, db->file.crc_mode);
626}
627
628// Create a new-file(s) and test that we can't open again with old CRC
629TEST_F(CouchstoreTest, legacy_crc_flags) {
630    // Open the new/clean file and ask for 'legacy-CRC'
631    ASSERT_EQ(COUCHSTORE_SUCCESS,
632              couchstore_open_db(filePath.c_str(),
633                                 COUCHSTORE_OPEN_FLAG_CREATE |
634                                 COUCHSTORE_OPEN_WITH_LEGACY_CRC,
635                                 &db));
636
637    EXPECT_EQ(CRC32, db->file.crc_mode);
638
639    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
640    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
641    db = nullptr;
642
643    // Open the now existing file and we should be allowed if ask for legacy-crc
644    ASSERT_EQ(COUCHSTORE_SUCCESS,
645              couchstore_open_db(filePath.c_str(),
646                                 COUCHSTORE_OPEN_FLAG_CREATE |
647                                 COUCHSTORE_OPEN_WITH_LEGACY_CRC,
648                                 &db));
649
650    EXPECT_EQ(CRC32, db->file.crc_mode);
651
652    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
653    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
654    db = nullptr;
655
656    // Open the now existing file and we should be allowed, legacy crc will be auto-selected
657    ASSERT_EQ(COUCHSTORE_SUCCESS,
658              couchstore_open_db(filePath.c_str(),
659                                 COUCHSTORE_OPEN_FLAG_CREATE,
660                                 &db));
661
662    EXPECT_EQ(CRC32, db->file.crc_mode);
663
664    // Close and delete.
665    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
666    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
667    db = nullptr;
668
669    ASSERT_EQ(0, remove(filePath.c_str()));
670
671    // Open the a new file without legacy CRC
672    ASSERT_EQ(COUCHSTORE_SUCCESS,
673              couchstore_open_db(filePath.c_str(),
674                                 COUCHSTORE_OPEN_FLAG_CREATE,
675                                 &db));
676
677    // Should be in crc32c
678    EXPECT_EQ(CRC32C, db->file.crc_mode);
679    EXPECT_GE(uint64_t(COUCH_DISK_VERSION_12), db->header.disk_version);
680
681    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
682    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
683    db = nullptr;
684
685    // Open it again and we should not be allowed.
686    ASSERT_EQ(COUCHSTORE_ERROR_INVALID_ARGUMENTS,
687              couchstore_open_db(filePath.c_str(),
688                                 COUCHSTORE_OPEN_FLAG_CREATE |
689                                 COUCHSTORE_OPEN_WITH_LEGACY_CRC,
690                                 &db));
691
692    // no open file for destruction...
693    db = nullptr;
694}
695
696// Test compaction doesn't upgrade (no upgrade flag specified)
697TEST_F(CouchstoreTest, no_crc_upgrade) {
698
699    const int docCount = 100;
700    Documents documents(docCount);
701    documents.generateDocs();
702
703    // Open file in legacy mode
704    ASSERT_EQ(COUCHSTORE_SUCCESS,
705              couchstore_open_db(filePath.c_str(),
706                                 COUCHSTORE_OPEN_FLAG_CREATE |
707                                 COUCHSTORE_OPEN_WITH_LEGACY_CRC,
708                                 &db));
709    EXPECT_EQ(CRC32, db->file.crc_mode);
710
711    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_documents(db,
712                                                            documents.getDocs(),
713                                                            documents.getDocInfos(),
714                                                            docCount,
715                                                            0));
716
717    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
718    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
719    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
720    db = nullptr;
721
722    // re-open, we're going to compact the file
723    ASSERT_EQ(COUCHSTORE_SUCCESS,
724        couchstore_open_db(filePath.c_str(),
725                           0,
726                           &db));
727    EXPECT_EQ(CRC32, db->file.crc_mode);
728    // new file should 11 or less
729    EXPECT_LE(db->header.disk_version, uint64_t(COUCH_DISK_VERSION_11));
730
731    std::string target("compacted.couch");
732    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_compact_db_ex(db,
733                                                           target.c_str(),
734                                                           0,
735                                                           nullptr,
736                                                           nullptr,
737                                                           nullptr,
738                                                           couchstore_get_default_file_ops()));
739
740    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
741    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
742    db = nullptr;
743
744    // Open target...
745    ASSERT_EQ(COUCHSTORE_SUCCESS,
746        couchstore_open_db(target.c_str(),
747                           0,
748                           &db));
749
750    EXPECT_EQ(CRC32, db->file.crc_mode); // new file still uses old CRC
751    EXPECT_LE(db->header.disk_version, uint64_t(COUCH_DISK_VERSION_11)); // compacted file still 11 or less
752    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
753    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
754    db = nullptr;
755    ASSERT_EQ(0, remove(target.c_str()));
756}
757
758// Test compaction upgrades when upgrade flag specified.
759TEST_F(CouchstoreTest, crc_upgrade) {
760    const int docCount = 100;
761    Documents documents(docCount);
762    documents.generateDocs();
763
764    // Open file in legacy mode
765    ASSERT_EQ(COUCHSTORE_SUCCESS,
766              couchstore_open_db(filePath.c_str(),
767                                 COUCHSTORE_OPEN_FLAG_CREATE |
768                                 COUCHSTORE_OPEN_WITH_LEGACY_CRC,
769                                 &db));
770    EXPECT_EQ(CRC32, db->file.crc_mode);
771    // new file must be version 11 or less
772    EXPECT_LE(db->header.disk_version, uint64_t(COUCH_DISK_VERSION_11));
773
774    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_documents(db,
775                                                            documents.getDocs(),
776                                                            documents.getDocInfos(),
777                                                            docCount,
778                                                            0));
779
780    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
781    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
782    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
783    db = nullptr;
784
785    // re-open, we're going to compact the file
786    ASSERT_EQ(COUCHSTORE_SUCCESS,
787        couchstore_open_db(filePath.c_str(),
788                           COUCHSTORE_OPEN_FLAG_CREATE,
789                           &db));
790    EXPECT_EQ(CRC32, db->file.crc_mode);
791
792    std::string target("compacted.couch");
793    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_compact_db_ex(db,
794                                                           target.c_str(),
795                                                           COUCHSTORE_COMPACT_FLAG_UPGRADE_DB,
796                                                           nullptr,
797                                                           nullptr,
798                                                           nullptr,
799                                                           couchstore_get_default_file_ops()));
800
801    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
802    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
803    db = nullptr;
804
805    // Open target...
806    ASSERT_EQ(COUCHSTORE_SUCCESS,
807        couchstore_open_db(target.c_str(),
808                           0,
809                           &db));
810
811    // File now with CRC32-C
812    EXPECT_EQ(CRC32C, db->file.crc_mode);
813    EXPECT_GE(db->header.disk_version, uint64_t(COUCH_DISK_VERSION_12)); // upgraded to 12
814    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
815    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
816    db = nullptr;
817    ASSERT_EQ(0, remove(target.c_str()));
818}
819
820// Test compaction upgrades has no ill effect when upgrade flag specified and the file
821// is already at version 12/crc32c
822TEST_F(CouchstoreTest, crc_upgrade2) {
823    const size_t docCount = 100;
824    Documents documents(docCount);
825    documents.generateDocs();
826
827    ASSERT_EQ(COUCHSTORE_SUCCESS,
828              couchstore_open_db(filePath.c_str(),
829                                 COUCHSTORE_OPEN_FLAG_CREATE,
830                                 &db));
831    EXPECT_EQ(CRC32C, db->file.crc_mode);
832    // new file must be version 11 or less
833    EXPECT_GE(db->header.disk_version, uint64_t(COUCH_DISK_VERSION_12));
834
835    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_documents(db,
836                                                            documents.getDocs(),
837                                                            documents.getDocInfos(),
838                                                            docCount,
839                                                            0));
840
841    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
842
843    std::string target("compacted.couch");
844    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_compact_db_ex(db,
845                                                           target.c_str(),
846                                                           COUCHSTORE_COMPACT_FLAG_UPGRADE_DB,
847                                                           nullptr,
848                                                           nullptr,
849                                                           nullptr,
850                                                           couchstore_get_default_file_ops()));
851
852    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
853    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
854    db = nullptr;
855
856    // Open target...
857    ASSERT_EQ(COUCHSTORE_SUCCESS,
858        couchstore_open_db(target.c_str(),
859                           0,
860                           &db));
861
862    // File should still be v12 with crc32C
863    EXPECT_EQ(CRC32C, db->file.crc_mode);
864    EXPECT_GE(db->header.disk_version, uint64_t(COUCH_DISK_VERSION_12)); // still version 12
865
866    // Now use callback to validate new file.
867    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_changes_since(db,
868                                                           0,
869                                                           0,
870                                                           &Documents::checkCallback,
871                                                           &documents));
872
873    EXPECT_EQ(docCount, size_t(documents.getCallbacks()));
874    EXPECT_EQ(0, documents.getDeleted());
875
876    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
877    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
878    db = nullptr;
879    ASSERT_EQ(0, remove(target.c_str()));
880}
881
882// Parameters for MT_save_worker.
883struct MT_save_args {
884    size_t worker_id;
885    Db* db;
886    std::string file_path_prefix;
887};
888
889// Worker thread for MT_save test.
890void MT_save_worker(void* voidargs) {
891    MT_save_args *args = static_cast<MT_save_args*>(voidargs);
892    std::string file_path = args->file_path_prefix
893                            + std::to_string(args->worker_id);
894    remove(file_path.c_str());
895
896    const uint32_t docsInTest = 100;
897    std::string key_str, value_str;
898    Documents documents(docsInTest);
899
900    for (uint32_t ii = 0; ii < docsInTest; ii++) {
901        key_str = "doc" + std::to_string(ii);
902        value_str = "{\"test_doc_index\":" + std::to_string(ii) + "}";
903        documents.setDoc(ii, key_str, value_str);
904    }
905
906    // Save docs.
907    couchstore_open_db(file_path.c_str(),
908                       COUCHSTORE_OPEN_FLAG_CREATE, &args->db);
909
910    for (uint32_t ii = 0; ii < docsInTest; ii++) {
911        couchstore_save_document(args->db,
912                                 documents.getDoc(ii),
913                                 documents.getDocInfo(ii),
914                                 0);
915    }
916
917    couchstore_commit(args->db);
918    couchstore_close_file(args->db);
919    couchstore_free_db(args->db);
920
921    // Check docs.
922    couchstore_open_db(file_path.c_str(),
923                       COUCHSTORE_OPEN_FLAG_CREATE, &args->db);
924
925    documents.resetCounters();
926    std::vector<sized_buf> buf(docsInTest);
927    for (uint32_t ii = 0; ii < docsInTest; ++ii) {
928        buf[ii] = documents.getDoc(ii)->id;
929    }
930    couchstore_docinfos_by_id(args->db,
931                              &buf[0],
932                              docsInTest,
933                              0,
934                              &Documents::docIterCheckCallback,
935                              &documents);
936    couchstore_close_file(args->db);
937    couchstore_free_db(args->db);
938}
939
940// Context for callback function of latency collector.
941struct MT_save_callback_ctx {
942    // Number of threads.
943    size_t num_threads;
944};
945
946int MT_save_callback(const char* stat_name,
947                     CouchLatencyHisto* latencies,
948                     const CouchLatencyMicroSecRep elapsed_time,
949                     void* ctx) {
950    struct MT_save_callback_ctx *actual_ctx =
951            static_cast<MT_save_callback_ctx*>(ctx);
952    uint64_t count_total = 0;
953    for (auto& itr_hist : *latencies) {
954        count_total += itr_hist->count();
955    }
956
957    // # calls of all APIs should be
958    // the multiplication of # threads.
959    EXPECT_EQ(static_cast<size_t>(0), count_total % actual_ctx->num_threads);
960
961    return 0;
962}
963
964// Multi-threaded document saving and loading test.
965// Check if latency collector registers latency timer
966// for each API correctly, under the racing condition.
967TEST_P(CouchstoreMTTest, MT_save)
968{
969    const size_t numRepeat = 4;
970    const bool enable_collector = std::get<0>(GetParam());
971    for (size_t rpt = 0; rpt < numRepeat; ++rpt) {
972        if (enable_collector) {
973            couchstore_latency_collector_start();
974        }
975
976        std::vector<std::thread> t_handles(numThreads);
977        std::vector<MT_save_args> args(numThreads);
978
979        for (size_t ii = 0; ii < numThreads; ++ii) {
980            args[ii].worker_id = ii;
981            args[ii].db = dbs[ii];
982            args[ii].file_path_prefix = filePath;
983            t_handles[ii] = std::thread(MT_save_worker, &args[ii]);
984        }
985
986        for (size_t ii = 0; ii < numThreads; ++ii) {
987            t_handles[ii].join();
988            // 'dbs[ii]' is already closed at the end of above thread.
989            dbs[ii] = nullptr;
990        }
991
992        if (enable_collector) {
993            couchstore_latency_dump_options options;
994            MT_save_callback_ctx ctx = {numThreads};
995            couchstore_get_latency_info(MT_save_callback,
996                                        options,
997                                        &ctx);
998            couchstore_latency_collector_stop();
999        }
1000    }
1001}
1002
1003/* Test to check that retrieving an item with value of zero length
1004 * doesn't result in a memory leak
1005 */
1006TEST_F(CouchstoreTest, mb23697) {
1007    DocInfo* ir = nullptr;
1008    Doc* doc = nullptr;
1009
1010    Documents documents(1);
1011    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(),
1012              COUCHSTORE_OPEN_FLAG_CREATE, &db));
1013    documents.setDoc(0, "test", "");
1014    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_document(db,
1015                                                           documents.getDoc(0),
1016                                                           documents.getDocInfo(0),
1017                                                           0));
1018    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_docinfo_by_id(db, "test", 4, &ir));
1019    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_doc_with_docinfo(db, ir, &doc,
1020                                                      DECOMPRESS_DOC_BODIES));
1021    EXPECT_EQ(static_cast<size_t>(0), doc->data.size);
1022    couchstore_free_docinfo(ir);
1023    couchstore_free_document(doc);
1024}
1025
1026static int time_purge_hook_impl(Db* target, DocInfo* info, sized_buf item) {
1027    if (item.buf == nullptr) {
1028        return COUCHSTORE_COMPACT_NEED_BODY;
1029    }
1030
1031    return COUCHSTORE_SUCCESS;
1032}
1033
1034class CompactionHookInterface {
1035    public:
1036        virtual int time_purge_hook(Db* target, DocInfo* info, sized_buf item) = 0;
1037};
1038
1039class CompactionHook : CompactionHookInterface {
1040    public:
1041        virtual int time_purge_hook(Db* target, DocInfo* info, sized_buf item) {
1042            return time_purge_hook_impl(target, info, item);
1043        }
1044};
1045
1046class MockTimePurgeHook : CompactionHook {
1047    public:
1048        MOCK_METHOD3(time_purge_hook, int(Db* target, DocInfo* info, sized_buf item));
1049};
1050
1051int mockTimePurgeHook(Db* target, DocInfo* info, sized_buf item, void* ctx_p) {
1052    auto* ctx = reinterpret_cast<MockTimePurgeHook*>(ctx_p);
1053    return ctx->time_purge_hook(target, info, item);
1054}
1055
1056/* Test to check that the compaction will send the full body in case the
1057 * client requests the same
1058 */
1059TEST_F(CouchstoreTest, compact_need_body) {
1060    Documents documents(1);
1061    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(),
1062              COUCHSTORE_OPEN_FLAG_CREATE, &db));
1063    documents.setDoc(0, "key", "value");
1064    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_save_document(db,
1065                                                           documents.getDoc(0),
1066                                                           documents.getDocInfo(0),
1067                                                           0));
1068    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
1069
1070    MockTimePurgeHook tph;
1071    std::string target("compacted.couch");
1072    EXPECT_CALL(tph, time_purge_hook(_,_,_)).Times(3)
1073                                            .WillOnce(testing::Return(COUCHSTORE_COMPACT_NEED_BODY))
1074                                            .WillOnce(testing::Return(COUCHSTORE_SUCCESS))
1075                                            .WillOnce(testing::Return(COUCHSTORE_SUCCESS));
1076
1077    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_compact_db_ex(db,
1078                                                           target.c_str(),
1079                                                           0,
1080                                                           mockTimePurgeHook,
1081                                                           nullptr,
1082                                                           &tph,
1083                                                           couchstore_get_default_file_ops()));
1084    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
1085    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
1086    db = nullptr;
1087    ASSERT_EQ(0, remove(target.c_str()));
1088}
1089
1090
1091/** verify couchstore_changes_count() returns correct values
1092 *
1093 * couchstore_changes_count() will return the # of unique documents
1094 * that have been inserted between 2 sequence #s. The sequence # will
1095 * increase for updated and deleted documents but the count of original
1096 * documents between the 2 sequence #'s remains the same
1097 */
1098TEST_F(CouchstoreTest, test_changes_count) {
1099    ASSERT_EQ(COUCHSTORE_SUCCESS,
1100              couchstore_open_db(
1101                      filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
1102
1103    /**
1104     * add some documents to the database and make sure the count =
1105     * the number of documents added
1106     */
1107    const int ndocs = 5; // use a value at least >= 5
1108    Documents documents(ndocs);
1109    const std::string ori_doc = "{\"test_doc\":\"original\"}";
1110
1111    for (int ii = 0; ii < ndocs; ++ii) {
1112        std::string key = "doc" + std::to_string(ii);
1113        documents.setDoc(ii, key, ori_doc);
1114    }
1115    ASSERT_EQ(COUCHSTORE_SUCCESS,
1116              couchstore_save_documents(db,
1117                                        documents.getDocs(),
1118                                        documents.getDocInfos(),
1119                                        ndocs,
1120                                        0));
1121    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
1122
1123    /**
1124     * count from the first seq # of the first doc added until the end
1125     */
1126    uint64_t count;
1127    uint64_t start_seq = documents.getDocInfo(0)->db_seq;
1128    ASSERT_EQ(COUCHSTORE_SUCCESS,
1129              couchstore_changes_count(
1130                      db, start_seq, db->header.update_seq, &count));
1131    ASSERT_EQ(count, ndocs);
1132
1133    /**
1134     * update a few docs... count should stay the same
1135     */
1136    documents.resetCounters();
1137
1138    std::string upd_doc = "{\"test_doc\":\"updated\"}";
1139    documents.setDoc(0, "doc0", upd_doc);
1140    documents.setDoc(1, "doc3", upd_doc);
1141    ASSERT_EQ(COUCHSTORE_SUCCESS,
1142              couchstore_save_documents(
1143                      db, documents.getDocs(), documents.getDocInfos(), 2, 0));
1144    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
1145
1146    ASSERT_EQ(COUCHSTORE_SUCCESS,
1147              couchstore_changes_count(
1148                      db, start_seq, db->header.update_seq, &count));
1149    ASSERT_EQ(count, ndocs);
1150
1151    /**
1152     * delete a few docs... count should stay the same
1153     */
1154    ASSERT_EQ(
1155            COUCHSTORE_SUCCESS,
1156            couchstore_save_documents(db, NULL, documents.getDocInfos(), 2, 0));
1157    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
1158
1159    ASSERT_EQ(COUCHSTORE_SUCCESS,
1160              couchstore_changes_count(
1161                      db, start_seq, db->header.update_seq, &count));
1162    ASSERT_EQ(count, ndocs);
1163
1164    /**
1165     * add some more documents
1166     */
1167    documents.resetCounters();
1168    for (int ii = 0; ii < ndocs; ++ii) {
1169        std::string key = "doc" + std::to_string(ii + ndocs);
1170        documents.setDoc(ii, key, ori_doc);
1171    }
1172    ASSERT_EQ(COUCHSTORE_SUCCESS,
1173              couchstore_save_documents(db,
1174                                        documents.getDocs(),
1175                                        documents.getDocInfos(),
1176                                        ndocs,
1177                                        0));
1178    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
1179
1180    ASSERT_EQ(COUCHSTORE_SUCCESS,
1181              couchstore_changes_count(
1182                      db, start_seq, db->header.update_seq, &count));
1183    ASSERT_EQ(count, ndocs + ndocs);
1184
1185    /**
1186     * we updated the first doc in the sequence which moved its seq #
1187     * so there are still 10 changed docs between the sequence #'s
1188     */
1189    ASSERT_EQ(COUCHSTORE_SUCCESS,
1190              couchstore_changes_count(
1191                      db, start_seq + 1, db->header.update_seq, &count));
1192    ASSERT_EQ(count, ndocs + ndocs);
1193
1194    /**
1195     * the 2nd doc was untouched so if we ask for changed docs
1196     * past it, we should get total - 1
1197     */
1198    ASSERT_EQ(COUCHSTORE_SUCCESS,
1199              couchstore_changes_count(
1200                      db, start_seq + 2, db->header.update_seq, &count));
1201    ASSERT_EQ(count, ndocs + ndocs - 1);
1202
1203    /* don't include the last document */
1204    ASSERT_EQ(COUCHSTORE_SUCCESS,
1205              couchstore_changes_count(
1206                      db, start_seq, db->header.update_seq - 1, &count));
1207    ASSERT_EQ(count, ndocs + ndocs - 1);
1208
1209    DbInfo info;
1210    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_db_info(db, &info));
1211
1212    /* deleted 2 docs */
1213    EXPECT_EQ(ndocs + ndocs - 2, info.doc_count);
1214    EXPECT_EQ(2, info.deleted_count);
1215
1216    /* ndocs + ndocs + 2 updates + 2 deletes */
1217    EXPECT_EQ(ndocs + ndocs + 2 + 2, info.last_sequence);
1218
1219    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
1220    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
1221    db = nullptr;
1222}
1223
1224/**
1225 * verify that couchstore_set_purge_seq() sets the purge_seq and retains
1226 * that value if the file is closed and reopened.
1227 */
1228TEST_F(CouchstoreTest, test_set_purge_seq) {
1229    const int ndocs = 100; // use a value at least >= 5
1230    Documents documents(ndocs);
1231
1232    ASSERT_EQ(COUCHSTORE_SUCCESS,
1233              couchstore_open_db(
1234                      filePath.c_str(), COUCHSTORE_OPEN_FLAG_CREATE, &db));
1235
1236    for (int ii = 0; ii < ndocs; ++ii) {
1237        std::string key = "doc" + std::to_string(ii);
1238        std::string doc = "{\"test_doc\":\"original\"}";
1239        documents.setDoc(ii, key, doc);
1240    }
1241    ASSERT_EQ(COUCHSTORE_SUCCESS,
1242              couchstore_save_documents(db,
1243                                        documents.getDocs(),
1244                                        documents.getDocInfos(),
1245                                        ndocs,
1246                                        0));
1247    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
1248
1249    DbInfo info1;
1250    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_db_info(db, &info1));
1251
1252    /**
1253     * verify the purge_seq is 0
1254     */
1255    ASSERT_EQ(0, info1.purge_seq);
1256
1257    /**
1258     * set the purge_seq to 1/2 of the last_sequence
1259     */
1260    uint64_t pseq = info1.last_sequence / 2;
1261    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_set_purge_seq(db, pseq));
1262
1263    /**
1264     * commit and close the file
1265     */
1266    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_commit(db));
1267    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
1268    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
1269    db = nullptr;
1270
1271    /**
1272     * reopen the file and verify the purge_seq is set to the correct value
1273     */
1274    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_open_db(filePath.c_str(), 0, &db));
1275    DbInfo info2;
1276    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_db_info(db, &info2));
1277    ASSERT_EQ(info2.purge_seq, pseq);
1278    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_close_file(db));
1279    ASSERT_EQ(COUCHSTORE_SUCCESS, couchstore_free_db(db));
1280    db = nullptr;
1281}
1282
1283
1284INSTANTIATE_TEST_CASE_P(DocTest,
1285                        CouchstoreDoctest,
1286                        ::testing::Combine(::testing::Bool(), ::testing::Values(4, 69, 666, 4090)),
1287                        [] (const ::testing::TestParamInfo<std::tuple<bool, int>>& info) {
1288                            std::stringstream fmt;
1289                            fmt << ((std::get<0>(info.param))?"Small":"Large")
1290                                << "x" << std::get<1>(info.param);
1291                            return fmt.str();
1292                        });
1293
1294
1295
1296INSTANTIATE_TEST_CASE_P(
1297        MTLatencyCollectTest,
1298        CouchstoreMTTest,
1299        ::testing::Combine(::testing::Values(true, false),
1300                           ::testing::Values(8)),
1301        [] (const ::testing::TestParamInfo<std::tuple<bool, size_t>>& info) {
1302            std::stringstream fmt;
1303            fmt << "collector_"
1304                << ((std::get<0>(info.param))?"enabled":"disabled")
1305                << "_" << std::get<1>(info.param);
1306            return fmt.str();
1307        });
1308
1309
1310int main(int argc, char ** argv)
1311{
1312    ::testing::InitGoogleTest(&argc, argv);
1313    return RUN_ALL_TESTS();
1314}
1315