1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
3 * Copyright 2015 Couchbase, Inc
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "ep_testsuite_common.h"
19 #include "ep_test_apis.h"
29 #define mkdir(a, b) _mkdir(a)
34 #include <platform/cb_malloc.h>
35 #include <platform/dirutils.h>
37 const char *dbname_env = NULL;
39 static enum test_result skipped_test_function(ENGINE_HANDLE *h,
40 ENGINE_HANDLE_V1 *h1);
42 BaseTestCase::BaseTestCase(const char *_name, const char *_cfg, bool _skip)
48 BaseTestCase::BaseTestCase(const BaseTestCase &o)
53 memset(&test, 0, sizeof(test));
57 TestCase::TestCase(const char *_name,
58 enum test_result(*_tfun)(ENGINE_HANDLE *, ENGINE_HANDLE_V1 *),
59 bool(*_test_setup)(ENGINE_HANDLE *, ENGINE_HANDLE_V1 *),
60 bool(*_test_teardown)(ENGINE_HANDLE *, ENGINE_HANDLE_V1 *),
62 enum test_result (*_prepare)(engine_test_t *test),
63 void (*_cleanup)(engine_test_t *test, enum test_result result),
65 : BaseTestCase(_name, _cfg, _skip) {
67 memset(&test, 0, sizeof(test));
69 test.test_setup = _test_setup;
70 test.test_teardown = _test_teardown;
71 test.prepare = _prepare;
72 test.cleanup = _cleanup;
75 TestCaseV2::TestCaseV2(const char *_name,
76 enum test_result(*_tfun)(engine_test_t *),
77 bool(*_test_setup)(engine_test_t *),
78 bool(*_test_teardown)(engine_test_t *),
80 enum test_result (*_prepare)(engine_test_t *test),
81 void (*_cleanup)(engine_test_t *test, enum test_result result),
83 : BaseTestCase(_name, _cfg, _skip) {
85 memset(&test, 0, sizeof(test));
86 test.api_v2.tfun = _tfun;
87 test.api_v2.test_setup = _test_setup;
88 test.api_v2.test_teardown = _test_teardown;
89 test.prepare = _prepare;
90 test.cleanup = _cleanup;
93 engine_test_t* BaseTestCase::getTest() {
94 engine_test_t *ret = &test;
102 ss << "flushall_enabled=true;";
105 // Default to the suite's dbname if the test config didn't already
107 if ((cfg == nullptr) ||
108 (std::string(cfg).find("dbname=") == std::string::npos)) {
109 ss << "dbname=" << default_dbname << ";";
113 nm.append(" (skipped)");
114 ret->tfun = skipped_test_function;
116 nm.append(" (couchstore)");
119 ret->name = cb_strdup(nm.c_str());
120 std::string config = ss.str();
121 if (config.length() == 0) {
124 ret->cfg = cb_strdup(config.c_str());
131 static enum test_result skipped_test_function(ENGINE_HANDLE *h,
132 ENGINE_HANDLE_V1 *h1) {
138 enum test_result rmdb(const char* path) {
141 } catch (std::system_error& e) {
144 if (access(path, F_OK) != -1) {
145 std::cerr << "Failed to remove: " << path << " " << std::endl;
151 bool test_setup(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
152 wait_for_warmup_complete(h, h1);
154 check(set_vbucket_state(h, h1, 0, vbucket_state_active),
155 "Failed to set VB0 state.");
157 const auto bucket_type = get_str_stat(h, h1, "ep_bucket_type");
158 if (bucket_type == "persistent") {
159 // Wait for vb0's state (active) to be persisted to disk, that way
160 // we know the KVStore files exist on disk.
161 wait_for_stat_to_be_gte(h, h1, "ep_persist_vbstate_total", 1);
162 } else if (bucket_type == "ephemeral") {
163 // No persistence to wait for here.
166 (std::string("test_setup: unknown bucket_type '") + bucket_type +
167 "' - cannot continue.")
172 // warmup is complete, notify ep engine that it must now enable
174 protocol_binary_request_header *pkt = createPacket(PROTOCOL_BINARY_CMD_ENABLE_TRAFFIC);
175 check(h1->unknown_command(h, NULL, pkt, add_response, testHarness.doc_namespace) == ENGINE_SUCCESS,
176 "Failed to enable data traffic");
182 bool teardown(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1) {
188 bool teardown_v2(engine_test_t* test) {
194 std::string get_dbname(const char* test_cfg) {
198 dbname.assign(dbname_env);
202 const char *nm = strstr(test_cfg, "dbname=");
204 dbname.assign(dbname_env);
206 dbname.assign(nm + 7);
207 std::string::size_type end = dbname.find(';');
208 if (end != dbname.npos) {
209 dbname = dbname.substr(0, end);
215 enum test_result prepare(engine_test_t *test) {
217 // Some of the tests doesn't work on Solaris.. Don't know why yet..
218 if (strstr(test->name, "concurrent set") != NULL ||
219 strstr(test->name, "retain rowid over a soft delete") != NULL)
225 std::string dbname = get_dbname(test->cfg);
226 /* Remove if the same DB directory already exists */
228 rmdb(dbname.c_str());
229 } catch (std::system_error& e) {
230 if (e.code() != std::error_code(ENOENT, std::system_category())) {
234 mkdir(dbname.c_str(), 0777);
238 enum test_result prepare_ep_bucket(engine_test_t* test) {
239 std::string cfg{test->cfg};
240 if (cfg.find("bucket_type=ephemeral") != std::string::npos) {
244 // Perform whatever prep the "base class" function wants.
245 return prepare(test);
248 enum test_result prepare_ephemeral_bucket(engine_test_t* test) {
249 std::string cfg{test->cfg};
250 if (cfg.find("bucket_type=ephemeral") == std::string::npos) {
254 // Perform whatever prep the "base class" function wants.
255 return prepare(test);
258 enum test_result prepare_full_eviction(engine_test_t *test) {
259 if (std::string(test->cfg).find("item_eviction_policy=full_eviction")
260 != std::string::npos) {
264 // Ephemeral buckets don't support full eviction.
265 if (std::string(test->cfg).find("bucket_type=ephemeral")
266 != std::string::npos) {
270 // Perform whatever prep the "base class" function wants.
271 return prepare(test);
274 enum test_result prepare_skip_broken_under_ephemeral(engine_test_t *test) {
275 return prepare_ep_bucket(test);
278 enum test_result prepare_tap(engine_test_t* test) {
279 // Ephemeral buckets don't support TAP.
280 if (std::string(test->cfg).find("bucket_type=ephemeral")
281 != std::string::npos) {
285 // Perform whatever prep the "base class" function wants.
286 return prepare(test);
288 void cleanup(engine_test_t *test, enum test_result result) {
290 // Nuke the database files we created
291 std::string dbname = get_dbname(test->cfg);
292 /* Remove only the db file this test created */
294 rmdb(dbname.c_str());
295 } catch (std::system_error& e) {
296 if (e.code() != std::error_code(ENOENT, std::system_category())) {
302 // Array of testcases to return back to engine_testapp.
303 static engine_test_t *testcases;
305 // Should only one test be run, and if so which number? If -1 then all tests
307 static int oneTestIdx;
309 struct test_harness testHarness;
311 // Array of testcases. Provided by the specific testsuite.
312 extern BaseTestCase testsuite_testcases[];
314 // Examines the list of tests provided by the specific testsuite
315 // via the testsuite_testcases[] array, populates `testcases` and returns it.
317 engine_test_t* get_tests(void) {
319 // Calculate the size of the tests..
321 while (testsuite_testcases[num].getName() != NULL) {
326 char *testNum = getenv("EP_TEST_NUM");
328 sscanf(testNum, "%d", &oneTestIdx);
329 if (oneTestIdx < 0 || oneTestIdx > num) {
333 dbname_env = getenv("EP_TEST_DIR");
335 dbname_env = default_dbname;
338 if (oneTestIdx == -1) {
339 testcases = static_cast<engine_test_t*>(cb_calloc(num + 1, sizeof(engine_test_t)));
342 for (int jj = 0; jj < num; ++jj) {
343 engine_test_t *r = testsuite_testcases[jj].getTest();
345 testcases[ii++] = *r;
349 testcases = static_cast<engine_test_t*>(cb_calloc(1 + 1, sizeof(engine_test_t)));
351 engine_test_t *r = testsuite_testcases[oneTestIdx].getTest();
361 bool setup_suite(struct test_harness *th) {
368 bool teardown_suite() {
369 for (int i = 0; testcases[i].name != nullptr; i++) {
370 cb_free((char*)testcases[i].name);
371 cb_free((char*)testcases[i].cfg);
379 * Create n_buckets and return how many were actually created.
381 int create_buckets(const char* cfg, int n_buckets, std::vector<BucketHolder> &buckets) {
382 std::string dbname = get_dbname(cfg);
384 for (int ii = 0; ii < n_buckets; ii++) {
385 std::stringstream config, dbpath;
386 dbpath << dbname.c_str() << ii;
387 std::string str_cfg(cfg);
388 /* Find the position of "dbname=" in str_cfg */
389 size_t pos = str_cfg.find("dbname=");
390 if (pos != std::string::npos) {
391 /* Move till end of the dbname */
392 size_t new_pos = str_cfg.find(';', pos);
393 str_cfg.insert(new_pos, std::to_string(ii));
396 config << str_cfg << "dbname=" << dbpath.str();
400 rmdb(dbpath.str().c_str());
401 } catch (std::system_error& e) {
402 if (e.code() != std::error_code(ENOENT, std::system_category())) {
406 ENGINE_HANDLE_V1* handle = testHarness.create_bucket(true, config.str().c_str());
408 buckets.push_back(BucketHolder((ENGINE_HANDLE*)handle, handle, dbpath.str()));
416 void destroy_buckets(std::vector<BucketHolder> &buckets) {
417 for(auto bucket : buckets) {
418 testHarness.destroy_bucket(bucket.h, bucket.h1, false);
419 rmdb(bucket.dbpath.c_str());
423 void check_key_value(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
424 const char* key, const char* val, size_t vlen,
427 check(get_item_info(h, h1, &info, key, vbucket), "checking key and value");
428 checkeq(vlen, info.value[0].iov_len, "Value length mismatch");
429 check(memcmp(info.value[0].iov_base, val, vlen) == 0, "Data mismatch");
432 const void* createTapConn(ENGINE_HANDLE *h, ENGINE_HANDLE_V1 *h1,
434 const void *cookie = testHarness.create_cookie();
435 testHarness.lock_cookie(cookie);
436 TAP_ITERATOR iter = h1->get_tap_iterator(h, cookie, name,
438 TAP_CONNECT_FLAG_DUMP, NULL,
440 check(iter != NULL, "Failed to create a tap iterator");
444 bool isWarmupEnabled(ENGINE_HANDLE* h, ENGINE_HANDLE_V1* h1) {
445 return get_bool_stat(h, h1, "ep_warmup");
448 bool isPersistentBucket(ENGINE_HANDLE* h, ENGINE_HANDLE_V1* h1) {
449 return get_str_stat(h, h1, "ep_bucket_type") == "persistent";
452 bool isEphemeralBucket(ENGINE_HANDLE* h, ENGINE_HANDLE_V1* h1) {
453 return get_str_stat(h, h1, "ep_bucket_type") == "ephemeral";
456 bool isTapEnabled(ENGINE_HANDLE* h, ENGINE_HANDLE_V1* h1) {
457 return get_bool_stat(h, h1, "ep_tap");