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 #pragma once
19 
20 #include <memcached/engine.h>
21 #include <memcached/engine_testapp.h>
22 
23 #include <sstream>
24 #include <string>
25 #include <vector>
26 
27 #include "ep_test_apis.h"
28 
29 #ifdef __cplusplus
30 extern "C" {
31 #endif
32 
33 /* API required by engine_testapp to be able to drive a testsuite. */
34 MEMCACHED_PUBLIC_API
35 engine_test_t* get_tests(void);
36 
37 MEMCACHED_PUBLIC_API
38 bool setup_suite(struct test_harness *th);
39 
40 MEMCACHED_PUBLIC_API
41 bool teardown_suite(void);
42 
43 #ifdef __cplusplus
44 }
45 #endif
46 
47 template <typename T>
checkeqfn(T exp, T got, const std::string& msg, const char* file, const int linenum)48 static void checkeqfn(T exp,
49                       T got,
50                       const std::string& msg,
51                       const char* file,
52                       const int linenum) {
53     if (exp != got) {
54         std::stringstream ss;
55         ss << "Expected `" << exp << "', got `" << got << "' - " << msg;
56         abort_msg(ss.str().c_str(), "", file, linenum);
57     }
58 }
59 
60 template <typename T>
checknefn(T exp, T got, const std::string& msg, const char* file, const int linenum)61 static void checknefn(T exp,
62                       T got,
63                       const std::string& msg,
64                       const char* file,
65                       const int linenum) {
66     if (exp == got) {
67         std::stringstream ss;
68         ss << "Expected `" << exp << "' to not equal `" << got << "' - " << msg;
69         abort_msg(ss.str().c_str(), "", file, linenum);
70     }
71 }
72 
73 template <typename T>
checklefn(T exp, T got, const std::string& msg, const char* file, const int linenum)74 static void checklefn(T exp,
75                       T got,
76                       const std::string& msg,
77                       const char* file,
78                       const int linenum) {
79     if (exp > got) {
80         std::stringstream ss;
81         ss << "Expected `" << exp << "' to be less than or equal to `" << got
82            << "' - " << msg;
83         abort_msg(ss.str().c_str(), "", file, linenum);
84     }
85 }
86 
87 template <typename T>
checkltfn(T exp, T got, const char *msg, const char *file, const int linenum)88 static void checkltfn(T exp, T got, const char *msg, const char *file,
89                       const int linenum) {
90     if (exp >= got) {
91         std::stringstream ss;
92         ss << "Expected `" << exp << "' to be less than `" << got
93            << "' - " << msg;
94         abort_msg(ss.str().c_str(), "", file, linenum);
95     }
96 }
97 
98 template <typename T>
checkgefn(T exp, T got, const char *msg, const char *file, const int linenum)99 static void checkgefn(T exp, T got, const char *msg, const char *file,
100                       const int linenum) {
101     if (exp < got) {
102         std::stringstream ss;
103         ss << "Expected `" << exp << "' to be greater than or equal to `" << got
104            << "' - " << msg;
105         abort_msg(ss.str().c_str(), "", file, linenum);
106     }
107 }
108 
109 template <typename T>
checkgtfn(T exp, T got, const char *msg, const char *file, const int linenum)110 static void checkgtfn(T exp, T got, const char *msg, const char *file,
111                       const int linenum) {
112     if (exp <= got) {
113         std::stringstream ss;
114         ss << "Expected `" << exp << "' to be greater than `" << got
115            << "' - " << msg;
116         abort_msg(ss.str().c_str(), "", file, linenum);
117     }
118 }
119 
120 #define checkeq(expected, actual, msg) \
121     checkeqfn(expected, actual, msg, __FILE__, __LINE__)
122 #define checkne(expected, actual, msg) \
123     checknefn(expected, actual, msg, __FILE__, __LINE__)
124 #define checkle(expected, actual, msg) \
125     checklefn(expected, actual, msg, __FILE__, __LINE__)
126 #define checklt(expected, actual, msg) \
127     checkltfn(expected, actual, msg, __FILE__, __LINE__)
128 #define checkge(expected, actual, msg) \
129     checkgefn(expected, actual, msg, __FILE__, __LINE__)
130 #define checkgt(expected, actual, msg) \
131     checkgtfn(expected, actual, msg, __FILE__, __LINE__)
132 
133 class BaseTestCase {
134 public:
135     BaseTestCase(const char *_name,  const char *_cfg, bool _skip = false);
136 
137     BaseTestCase(const BaseTestCase &o);
138 
139     engine_test_t *getTest();
140 
getName()141     const char *getName() {
142         return name;
143     }
144 
145 protected:
146     engine_test_t test;
147 
148 private:
149     const char *name;
150     const char *cfg;
151     bool skip;
152 };
153 
154 class TestCase : public BaseTestCase {
155 public:
156     TestCase(const char* _name,
157              enum test_result (*_tfun)(EngineIface*),
158              bool (*_test_setup)(EngineIface*),
159              bool (*_test_teardown)(EngineIface*),
160              const char* _cfg,
161              enum test_result (*_prepare)(engine_test_t* test),
162              void (*_cleanup)(engine_test_t* test, enum test_result result),
163              bool _skip = false);
164 };
165 
166 class TestCaseV2 : public BaseTestCase {
167 public:
168     TestCaseV2(const char *_name,
169                enum test_result(*_tfun)(engine_test_t *),
170                bool(*_test_setup)(engine_test_t *),
171                bool(*_test_teardown)(engine_test_t *),
172                const char *_cfg,
173                enum test_result (*_prepare)(engine_test_t *test),
174                void (*_cleanup)(engine_test_t *test, enum test_result result),
175                bool _skip = false);
176 };
177 
178 // Name to use for database directory
179 extern const char *dbname_env;
180 
181 // Handle of the test_harness, provided by engine_testapp.
182 extern struct test_harness* testHarness;
183 
184 // Default DB name. Provided by the specific testsuite.
185 extern const char* default_dbname;
186 
187 enum test_result rmdb(const char* path);
188 enum test_result rmdb(void);
189 
190 
191 // Default testcase setup function
192 bool test_setup(EngineIface* h);
193 
194 // Default testcase teardown function
195 bool teardown(EngineIface* h);
196 bool teardown_v2(engine_test_t* test);
197 
198 
199 // Default testcase prepare function.
200 enum test_result prepare(engine_test_t *test);
201 
202 /// Prepare a test which is currently broken (i.e. under investigation).
203 enum test_result prepare_broken_test(engine_test_t* test);
204 
205 /**
206  * Prepare a test which is only applicable for persistent buckets (EPBucket) -
207  * for other types it should be skipped.
208  */
209 enum test_result prepare_ep_bucket(engine_test_t* test);
210 
211 /**
212  * Prepare a test which is currently expected to fail when using
213  * Rocks, Magma or both and skip the test.
214  * As Rocks and Magma mature, these tests should be revisited.
215  *
216  * NB: some tests may currently be marked with this but will never pass
217  * e.g., compaction tests as RocksDB's compaction model is different.
218  * Eventually, they should be marked as not applicable, and an
219  * equivalent test constructed exclusively for RocksDB.
220  */
221 enum test_result prepare_skip_broken_under_rocks(engine_test_t* test);
222 enum test_result prepare_skip_broken_under_magma(engine_test_t* test);
223 enum test_result prepare_skip_broken_under_rocks_and_magma(engine_test_t* test);
224 
225 /**
226  * Prepare a test which is only applicable to a persistent bucket, but
227  * is currently expected to fail when using RocksDBKVStore and so should
228  * be skipped.
229  * As RocksDBKVStore progresses these tests should be rechecked, as
230  * all applicable tests should eventually pass.
231  */
232 enum test_result prepare_ep_bucket_skip_broken_under_rocks(engine_test_t* test);
233 enum test_result prepare_ep_bucket_skip_broken_under_magma(engine_test_t* test);
234 enum test_result prepare_ep_bucket_skip_broken_under_rocks_and_magma(
235         engine_test_t* test);
236 
237 /**
238  * Prepare a test which is only applicable to a persistent bucket, but is
239  * currently expected to fail when using RocksDBKVStore in the full eviction
240  * mode and so should be skipped.
241  */
242 enum test_result prepare_ep_bucket_skip_broken_under_rocks_full_eviction(
243         engine_test_t* test);
244 
245 /**
246  * Prepare a test which is currently expected to fail when either:
247  *  - using RocksDBKVStore in a persistend bucket
248  *  - using an Ephemeral bucket
249  * and so should be skipped.
250  * These tests should eventually pass in both cases.
251  */
252 enum test_result prepare_skip_broken_under_ephemeral_and_rocks(
253         engine_test_t* test);
254 
255 /**
256  * Prepare a test which is only applicable for ephemeral buckets
257  * (EphemeralBucket) - for other types it should be skipped.
258  */
259 enum test_result prepare_ephemeral_bucket(engine_test_t* test);
260 
261 /**
262  * Prepare a test which is only applicable to full eviction mode - for
263  * for other eviction types it should be skipped.
264  */
265 enum test_result prepare_full_eviction(engine_test_t *test);
266 
267 /**
268  * Prepare a test which is only applicable to full eviction mode and not rocksdb
269  */
270 enum test_result prepare_full_eviction_skip_under_rocks(engine_test_t *test);
271 
272 /**
273  * Prepare a test which is not applicable for full eviction when running rocksdb
274  */
275 enum test_result prepare_skip_broken_under_rocks_full_eviction(
276         engine_test_t* test);
277 
278 /**
279  * TODO TEMPORARY:
280  * Prepare a test which currently is broken for Ephemeral buckets and so
281  * should be skipped for them for now.
282  *
283  * Any test using this *should* eventually pass, so these should be fixed.
284  */
285 enum test_result prepare_skip_broken_under_ephemeral(engine_test_t *test);
286 
287 
288 // Default testcase cleanup function.
289 void cleanup(engine_test_t *test, enum test_result result);
290 
291 struct BucketHolder {
BucketHolderBucketHolder292     BucketHolder(EngineIface* _h, std::string _dbpath)
293         : h(_h), dbpath(std::move(_dbpath)) {
294     }
295 
296     EngineIface* h;
297     const std::string dbpath;
298 };
299 
300 /*
301   Create n_buckets and add to the buckets vector.
302   Returns the number of buckets actually created.
303 */
304 int create_buckets(const char* cfg, int n_buckets, std::vector<BucketHolder> &buckets);
305 
306 /*
307   Destroy all of the buckets in the vector and delete the DB path.
308 */
309 void destroy_buckets(std::vector<BucketHolder> &buckets);
310 
311 // Verifies that the given key and value exist in the store.
312 void check_key_value(EngineIface* h,
313                      const char* key,
314                      const char* val,
315                      size_t vlen,
316                      Vbid vbucket = Vbid(0));
317 
318 std::string get_dbname(const char* test_cfg);
319 
320 // Returns true if Compression is enabled for the given engine.
321 bool isCompressionEnabled(EngineIface* h);
322 
323 // Returns true if passive compression is enabled for the given engine.
324 bool isPassiveCompressionEnabled(EngineIface* h);
325 
326 // Returns true if active compression is enabled for the given engine.
327 bool isActiveCompressionEnabled(EngineIface* h);
328 
329 // Returns true if Warmup is enabled for the given engine.
330 bool isWarmupEnabled(EngineIface* h);
331 
332 // Returns true if the given engine is a persistent bucket (EPBucket).
333 bool isPersistentBucket(EngineIface* h);
334 
335 // Returns true if the given engine is an ephemeral bucket (EphemeralBucket).
336 bool isEphemeralBucket(EngineIface* h);
337 
338 // Checks number of temp items in a persistent bucket (EPBucket).
339 void checkPersistentBucketTempItems(EngineIface* h, int exp);
340