1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2012 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 #include "config.h"
18 #include "iotests.h"
19 #include <map>
20 #include <climits>
21 #include <algorithm>
22 #include "internal.h" /* vbucket_* things from lcb_t */
23 #include "auth-priv.h"
24 #include <lcbio/iotable.h>
25 #include "bucketconfig/bc_http.h"
26 
27 #define LOGARGS(instance, lvl) \
28     instance->settings, "tests-MUT", LCB_LOG_##lvl, __FILE__, __LINE__
29 
30 
31 extern "C" {
timings_callback(lcb_t,const void * cookie,lcb_timeunit_t,lcb_U32,lcb_U32,lcb_U32,lcb_U32)32 static void timings_callback(lcb_t, const void *cookie, lcb_timeunit_t,
33     lcb_U32, lcb_U32, lcb_U32, lcb_U32)
34 {
35     bool *bPtr = (bool *)cookie;
36     *bPtr = true;
37 }
38 }
39 
TEST_F(MockUnitTest,testTimings)40 TEST_F(MockUnitTest, testTimings)
41 {
42     lcb_t instance;
43     HandleWrap hw;
44     bool called = false;
45     createConnection(hw, instance);
46 
47     lcb_enable_timings(instance);
48 
49     lcb_store_cmd_t storecmd(LCB_SET, "counter", 7, "0", 1);
50     lcb_store_cmd_t *storecmds[] = { &storecmd };
51 
52     lcb_store(instance, NULL, 1, storecmds);
53     lcb_wait(instance);
54     lcb_get_timings(instance, &called, timings_callback);
55     lcb_disable_timings(instance);
56     ASSERT_TRUE(called);
57 }
58 
59 
60 namespace {
61 struct TimingInfo {
62     lcb_U64 ns_start;
63     lcb_U64 ns_end;
64     size_t count;
65 
TimingInfo__anona58364290111::TimingInfo66     TimingInfo() : ns_start(-1), ns_end(-1), count(-1) {}
67 
operator <__anona58364290111::TimingInfo68     bool operator<(const TimingInfo& other) const {
69         return other.ns_start > ns_start;
70     }
operator >__anona58364290111::TimingInfo71     bool operator>(const TimingInfo& other) const {
72         return ns_start > other.ns_start;
73     }
74 
valid__anona58364290111::TimingInfo75     bool valid() const {
76         return count != -1;
77     }
78 };
79 
intervalToNsec(lcb_U64 interval,lcb_timeunit_t unit)80 static lcb_U64 intervalToNsec(lcb_U64 interval, lcb_timeunit_t unit)
81 {
82     if (unit == LCB_TIMEUNIT_NSEC) {
83         return interval;
84     } else if (unit == LCB_TIMEUNIT_USEC) {
85         return interval * 1000;
86     } else if (unit == LCB_TIMEUNIT_MSEC) {
87         return interval * 1000000;
88     } else if (unit == LCB_TIMEUNIT_SEC) {
89         return interval * 1000000000;
90     } else {
91         return -1;
92     }
93 }
94 
95 struct LcbTimings {
LcbTimings__anona58364290111::LcbTimings96     LcbTimings() {}
97     std::vector<TimingInfo> m_info;
98     void load(lcb_t);
99     void clear();
100 
101     TimingInfo infoAt(hrtime_t duration, lcb_timeunit_t unit = LCB_TIMEUNIT_NSEC);
countAt__anona58364290111::LcbTimings102     size_t countAt(hrtime_t duration, lcb_timeunit_t unit = LCB_TIMEUNIT_NSEC) {
103         return infoAt(duration, unit).count;
104     }
105 
106     void dump() const;
107 };
108 
109 extern "C" {
load_timings_callback(lcb_t,const void * cookie,lcb_timeunit_t unit,lcb_U32 min,lcb_U32 max,lcb_U32 total,lcb_U32 maxtotal)110 static void load_timings_callback(lcb_t, const void *cookie, lcb_timeunit_t unit,
111     lcb_U32 min, lcb_U32 max, lcb_U32 total, lcb_U32 maxtotal)
112 {
113     lcb_U64 start = intervalToNsec(min, unit);
114     lcb_U64 end = intervalToNsec(max, unit);
115     LcbTimings *timings = (LcbTimings *)cookie;
116     TimingInfo info;
117 
118     info.ns_start = start;
119     info.ns_end = end;
120     info.count = total;
121     timings->m_info.push_back(info);
122 }
123 } // extern "C"
124 
125 void
load(lcb_t instance)126 LcbTimings::load(lcb_t instance)
127 {
128     lcb_get_timings(instance, this, load_timings_callback);
129     std::sort(m_info.begin(), m_info.end());
130 }
131 
132 TimingInfo
infoAt(hrtime_t duration,lcb_timeunit_t unit)133 LcbTimings::infoAt(hrtime_t duration, lcb_timeunit_t unit)
134 {
135     duration = intervalToNsec(duration, unit);
136     std::vector<TimingInfo>::iterator ii;
137     for (ii = m_info.begin(); ii != m_info.end(); ++ii) {
138         if (ii->ns_start <= duration && ii->ns_end > duration) {
139             return *ii;
140         }
141     }
142     return TimingInfo();
143 }
144 
145 void
dump() const146 LcbTimings::dump() const
147 {
148     std::vector<TimingInfo>::const_iterator ii = m_info.begin();
149     for (; ii != m_info.end(); ii++) {
150         if (ii->ns_end < 1000) {
151             printf("[%llu-%llu ns] %lu\n",
152                 ii->ns_start, ii->ns_end, ii->count);
153         } else if (ii->ns_end < 10000000) {
154             printf("[%llu-%llu us] %lu\n",
155                 ii->ns_start / 1000, ii->ns_end / 1000, ii->count);
156         } else {
157             printf("[%llu-%llu ms] %lu\n",
158                 ii->ns_start / 1000000, ii->ns_end / 1000000, ii->count);
159         }
160     }
161 }
162 
163 } // namespace{
164 
165 struct UnitInterval {
166     lcb_U64 n;
167     lcb_timeunit_t unit;
UnitIntervalUnitInterval168     UnitInterval(lcb_U64 n, lcb_timeunit_t unit) : n(n), unit(unit) {}
169 };
170 
addTiming(lcb_t instance,const UnitInterval & interval)171 static void addTiming(lcb_t instance, const UnitInterval& interval)
172 {
173     hrtime_t n = intervalToNsec(interval.n, interval.unit);
174     lcb_histogram_record(instance->kv_timings, n);
175 }
176 
177 
TEST_F(MockUnitTest,testTimingsEx)178 TEST_F(MockUnitTest, testTimingsEx)
179 {
180     lcb_t instance;
181     HandleWrap hw;
182 
183     createConnection(hw, instance);
184     lcb_disable_timings(instance);
185     lcb_enable_timings(instance);
186 
187     std::vector<UnitInterval> intervals;
188     intervals.push_back(UnitInterval(1, LCB_TIMEUNIT_NSEC));
189     intervals.push_back(UnitInterval(250, LCB_TIMEUNIT_NSEC));
190     intervals.push_back(UnitInterval(4, LCB_TIMEUNIT_USEC));
191     intervals.push_back(UnitInterval(32, LCB_TIMEUNIT_USEC));
192     intervals.push_back(UnitInterval(942, LCB_TIMEUNIT_USEC));
193     intervals.push_back(UnitInterval(1243, LCB_TIMEUNIT_USEC));
194     intervals.push_back(UnitInterval(1732, LCB_TIMEUNIT_USEC));
195     intervals.push_back(UnitInterval(5630, LCB_TIMEUNIT_USEC));
196     intervals.push_back(UnitInterval(42, LCB_TIMEUNIT_MSEC));
197     intervals.push_back(UnitInterval(434, LCB_TIMEUNIT_MSEC));
198 
199     intervals.push_back(UnitInterval(8234, LCB_TIMEUNIT_MSEC));
200     intervals.push_back(UnitInterval(1294, LCB_TIMEUNIT_MSEC));
201     intervals.push_back(UnitInterval(48, LCB_TIMEUNIT_SEC));
202 
203     for (size_t ii = 0; ii < intervals.size(); ++ii) {
204         addTiming(instance, intervals[ii]);
205     }
206 
207     // Ensure they all exist, at least. Currently we bundle everything
208     LcbTimings timings;
209     timings.load(instance);
210 
211     //timings.dump();
212 
213     // Measuring in < us
214     ASSERT_EQ(2, timings.countAt(50, LCB_TIMEUNIT_NSEC));
215 
216     ASSERT_EQ(1, timings.countAt(4, LCB_TIMEUNIT_USEC));
217     ASSERT_EQ(1, timings.countAt(30, LCB_TIMEUNIT_USEC));
218     ASSERT_EQ(-1, timings.countAt(900, LCB_TIMEUNIT_USEC));
219     ASSERT_EQ(1, timings.countAt(940, LCB_TIMEUNIT_USEC));
220     ASSERT_EQ(1, timings.countAt(1200, LCB_TIMEUNIT_USEC));
221     ASSERT_EQ(1, timings.countAt(1250, LCB_TIMEUNIT_USEC));
222     ASSERT_EQ(1, timings.countAt(5600, LCB_TIMEUNIT_USEC));
223     ASSERT_EQ(1, timings.countAt(40, LCB_TIMEUNIT_MSEC));
224     ASSERT_EQ(1, timings.countAt(430, LCB_TIMEUNIT_MSEC));
225     ASSERT_EQ(1, timings.countAt(1, LCB_TIMEUNIT_SEC));
226     ASSERT_EQ(1, timings.countAt(8, LCB_TIMEUNIT_SEC));
227     ASSERT_EQ(1, timings.countAt(93, LCB_TIMEUNIT_SEC));
228 }
229 
230 
231 struct async_ctx {
232     int count;
233     lcbio_pTABLE table;
234 };
235 
236 extern "C" {
dtor_callback(const void * cookie)237 static void dtor_callback(const void *cookie)
238 {
239     async_ctx *ctx = (async_ctx *)cookie;
240     ctx->count++;
241     IOT_STOP(ctx->table);
242 }
243 }
244 
TEST_F(MockUnitTest,testAsyncDestroy)245 TEST_F(MockUnitTest, testAsyncDestroy)
246 {
247     lcb_t instance;
248     createConnection(instance);
249     lcbio_pTABLE iot = instance->iotable;
250     lcb_settings *settings = instance->settings;
251 
252     storeKey(instance, "foo", "bar");
253     // Now destroy the instance
254     async_ctx ctx;
255     ctx.count = 0;
256     ctx.table = iot;
257     lcb_set_destroy_callback(instance, dtor_callback);
258     lcb_destroy_async(instance, &ctx);
259     lcb_settings_ref(settings);
260     lcbio_table_ref(iot);
261     lcb_run_loop(instance);
262     lcb_settings_unref(settings);
263     lcbio_table_unref(iot);
264     ASSERT_EQ(1, ctx.count);
265 }
266 
TEST_F(MockUnitTest,testGetHostInfo)267 TEST_F(MockUnitTest, testGetHostInfo)
268 {
269     lcb_t instance;
270     createConnection(instance);
271     lcb_config_transport_t tx;
272     const char *hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG, 0);
273     ASSERT_FALSE(hoststr == NULL);
274 
275     hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG_CONNECTED, 0);
276     lcb_error_t err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_CONFIG_TRANSPORT, &tx);
277 
278     ASSERT_EQ(LCB_SUCCESS, err);
279     if (tx == LCB_CONFIG_TRANSPORT_HTTP) {
280         ASSERT_FALSE(hoststr == NULL);
281         hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG_CONNECTED, 99);
282         ASSERT_FALSE(hoststr == NULL);
283     } else {
284         if (hoststr) {
285             printf("%s\n", hoststr);
286         }
287         ASSERT_TRUE(hoststr == NULL);
288     }
289 
290     // Get any data node
291     using std::map;
292     using std::string;
293     map<string,bool> smap;
294 
295     // Ensure we only get unique nodes
296     for (lcb_S32 ii = 0; ii < lcb_get_num_nodes(instance); ii++) {
297         const char *cur = lcb_get_node(instance, LCB_NODE_DATA, ii);
298         ASSERT_FALSE(cur == NULL);
299         ASSERT_FALSE(smap[cur]);
300         smap[cur] = true;
301     }
302     lcb_destroy(instance);
303 
304     // Try with no connection
305     err = lcb_create(&instance, NULL);
306     ASSERT_EQ(LCB_SUCCESS, err);
307 
308     hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG_CONNECTED, 0);
309     ASSERT_TRUE(NULL == hoststr);
310 
311     hoststr = lcb_get_node(instance, LCB_NODE_HTCONFIG, 0);
312     ASSERT_TRUE(NULL == hoststr);
313 
314 
315 
316     // These older API functions are special as they should never return NULL
317     hoststr = lcb_get_host(instance);
318     ASSERT_FALSE(hoststr == NULL);
319     ASSERT_STREQ("localhost", hoststr);
320 
321     hoststr = lcb_get_port(instance);
322     ASSERT_FALSE(hoststr == NULL);
323     ASSERT_STREQ("8091", hoststr);
324 
325     lcb_destroy(instance);
326 }
327 
TEST_F(MockUnitTest,testEmptyKeys)328 TEST_F(MockUnitTest, testEmptyKeys)
329 {
330     lcb_t instance;
331     HandleWrap hw;
332     createConnection(hw, instance);
333 
334     union {
335         lcb_CMDGET get;
336         lcb_CMDSTORE store;
337         lcb_CMDCOUNTER counter;
338         lcb_CMDENDURE endure;
339         lcb_CMDOBSERVE observe;
340         lcb_CMDTOUCH touch;
341         lcb_CMDUNLOCK unlock;
342         lcb_CMDGETREPLICA rget;
343         lcb_CMDBASE base;
344         lcb_CMDSTATS stats;
345     } u;
346     memset(&u, 0, sizeof u);
347 
348     lcb_sched_enter(instance);
349 
350     ASSERT_EQ(LCB_EMPTY_KEY, lcb_get3(instance, NULL, &u.get));
351     ASSERT_EQ(LCB_EMPTY_KEY, lcb_store3(instance, NULL, &u.store));
352     ASSERT_EQ(LCB_EMPTY_KEY, lcb_counter3(instance, NULL, &u.counter));
353     ASSERT_EQ(LCB_EMPTY_KEY, lcb_touch3(instance, NULL, &u.touch));
354     ASSERT_EQ(LCB_EMPTY_KEY, lcb_unlock3(instance, NULL, &u.unlock));
355     ASSERT_EQ(LCB_EMPTY_KEY, lcb_rget3(instance, NULL, &u.rget));
356 
357     // Observe and such
358     lcb_MULTICMD_CTX *ctx = lcb_observe3_ctxnew(instance);
359     ASSERT_EQ(LCB_EMPTY_KEY, ctx->addcmd(ctx, (lcb_CMDBASE*)&u.observe));
360     ctx->fail(ctx);
361 
362     lcb_durability_opts_t dopts;
363     memset(&dopts, 0, sizeof dopts);
364     dopts.v.v0.persist_to = 1;
365 
366     ctx = lcb_endure3_ctxnew(instance, &dopts, NULL);
367     ASSERT_TRUE(ctx != NULL);
368     ASSERT_EQ(LCB_EMPTY_KEY, ctx->addcmd(ctx, (lcb_CMDBASE*)&u.endure));
369     ctx->fail(ctx);
370 
371     ASSERT_EQ(LCB_SUCCESS, lcb_stats3(instance, NULL, &u.stats));
372     lcb_sched_fail(instance);
373 }
374 
375 template <typename T>
ctlSet(lcb_t instance,int setting,T val)376 static bool ctlSet(lcb_t instance, int setting, T val)
377 {
378     lcb_error_t err = lcb_cntl(instance, LCB_CNTL_SET, setting, &val);
379     return err == LCB_SUCCESS;
380 }
381 
382 template<>
ctlSet(lcb_t instance,int setting,const char * val)383 bool ctlSet<const char*>(lcb_t instance, int setting, const char *val)
384 {
385     return lcb_cntl(instance, LCB_CNTL_SET, setting, (void*)val) == LCB_SUCCESS;
386 }
387 
388 template <typename T>
ctlGet(lcb_t instance,int setting)389 static T ctlGet(lcb_t instance, int setting)
390 {
391     T tmp;
392     lcb_error_t err = lcb_cntl(instance, LCB_CNTL_GET, setting, &tmp);
393     EXPECT_EQ(LCB_SUCCESS, err);
394     return tmp;
395 }
396 template <typename T>
ctlGetSet(lcb_t instance,int setting,T val)397 static void ctlGetSet(lcb_t instance, int setting, T val) {
398     EXPECT_TRUE(ctlSet<T>(instance, setting, val));
399     EXPECT_EQ(val, ctlGet<T>(instance, setting));
400 }
401 
402 template <>
ctlGetSet(lcb_t instance,int setting,const char * val)403 void ctlGetSet<const char*>(lcb_t instance, int setting, const char *val)
404 {
405     EXPECT_TRUE(ctlSet<const char*>(instance, setting, val));
406     EXPECT_STREQ(val, ctlGet<const char*>(instance, setting));
407 }
408 
ctlSetInt(lcb_t instance,int setting,int val)409 static bool ctlSetInt(lcb_t instance, int setting, int val) {
410     return ctlSet<int>(instance, setting, val);
411 }
ctlGetInt(lcb_t instance,int setting)412 static int ctlGetInt(lcb_t instance, int setting) {
413     return ctlGet<int>(instance, setting);
414 }
ctlSetU32(lcb_t instance,int setting,lcb_U32 val)415 static bool ctlSetU32(lcb_t instance, int setting, lcb_U32 val) {
416     return ctlSet<lcb_U32>(instance, setting, val);
417 }
ctlGetU32(lcb_t instance,int setting)418 static lcb_U32 ctlGetU32(lcb_t instance, int setting) {
419     return ctlGet<lcb_U32>(instance, setting);
420 }
421 
TEST_F(MockUnitTest,testCtls)422 TEST_F(MockUnitTest, testCtls)
423 {
424     lcb_t instance;
425     HandleWrap hw;
426     lcb_error_t err;
427     createConnection(hw, instance);
428 
429     ctlGetSet<lcb_U32>(instance, LCB_CNTL_OP_TIMEOUT, UINT_MAX);
430     ctlGetSet<lcb_U32>(instance, LCB_CNTL_VIEW_TIMEOUT, UINT_MAX);
431 
432     ASSERT_EQ(LCB_TYPE_BUCKET, ctlGet<lcb_type_t>(instance, LCB_CNTL_HANDLETYPE));
433     ASSERT_FALSE(ctlSet<lcb_type_t>(instance, LCB_CNTL_HANDLETYPE, LCB_TYPE_BUCKET));
434 
435     lcbvb_CONFIG *cfg = ctlGet<lcbvb_CONFIG*>(instance, LCB_CNTL_VBCONFIG);
436     // Do we have a way to verify this?
437     ASSERT_FALSE(cfg == NULL);
438     ASSERT_GT(cfg->nsrv, (unsigned int)0);
439 
440     lcb_io_opt_t io = ctlGet<lcb_io_opt_t>(instance, LCB_CNTL_IOPS);
441     ASSERT_TRUE(io == instance->getIOT()->p);
442     // Try to set it?
443     ASSERT_FALSE(ctlSet<lcb_io_opt_t>(instance, LCB_CNTL_IOPS, (lcb_io_opt_t)"Hello"));
444 
445     // Map a key
446     lcb_cntl_vbinfo_t vbi = { 0 };
447     vbi.v.v0.key = "123";
448     vbi.v.v0.nkey = 3;
449     err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_VBMAP, &vbi);
450     ASSERT_EQ(LCB_SUCCESS, err);
451 
452     // Try to modify it?
453     err = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_VBMAP, &vbi);
454     ASSERT_NE(LCB_SUCCESS, err);
455 
456     ctlGetSet<lcb_ipv6_t>(instance, LCB_CNTL_IP6POLICY, LCB_IPV6_DISABLED);
457     ctlGetSet<lcb_ipv6_t>(instance, LCB_CNTL_IP6POLICY, LCB_IPV6_ONLY);
458     ctlGetSet<lcb_SIZE>(instance, LCB_CNTL_CONFERRTHRESH, UINT_MAX);
459     ctlGetSet<lcb_U32>(instance, LCB_CNTL_DURABILITY_TIMEOUT, UINT_MAX);
460     ctlGetSet<lcb_U32>(instance, LCB_CNTL_DURABILITY_INTERVAL, UINT_MAX);
461     ctlGetSet<lcb_U32>(instance, LCB_CNTL_HTTP_TIMEOUT, UINT_MAX);
462     ctlGetSet<int>(instance, LCB_CNTL_IOPS_DLOPEN_DEBUG, 55);
463     ctlGetSet<lcb_U32>(instance, LCB_CNTL_CONFIGURATION_TIMEOUT, UINT_MAX);
464 
465     ctlGetSet<int>(instance, LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS, 1);
466     ctlGetSet<int>(instance, LCB_CNTL_RANDOMIZE_BOOTSTRAP_HOSTS, 0);
467 
468     ASSERT_EQ(0, ctlGetInt(instance, LCB_CNTL_CONFIG_CACHE_LOADED));
469     ASSERT_FALSE(ctlSetInt(instance, LCB_CNTL_CONFIG_CACHE_LOADED, 99));
470 
471     ctlGetSet<const char*>(instance, LCB_CNTL_FORCE_SASL_MECH, "SECRET");
472 
473     ctlGetSet<int>(instance, LCB_CNTL_MAX_REDIRECTS, SHRT_MAX);
474     ctlGetSet<int>(instance, LCB_CNTL_MAX_REDIRECTS, -1);
475     ctlGetSet<int>(instance, LCB_CNTL_MAX_REDIRECTS, 0);
476 
477     // LCB_CNTL_LOGGER handled in other tests
478 
479     ctlGetSet<lcb_U32>(instance, LCB_CNTL_CONFDELAY_THRESH, UINT_MAX);
480 
481     // CONFIG_TRANSPORT. Test that we shouldn't be able to set it
482     ASSERT_FALSE(ctlSet<lcb_config_transport_t>(
483         instance, LCB_CNTL_CONFIG_TRANSPORT, LCB_CONFIG_TRANSPORT_LIST_END));
484 
485     ctlGetSet<lcb_U32>(instance, LCB_CNTL_CONFIG_NODE_TIMEOUT, UINT_MAX);
486     ctlGetSet<lcb_U32>(instance, LCB_CNTL_HTCONFIG_IDLE_TIMEOUT, UINT_MAX);
487 
488     ASSERT_FALSE(ctlSet<const char*>(instance, LCB_CNTL_CHANGESET, "deadbeef"));
489     ASSERT_FALSE(ctlGet<const char*>(instance, LCB_CNTL_CHANGESET) == NULL);
490     ctlGetSet<const char*>(instance, LCB_CNTL_CONFIGCACHE, "/foo/bar/baz");
491     ASSERT_FALSE(ctlSetInt(instance, LCB_CNTL_SSL_MODE, 90));
492     ASSERT_GE(ctlGetInt(instance, LCB_CNTL_SSL_MODE), 0);
493     ASSERT_FALSE(ctlSet<const char*>(instance, LCB_CNTL_SSL_CACERT, "/tmp"));
494 
495     lcb_U32 ro_in, ro_out;
496     ro_in = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_SOCKERR, LCB_RETRY_CMDS_GET);
497     ASSERT_TRUE(ctlSet<lcb_U32>(instance, LCB_CNTL_RETRYMODE, ro_in));
498 
499     ro_out = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_SOCKERR, 0);
500     err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_RETRYMODE, &ro_out);
501     ASSERT_EQ(LCB_SUCCESS, err);
502     ASSERT_EQ(LCB_RETRY_CMDS_GET, LCB_RETRYOPT_GETPOLICY(ro_out));
503 
504     ASSERT_EQ(LCB_SUCCESS, lcb_cntl_string(instance, "retry_policy", "topochange:get"));
505     ro_out = LCB_RETRYOPT_CREATE(LCB_RETRY_ON_TOPOCHANGE, 0);
506     err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_RETRYMODE, &ro_out);
507     ASSERT_EQ(LCB_RETRY_CMDS_GET, LCB_RETRYOPT_GETPOLICY(ro_out));
508 
509 
510     ctlGetSet<int>(instance, LCB_CNTL_HTCONFIG_URLTYPE, LCB_HTCONFIG_URLTYPE_COMPAT);
511     ctlGetSet<int>(instance, LCB_CNTL_COMPRESSION_OPTS, LCB_COMPRESS_FORCE);
512 
513     ctlSetU32(instance, LCB_CNTL_CONLOGGER_LEVEL, 3);
514     lcb_U32 tmp;
515     err = lcb_cntl(instance, LCB_CNTL_GET, LCB_CNTL_CONLOGGER_LEVEL, &tmp);
516     ASSERT_NE(LCB_SUCCESS, err);
517 
518     ctlGetSet<int>(instance, LCB_CNTL_DETAILED_ERRCODES, 1);
519     ctlGetSet<lcb_U32>(instance, LCB_CNTL_RETRY_INTERVAL, UINT_MAX);
520     ctlGetSet<float>(instance, LCB_CNTL_RETRY_BACKOFF, (float)3.4);
521     ctlGetSet<lcb_SIZE>(instance, LCB_CNTL_HTTP_POOLSIZE, UINT_MAX);
522     ctlGetSet<int>(instance, LCB_CNTL_HTTP_REFRESH_CONFIG_ON_ERROR, 0);
523 
524     // Allow timeouts to be expressed as fractional seconds.
525     err = lcb_cntl_string(instance, "operation_timeout", "1.0");
526     ASSERT_EQ(LCB_SUCCESS, err);
527     ASSERT_EQ(1000000, ctlGet<lcb_U32>(instance, LCB_CNTL_OP_TIMEOUT));
528     err = lcb_cntl_string(instance, "operation_timeout", "0.255");
529     ASSERT_EQ(LCB_SUCCESS, err);
530     ASSERT_EQ(255000, ctlGet<lcb_U32>(instance, LCB_CNTL_OP_TIMEOUT));
531 
532     // Test default for nmv retry
533     int itmp = ctlGetInt(instance, LCB_CNTL_RETRY_NMV_IMM);
534     ASSERT_NE(0, itmp);
535 
536     err = lcb_cntl_string(instance, "retry_nmv_imm", "0");
537     ASSERT_EQ(LCB_SUCCESS, err);
538     itmp = ctlGetInt(instance, LCB_CNTL_RETRY_NMV_IMM);
539     ASSERT_EQ(0, itmp);
540 }
541 
TEST_F(MockUnitTest,testConflictingOptions)542 TEST_F(MockUnitTest, testConflictingOptions)
543 {
544     HandleWrap hw;
545     lcb_t instance;
546     createConnection(hw, instance);
547 
548     lcb_sched_enter(instance);
549     const char *key = "key";
550     size_t nkey = 3;
551     const char *value = "value";
552     size_t nvalue = 5;
553 
554     lcb_CMDSTORE scmd = { 0 };
555     scmd.operation = LCB_APPEND;
556     scmd.exptime = 1;
557     LCB_CMD_SET_KEY(&scmd, key, nkey);
558     LCB_CMD_SET_VALUE(&scmd, value, nvalue);
559 
560     lcb_error_t err;
561     err = lcb_store3(instance, NULL, &scmd);
562     ASSERT_EQ(LCB_OPTIONS_CONFLICT, err);
563     scmd.exptime = 0;
564     scmd.flags = 99;
565     err = lcb_store3(instance, NULL, &scmd);
566     ASSERT_EQ(LCB_OPTIONS_CONFLICT, err);
567 
568     scmd.flags = 0;
569     scmd.exptime = 0;
570     err = lcb_store3(instance, NULL, &scmd);
571     ASSERT_EQ(LCB_SUCCESS, err);
572 
573     scmd.operation = LCB_ADD;
574     scmd.cas = 0xdeadbeef;
575     err = lcb_store3(instance, NULL, &scmd);
576     ASSERT_EQ(LCB_OPTIONS_CONFLICT, err);
577 
578     scmd.cas = 0;
579     err = lcb_store3(instance, NULL, &scmd);
580     ASSERT_EQ(LCB_SUCCESS, err);
581 
582     lcb_CMDCOUNTER ccmd = { 0 };
583     LCB_CMD_SET_KEY(&ccmd, key, nkey);
584     ccmd.cas = 0xdeadbeef;
585     err = lcb_counter3(instance, NULL, &ccmd);
586     ASSERT_EQ(LCB_OPTIONS_CONFLICT, err);
587     ccmd.cas = 0;
588     err = lcb_counter3(instance, NULL, &ccmd);
589     ASSERT_EQ(LCB_SUCCESS, err);
590 
591     ccmd.exptime = 10;
592     ccmd.initial = 0;
593     ccmd.create = 0;
594     err = lcb_counter3(instance, NULL, &ccmd);
595     ASSERT_EQ(LCB_OPTIONS_CONFLICT, err);
596     ccmd.create = 1;
597     err = lcb_counter3(instance, NULL, &ccmd);
598     ASSERT_EQ(LCB_SUCCESS, err);
599 
600     lcb_CMDGET gcmd = { 0 };
601     LCB_CMD_SET_KEY(&gcmd, key, nkey);
602     gcmd.cas = 0xdeadbeef;
603     err = lcb_get3(instance, NULL, &gcmd);
604     ASSERT_EQ(LCB_OPTIONS_CONFLICT, err);
605 
606     gcmd.cas = 0;
607     err = lcb_get3(instance, NULL, &gcmd);
608     ASSERT_EQ(LCB_SUCCESS, err);
609     lcb_sched_fail(instance);
610 }
611 
TEST_F(MockUnitTest,testDump)612 TEST_F(MockUnitTest, testDump)
613 {
614     const char *fpname;
615 #ifdef _WIN32
616     fpname = "NUL:";
617 #else
618     fpname = "/dev/null";
619 #endif
620     FILE *fp = fopen(fpname, "w");
621     if (!fp) {
622         perror(fpname);
623         return;
624     }
625 
626     // Simply try to dump the instance;
627     HandleWrap hw;
628     lcb_t instance;
629     createConnection(hw, instance);
630     std::vector<std::string> keys;
631     genDistKeys(LCBT_VBCONFIG(instance), keys);
632     for (size_t ii = 0; ii < keys.size(); ii++) {
633         storeKey(instance, keys[ii], keys[ii]);
634     }
635     lcb_dump(instance, fp, LCB_DUMP_ALL);
636     fclose(fp);
637 }
638 
TEST_F(MockUnitTest,testRefreshConfig)639 TEST_F(MockUnitTest, testRefreshConfig)
640 {
641     SKIP_UNLESS_MOCK();
642     HandleWrap hw;
643     lcb_t instance;
644     createConnection(hw, instance);
645     lcb_refresh_config(instance);
646     lcb_wait3(instance, LCB_WAIT_NOCHECK);
647 }
648 
649 extern "C" {
tickOpCb(lcb_t,int,const lcb_RESPBASE * rb)650 static void tickOpCb(lcb_t, int, const lcb_RESPBASE *rb)
651 {
652     int *p = (int *)rb->cookie;
653     *p -= 1;
654     EXPECT_EQ(LCB_SUCCESS, rb->rc);
655 }
656 }
657 
TEST_F(MockUnitTest,testTickLoop)658 TEST_F(MockUnitTest, testTickLoop)
659 {
660     HandleWrap hw;
661     lcb_t instance;
662     lcb_error_t err;
663     createConnection(hw, instance);
664 
665     const char *key = "tickKey";
666     const char *value = "tickValue";
667 
668     lcb_install_callback3(instance, LCB_CALLBACK_STORE, tickOpCb);
669     lcb_CMDSTORE cmd = { 0 };
670     cmd.operation = LCB_SET;
671     LCB_CMD_SET_KEY(&cmd, key, strlen(key));
672     LCB_CMD_SET_VALUE(&cmd, value, strlen(value));
673 
674     err = lcb_tick_nowait(instance);
675     if (err == LCB_CLIENT_FEATURE_UNAVAILABLE) {
676         fprintf(stderr, "Current event loop does not support tick!");
677         return;
678     }
679 
680     lcb_sched_enter(instance);
681     int counter = 0;
682     for (int ii = 0; ii < 10; ii++) {
683         err = lcb_store3(instance, &counter, &cmd);
684         ASSERT_EQ(LCB_SUCCESS, err);
685         counter++;
686     }
687 
688     lcb_sched_leave(instance);
689     while (counter) {
690         lcb_tick_nowait(instance);
691     }
692 }
693 
TEST_F(MockUnitTest,testEmptyCtx)694 TEST_F(MockUnitTest, testEmptyCtx)
695 {
696     HandleWrap hw;
697     lcb_t instance;
698     lcb_error_t err = LCB_SUCCESS;
699     createConnection(hw, instance);
700 
701     lcb_MULTICMD_CTX *mctx;
702     lcb_durability_opts_t duropts = { 0 };
703     duropts.v.v0.persist_to = 1;
704     mctx = lcb_endure3_ctxnew(instance, &duropts, &err);
705     ASSERT_EQ(LCB_SUCCESS, err);
706     ASSERT_FALSE(mctx == NULL);
707 
708     err = mctx->done(mctx, NULL);
709     ASSERT_NE(LCB_SUCCESS, err);
710 
711     mctx = lcb_observe3_ctxnew(instance);
712     ASSERT_FALSE(mctx == NULL);
713     err = mctx->done(mctx, NULL);
714     ASSERT_NE(LCB_SUCCESS, err);
715 }
716 
TEST_F(MockUnitTest,testMultiCreds)717 TEST_F(MockUnitTest, testMultiCreds)
718 {
719     SKIP_IF_CLUSTER_VERSION_IS_HIGHER_THAN(MockEnvironment::VERSION_50);
720     using lcb::Authenticator;
721 
722     HandleWrap hw;
723     lcb_t instance;
724     createConnection(hw, instance);
725 
726     lcb_BUCKETCRED cred;
727     cred[0] = "protected";
728     cred[1] = "secret";
729     lcb_error_t rc = lcb_cntl(instance, LCB_CNTL_SET, LCB_CNTL_BUCKET_CRED, cred);
730     ASSERT_EQ(LCB_SUCCESS, rc);
731     Authenticator& auth = *instance->settings->auth;
732     lcb::Authenticator::Map::const_iterator res = auth.buckets().find("protected");
733     ASSERT_NE(auth.buckets().end(), res);
734     ASSERT_EQ("secret", res->second);
735 }
736 
737 extern "C" {
appendE2BIGcb(lcb_t,int,const lcb_RESPBASE * rb)738 static void appendE2BIGcb(lcb_t, int, const lcb_RESPBASE *rb)
739 {
740     lcb_error_t *e = (lcb_error_t *)rb->cookie;
741     *e = rb->rc;
742 }
743 }
744 
TEST_F(MockUnitTest,testAppendE2BIG)745 TEST_F(MockUnitTest, testAppendE2BIG)
746 {
747     HandleWrap hw;
748     lcb_t instance;
749     createConnection(hw, instance);
750     lcb_install_callback3(instance, LCB_CALLBACK_STORE, appendE2BIGcb);
751 
752     lcb_error_t err, res;
753 
754     const char *key = "key";
755     size_t nkey = strlen(key);
756 
757     size_t nvalue1 = 20 * 1024 * 1024;
758     void *value1 = calloc(nvalue1, sizeof(char));
759     lcb_CMDSTORE scmd = { 0 };
760     scmd.operation = LCB_SET;
761     LCB_CMD_SET_KEY(&scmd, key, nkey);
762     LCB_CMD_SET_VALUE(&scmd, value1, nvalue1);
763     err = lcb_store3(instance, &res, &scmd);
764     lcb_wait(instance);
765     ASSERT_EQ(LCB_SUCCESS, res);
766     free(value1);
767 
768     size_t nvalue2 = 1 * 1024 * 1024;
769     void *value2 = calloc(nvalue2, sizeof(char));
770     lcb_CMDSTORE acmd = { 0 };
771     acmd.operation = LCB_APPEND;
772     LCB_CMD_SET_KEY(&acmd, key, nkey);
773     LCB_CMD_SET_VALUE(&acmd, value2, nvalue2);
774     err = lcb_store3(instance, &res, &acmd);
775     lcb_wait(instance);
776     ASSERT_EQ(LCB_E2BIG, res);
777     free(value2);
778 }
779