xref: /6.0.3/couchstore/tests/documents.cc (revision 8de65b7e)
1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2015 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include "documents.h"
19
20#include <algorithm>
21#include <cstring>
22#include <gtest/gtest.h>
23#include <libcouchstore/couch_db.h>
24#include <random>
25
26Documents::Documents(int n_docs)
27  : docs(n_docs),
28    docInfos(n_docs),
29    documents(n_docs),
30    deleted(0),
31    callbacks(0),
32    position(0) {
33}
34
35void Documents::setDoc(int index, const std::string& id, const std::string& data) {
36    documents[index].init(id, data);
37    docs[index] = documents[index].getDocPointer();
38    docInfos[index] = documents[index].getDocInfoPointer();
39}
40
41// shuffle the doc*/docinfo*
42void Documents::shuffle() {
43    // shuffle both arrays using same psudeo-rand input
44    std::mt19937 twister1(10), twister2(10);
45    std::shuffle(docs.begin(), docs.end(), twister1);
46    std::shuffle(docInfos.begin(), docInfos.end(), twister2);
47}
48
49void Documents::generateDocs() {
50    for (size_t ii = 0; ii < documents.size(); ii++) {
51        std::string key = "doc" + std::to_string(ii);
52        std::string data = key + "-data";
53        setDoc(ii, key, data);
54    }
55}
56
57void Documents::setContentMeta(int index, int flag) {
58    documents[index].setContentMeta(flag);
59}
60
61Doc** Documents::getDocs() {
62    return docs.data();
63}
64
65DocInfo** Documents::getDocInfos() {
66    return docInfos.data();
67}
68
69Doc* Documents::getDoc(int index) {
70    return docs[index];
71}
72
73DocInfo* Documents::getDocInfo(int index) {
74    return docInfos[index];
75}
76
77int Documents::getDocsCount() const {
78    return docs.size();
79}
80
81int Documents::getDocInfosCount() const {
82    return docInfos.size();
83}
84
85void Documents::incrementDeleted() {
86    deleted++;
87}
88
89int Documents::getDeleted() const {
90    return deleted;
91}
92
93void Documents::incrementCallbacks() {
94    callbacks++;
95}
96
97int Documents::getCallbacks() const {
98    return callbacks;
99}
100
101void Documents::incrementPosition() {
102    position++;
103}
104
105int Documents::getPosition() const {
106    return position;
107}
108
109void Documents::resetCounters() {
110    deleted = 0;
111    callbacks = 0;
112    position = 0;
113}
114
115void Documents::updateDocumentMap(const std::string& key) {
116    EXPECT_EQ(0ul, documentMap.count(key));
117    documentMap.insert(key);
118}
119
120void Documents::clearDocumentMap() {
121    documentMap.clear();
122}
123
124/**
125    Couchstore callback method that checks the document against
126    the orginal Documents data.
127**/
128int Documents::checkCallback(Db* db, DocInfo* info, void* ctx) {
129    Documents* ds = reinterpret_cast<Documents*>(ctx);
130
131    ds->incrementCallbacks();
132
133    if (info->deleted) {
134        ds->incrementDeleted();
135    }
136
137    Doc* doc = ds->getDoc(ds->getPosition());
138    DocInfo* docInfo = ds->getDocInfo(ds->getPosition());
139
140    EXPECT_EQ(0, std::memcmp(info->id.buf, docInfo->id.buf, info->id.size));
141    EXPECT_EQ(0, std::memcmp(info->rev_meta.buf, docInfo->rev_meta.buf, info->rev_meta.size));
142    EXPECT_EQ(info->id.size, docInfo->id.size);
143    EXPECT_EQ(info->rev_meta.size, info->rev_meta.size);
144
145    Doc* openDoc = nullptr;
146    couchstore_open_doc_with_docinfo(db,
147                                     info,
148                                     &openDoc,
149                                     DECOMPRESS_DOC_BODIES);
150    EXPECT_TRUE(openDoc != nullptr);
151
152    if (openDoc && doc->data.size > 0) {
153        EXPECT_EQ(openDoc->id.size, doc->id.size);
154        EXPECT_EQ(openDoc->data.size, doc->data.size);
155        EXPECT_EQ(0, std::memcmp(doc->id.buf, openDoc->id.buf, openDoc->id.size));
156        EXPECT_EQ(0, std::memcmp(doc->data.buf, openDoc->data.buf, openDoc->data.size));
157    }
158
159    ds->incrementPosition();
160    if (openDoc) {
161        couchstore_free_document(openDoc);
162    }
163    return 0;
164}
165
166/**
167    Couchstore callback method that just counts the number of callbacks.
168**/
169int Documents::countCallback(Db* db, DocInfo* info, void* ctx) {
170    reinterpret_cast<Documents*>(ctx)->incrementCallbacks();
171    return 0;
172}
173
174/**
175    Couchstore callback method that checks the document can be opened.
176        - Also counts callbacks and deleted documents.
177**/
178int Documents::docIterCheckCallback(Db *db, DocInfo *info, void *ctx) {
179    Documents* ds = reinterpret_cast<Documents*>(ctx);
180
181    ds->incrementCallbacks();
182    if (info->deleted) {
183        ds->incrementDeleted();
184    }
185
186    Doc* doc = nullptr;
187    EXPECT_EQ(COUCHSTORE_SUCCESS, couchstore_open_doc_with_docinfo(db,
188                                                                   info,
189                                                                   &doc,
190                                                                   DECOMPRESS_DOC_BODIES));
191
192    EXPECT_TRUE(doc != nullptr);
193
194    if (doc) {
195        couchstore_free_document(doc);
196    }
197    return 0;
198}
199
200/**
201    Couchstore callback that updates a set of document keys.
202        - The update call expects the document to not exist in the set.
203**/
204int Documents::docMapUpdateCallback(Db *db, DocInfo *info, void *ctx) {
205    Documents* ds = reinterpret_cast<Documents*>(ctx);
206    std::string key(info->id.buf, info->id.size);
207    ds->updateDocumentMap(key);
208    return 0;
209}
210
211Documents::Document::Document() {
212    std::memset(&doc, 0, sizeof(Doc));
213    std::memset(&docInfo, 0, sizeof(DocInfo));
214}
215
216Documents::Document::~Document() {
217    documentId.clear();
218    documentData.clear();
219    documentMeta.clear();
220}
221
222/* Init a document */
223void Documents::Document::init(const std::string& id, const std::string& data, const std::vector<char>& meta) {
224    // Copy-in id and data as Doc/DocInfo use raw char* (non const)
225    // This is probably overkill (const cast could work) but this is the safe and more
226    // correct style.
227    documentId.resize(id.length());
228    std::memcpy(documentId.data(), id.c_str(), id.length());
229
230    documentData.resize(data.length());
231    std::copy(data.begin(), data.end(), documentData.begin());
232
233    doc.id.buf = documentId.data();
234    doc.id.size = documentId.size();
235
236    doc.data.buf = documentData.data();
237    doc.data.size = documentData.size();
238
239    documentMeta = meta;
240    docInfo.id.buf = documentId.data();
241    docInfo.id.size = documentId.size();
242    docInfo.rev_meta.buf = documentMeta.data();
243    docInfo.rev_meta.size = documentMeta.size();
244}
245
246/* Init a document with default 'zero' meta */
247void Documents::Document::init(const std::string& id, const std::string& data) {
248    init(id, data, zeroMeta);
249}
250
251void Documents::Document::setContentMeta(int flag) {
252    docInfo.content_meta = flag;
253}
254
255Doc* Documents::Document::getDocPointer() {
256    return &doc;
257}
258
259DocInfo* Documents::Document::getDocInfoPointer() {
260    return &docInfo;
261}
262
263std::vector<char> Documents::Document::zeroMeta = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3};
264