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