xref: /5.5.2/kv_engine/tests/testapp/testapp.h (revision 9d912744)
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 "config.h"
21
22#include <cJSON_utils.h>
23
24#include "testapp_binprot.h"
25#include <memory>
26#include <stdint.h>
27#include <stdlib.h>
28#include <sys/types.h>
29#include <string>
30#include <tuple>
31
32#include <cJSON.h>
33#include <gtest/gtest.h>
34#include <memcached/protocol_binary.h>
35#include <memcached/types.h>
36
37#include "engines/ewouldblock_engine/ewouldblock_engine.h"
38
39#include <protocol/connection/client_connection.h>
40#include <protocol/connection/client_connection_map.h>
41#include <protocol/connection/client_mcbp_commands.h>
42
43#include "testapp_environment.h"
44
45enum class TransportProtocols {
46    McbpPlain,
47    McbpSsl,
48    McbpIpv6Plain,
49    McbpIpv6Ssl
50};
51
52// Properties of a particular subdoc statistic set.
53struct SubdocStatTraits {
54    const char* count_name;
55    const char* bytes_total_name;
56    const char* bytes_extracted_subset;
57};
58
59namespace Testapp {
60const size_t MAX_CONNECTIONS = 1000;
61const size_t BACKLOG = 1000;
62}
63
64std::ostream& operator<<(std::ostream& os, const TransportProtocols& t);
65std::string to_string(const TransportProtocols& transport);
66
67/// Should testapp client negotiate JSON HELLO feature?
68enum class ClientJSONSupport { Yes, No };
69std::string to_string(ClientJSONSupport json);
70
71/// Should testapp client negotiate Snappy HELLO feature?
72enum class ClientSnappySupport { Yes, No };
73std::string to_string(ClientSnappySupport s);
74
75// Needed by subdocument tests in seperate .cc file.
76extern SOCKET sock;
77extern in_port_t port;
78extern pid_t server_pid;
79
80// Needed by testapp_tests
81extern in_port_t ssl_port;
82extern SOCKET sock_ssl;
83
84// Set of HELLO features which are currently enabled.
85extern std::set<cb::mcbp::Feature> enabled_hello_features;
86
87class TestBucketImpl;
88
89/**
90 * Base test fixture for all 'testapp' tests - aka a test application talking to
91 * memcached.
92 *
93 * These tests connect to memcached via the binary protocol; and issue
94 * various commands to test functionality. TestappTest provides baseline
95 * functionality for simple tests; or for other more complex subclasses to
96 * build on.
97 * It provides a single connection type (no SSL / IPv6 etc), and a minimal
98 * set of HELLO flags negotiated.
99 */
100class TestappTest : public ::testing::Test {
101public:
102    // Per-test-case set-up.
103    // Called before the first test in this test case.
104    static void SetUpTestCase();
105
106    // Per-test-case tear-down.
107    // Called after the last test in this test case.
108    static void TearDownTestCase();
109
110    static uint16_t sasl_auth(const char *username, const char *password);
111
112    /// Helper which which returns true if the specified value is correctly
113    /// encoded as JSON.
114    static bool isJSON(cb::const_char_buffer value);
115
116    /// Does this test/connection support JSON datatype?
117    virtual ClientJSONSupport hasJSONSupport() const {
118        return ClientJSONSupport::No;
119    }
120
121    /// Does this test/connection support Snappy?
122    virtual ClientSnappySupport hasSnappySupport() const {
123        return ClientSnappySupport::No;
124    }
125
126    /**
127     * What response datatype do we expect for documents which are JSON?
128     * Will be JSON only if the client successfully negotiated JSON feature.
129     */
130    cb::mcbp::Datatype expectedJSONDatatype() const;
131
132    virtual MemcachedConnection& getConnection();
133    virtual MemcachedConnection& getAdminConnection();
134
135    /**
136     * Reconfigure the server to use the given cert_auth policy
137     *
138     * @param state the new state (mandatory, enable, disable)
139     * @param path path in the certificate (ie: subject.cn)
140     * @param prefix the prefix to map
141     * @param delimiter the delimiter in the field
142     */
143    void reconfigure_client_cert_auth(const std::string& state,
144                                      const std::string& path,
145                                      const std::string& prefix,
146                                      const std::string& delimiter);
147
148    /**
149     * Make sure that the provided connection use our client certificates
150     */
151    void setClientCertData(MemcachedConnection& connection);
152
153protected:
154    // per test setup function.
155    void SetUp() override;
156
157    // per test tear-down function.
158    void TearDown() override;
159
160    //per test compression mode configuration function
161    void setCompressionMode(const std::string& compression_mode);
162
163    //per test min compression ratio configuration
164    void setMinCompressionRatio(const float min_compression_ratio);
165
166    static unique_cJSON_ptr generate_config(uint16_t ssl_port);
167    static unique_cJSON_ptr generate_config();
168
169    static void start_memcached_server(cJSON* config);
170
171    /**
172     * Function to start the server and let it listen on a random port.
173     * Set <code>server_pid</code> to the pid of the process
174     */
175    static void start_external_server();
176
177    /**
178     * Function to start the server as a thread in the current process
179     * and let it listen on a random port.
180     *
181     * Note that this is only supposed to be used for debugging as it don't
182     * properly shut down the server as part of the TearDown process
183     * so that you might experience problems if you try to run multiple
184     * test batches with the same process..
185     *
186     * Set <code>server_pid</code> to the pid of the process
187     */
188    static void spawn_embedded_server();
189
190    /**
191     * Parse the portnumber file created from the memcached server
192     *
193     * @param port_out where to store the TCP port number the server is
194     *                 listening on (deprecated, use connectionMap instead)
195     * @param ssl_port_out where to store the TCP port number the server is
196     *                     listening for SSL connections (deprecated, use
197     *                     connectionMap instead)
198     */
199    static void parse_portnumber_file(in_port_t& port_out,
200                                      in_port_t& ssl_port_out);
201
202    static void verify_server_running();
203
204    /**
205     * Waits for server to shutdown.  It assumes that the server is
206     * already in the process of being shutdown
207     * @param killed If true server was shutdown by a signal, and we should
208     * expect different waitpid() result.
209     */
210    static void waitForShutdown(bool killed = false);
211
212    static void stop_memcached_server();
213
214    // Create the bucket used for testing
215    static void CreateTestBucket();
216
217    // Delete the bucket used for testing.
218    static void DeleteTestBucket();
219
220    // Get information about the bucket used for testing
221    static TestBucketImpl& GetTestBucket();
222
223    /* Configure the ewouldblock error-injecting engine */
224    static void ewouldblock_engine_configure(ENGINE_ERROR_CODE err_code,
225                                             const EWBEngineMode& mode,
226                                             uint32_t value,
227                                             const std::string& key = "");
228
229    /* Disable the ewouldblock_engine. */
230    static void ewouldblock_engine_disable();
231
232    void reconfigure();
233
234    // JSON configuration (as JSON object) memcached was configured with.
235    static unique_cJSON_ptr memcached_cfg;
236    static std::string portnumber_file;
237    static std::string config_file;
238
239    static ConnectionMap connectionMap;
240    static uint64_t token;
241    static cb_thread_t memcached_server_thread;
242
243    /**
244     * Prepare a connection object to be used from a client by reconnecting
245     * and performing the initial handshake logic
246     *
247     * @param connection the connection to prepare
248     * @return The connection to use
249     */
250    MemcachedConnection& prepare(MemcachedConnection& connection);
251
252    /**
253     * Create an extended attribute
254     *
255     * @param path the full path to the attribute (including the key)
256     * @param value The value to store
257     * @param macro is this a macro for expansion or not
258     * @param expectedStatus optional status if success is not expected
259     */
260    void createXattr(const std::string& path,
261                     const std::string& value,
262                     bool macro = false);
263
264    void runCreateXattr(const std::string& path,
265                        const std::string& value,
266                        bool macro,
267                        protocol_binary_response_status expectedStatus);
268
269    /**
270     * Get an extended attribute
271     *
272     * @param path the full path to the attribute to fetch
273     * @param deleted allow get from deleted documents
274     * @param expectedStatus optional status if success is not expected
275     * @return the value stored for the key (it is expected to be there!)
276     */
277    BinprotSubdocResponse getXattr(const std::string& path,
278                                   bool deleted = false);
279
280    BinprotSubdocResponse runGetXattr(
281            const std::string& path,
282            bool deleted,
283            protocol_binary_response_status expectedStatus);
284
285    int getResponseCount(protocol_binary_response_status statusCode);
286
287    static int statResps() {
288        // Each stats call gets a new connection prepared for it, resulting in
289        // a HELLO. This means we expect 1 success from the stats call and
290        // the number of successes a HELLO takes.
291        return 1 + helloResps();
292    }
293
294    static int helloResps() {
295        // We do a HELLO single hello enabling all of the features
296        // we want as part of preparing the connection.
297        return 1;
298    }
299
300    static int saslResps() {
301        // 2 successes expected due to the initial response and then the
302        // continue step.
303        return 2;
304    }
305    std::string name;
306    static const std::string bucketName;
307};
308
309#define TESTAPP__DOSKIP(cond, reason) \
310    if ((cond)) { \
311        std::cerr \
312        << __FILE__ << ":" << __LINE__ << ": Skipping - '" \
313        << #cond << "' (" << reason << ")" << std::endl; \
314        return; \
315    }
316
317#define TESTAPP_SKIP_IF_UNSUPPORTED(op) \
318    do { TESTAPP__DOSKIP(!GetTestBucket().supportsOp(op), #op); } while (0)
319
320#define TESTAPP_SKIP_IF_SUPPORTED(op) \
321    do { TESTAPP__DOSKIP(GetTestBucket().supportsOp(op), #op); } while (0)
322
323/**
324 * Test fixture for testapp tests which are parameterised on Transport
325 * (IPv4/Ipv6,Plain/SSL) and Hello::JSON on/off.
326 */
327class McdTestappTest
328        : public TestappTest,
329          public ::testing::WithParamInterface<
330                  ::testing::tuple<TransportProtocols, ClientJSONSupport>> {
331public:
332    /// Custom Test name function.
333    static std::string PrintToStringCombinedName(
334            const ::testing::TestParamInfo<
335                    ::testing::tuple<TransportProtocols, ClientJSONSupport>>&
336                    info);
337
338protected:
339    // per test setup function.
340    void SetUp() override;
341
342    // per test tear-down function.
343    void TearDown() override;
344
345    /// return the TransportProtocol parameter for this test instance.
346    TransportProtocols getProtocolParam() const {
347        return std::get<0>(GetParam());
348    }
349
350    /// return the ClientJSONSupport parameter for this test instance.
351    ClientJSONSupport getJSONParam() const {
352        return std::get<1>(GetParam());
353    }
354
355    ClientJSONSupport hasJSONSupport() const override;
356
357    /* Helpers for individual testcases */
358    void test_set_huge_impl(const char *key, uint8_t cmd, int result,
359                            bool pipeline, int iterations, int message_size);
360};
361
362SOCKET connect_to_server_plain(in_port_t port);
363void reconnect_to_server();
364
365/* Set the datatype feature on the connection to the specified value */
366void set_datatype_feature(bool enable);
367
368// Attempts to fetch the document with the given key.
369// Returns a pair of {status, value}; where status is the response code from
370// the server and value is the documents value (if status == SUCCESS).
371std::pair<protocol_binary_response_status, std::string>
372fetch_value(const std::string& key);
373
374/* Attempts to get the given key and checks if it's value matches
375 * {expected_value}.
376 */
377void validate_object(const char *key, const std::string& expected_value);
378
379/* Attempts to get the given key and checks if it's flags matches
380 * {expected_flags}.
381 */
382void validate_flags(const char *key, uint32_t expected_flags);
383
384/**
385 * Attempts to store a document with the given key, value, flags and expiry
386 * time (and optionally compress the value before storing it)
387 *
388 * @param key Document key
389 * @param value Document value. Supports up to maximum size server allows.
390 * @param flags Document flag
391 * @param exptime Document expiry time
392 * @param compress Should the value be compressed before storing
393 */
394void store_document(const std::string& key,
395                    const std::string& value,
396                    uint32_t flags = 0,
397                    uint32_t exptime = 0,
398                    bool compress = false);
399
400/* Attempts to delete the object with the given key.
401 * @param key key to remove
402 * @param ignore_missing do not fail if key did not exist
403 */
404void delete_object(const char *key, bool ignore_missing = false);
405
406/**
407 * Attempts to store an object with a datatype
408 *
409 * @param key The documents key
410 * @param value The documents value
411 * @param flags The documents flags
412 * @param expiration The documents expiration (0 == never)
413 * @param datatype The datatype to use
414 */
415void store_object_w_datatype(const std::string& key,
416                             cb::const_char_buffer value,
417                             uint32_t flags,
418                             uint32_t expiration,
419                             cb::mcbp::Datatype datatype);
420
421// Enables / disables the JSON feature.
422void set_json_feature(bool enable);
423
424// Enables / disables the MUTATION_SEQNO feature.
425void set_mutation_seqno_feature(bool enable);
426
427/* Send the specified buffer+len to memcached. */
428void safe_send(const void* buf, size_t len, bool hickup);
429
430/* Receive the specified len into buf from memcached */
431bool safe_recv(void *buf, size_t len);
432
433/* Attempts to receive size bytes into buf. Returns true if successful.
434 */
435bool safe_recv_packet(void *buf, size_t size);
436bool safe_recv_packet(std::vector<uint8_t>& buf);
437bool safe_recv_packet(std::vector<char>& buf);
438
439/* Whether receiving an EOF during a read is considered an error */
440void set_allow_closed_read(bool enabled);
441
442/* The opposite of safe_recv. Simply tries to read from the socket (will use
443 * SSL if the socket is SSL configured.
444 */
445ssize_t phase_recv(void *buf, size_t len);
446
447/* Whether the current socket is SSL */
448bool sock_is_ssl();
449
450SOCKET create_connect_plain_socket(in_port_t port);
451
452time_t get_server_start_time();
453
454std::string CERTIFICATE_PATH(const std::string& in);
455
456void write_config_to_file(const std::string& config, const std::string& fname);
457
458// map of statistic key (name) -> value.
459typedef std::map<std::string, std::string> stats_response_t;
460
461/* Request stats
462 * @return a map of stat key & values in the server response.
463 */
464stats_response_t request_stats();
465
466/* Extracts a single statistic from the set of stats, returning as a uint64_t
467 */
468uint64_t extract_single_stat(const stats_response_t& stats,
469                                      const char* name);
470
471unique_cJSON_ptr loadJsonFile(const std::string &file);
472
473ssize_t socket_recv(SOCKET s, char *buf, size_t len);
474ssize_t socket_send(SOCKET s, const char *buf, size_t len);
475void adjust_memcached_clock(int64_t clock_shift, TimeType timeType);
476