1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2016 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/*
19 * Unit tests for the EPBucket class.
20 */
21
22#pragma once
23
24#include "evp_store_test.h"
25#include "fakes/fake_executorpool.h"
26
27class MockActiveStreamWithOverloadedRegisterCursor;
28class MockDcpProducer;
29
30class MockDcpProducer;
31
32/*
33 * A subclass of KVBucketTest which uses a fake ExecutorPool,
34 * which will not spawn ExecutorThreads and hence not run any tasks
35 * automatically in the background. All tasks must be manually run().
36 */
37class SingleThreadedKVBucketTest : public KVBucketTest {
38public:
39    /*
40     * Run the next task from the taskQ
41     * The task must match the expectedTaskName parameter
42     */
43    ProcessClock::time_point runNextTask(TaskQueue& taskQ,
44                                         const std::string& expectedTaskName);
45
46    /*
47     * Run the next task from the taskQ
48     */
49    ProcessClock::time_point runNextTask(TaskQueue& taskQ);
50
51    /*
52     * DCP helper. Create a MockDcpProducer configured with (or without)
53     * collections and/or delete_times enabled
54     * @param cookie cookie to associate with the new producer
55     * @param filter DCP JSON filter (for collection support)
56     * @param dcpCollectionAware enable/disable collections
57     * @param deleteTime yes/no - enable/disable delete times
58     */
59    std::shared_ptr<MockDcpProducer> createDcpProducer(
60            const void* cookie,
61            const std::string& filter,
62            bool dcpCollectionAware,
63            IncludeDeleteTime deleteTime);
64
65    /*
66     * DCP helper.
67     * Notify and step the given producer
68     * @param expectedOp once stepped we expect to see this DCP opcode produced
69     * @param fromMemory if false then step a backfill
70     */
71    void notifyAndStepToCheckpoint(
72            MockDcpProducer& producer,
73            dcp_message_producers& producers,
74            cb::mcbp::ClientOpcode expectedOp =
75                    cb::mcbp::ClientOpcode::DcpSnapshotMarker,
76            bool fromMemory = true);
77
78    /*
79     * DCP helper.
80     * Run the active-checkpoint processor task for the given producer
81     * @param producer The producer whose task will be ran
82     * @param producers The dcp callbacks
83     */
84    void runCheckpointProcessor(MockDcpProducer& producer,
85                                dcp_message_producers& producers);
86
87    /**
88     * Create a DCP stream on the producer for this->vbid
89     */
90    void createDcpStream(MockDcpProducer& producer);
91
92    /**
93     * Create a DCP stream on the producer for vbid
94     */
95    void createDcpStream(MockDcpProducer& producer, uint16_t vbid);
96
97    /**
98     * Run the compaction task
99     * @param purgeBeforeTime purge tombstones with timestamps less than this
100     * @param purgeBeforeSeq purge tombstones with seqnos less than this
101     */
102    void runCompaction(uint64_t purgeBeforeTime = 0,
103                       uint64_t purgeBeforeSeq = 0);
104
105protected:
106    void SetUp() override;
107
108    void TearDown() override;
109
110    /*
111     * Change the vbucket state, and run the VBStatePeristTask (if necessary
112     * for this bucket type).
113     * On return the state will be changed and the task completed.
114     */
115    void setVBucketStateAndRunPersistTask(uint16_t vbid,
116                                          vbucket_state_t newState);
117
118    /*
119     * Set the stats isShutdown and attempt to drive all tasks to cancel for
120     * the specified engine.
121     */
122    void shutdownAndPurgeTasks(EventuallyPersistentEngine* ep);
123
124    void cancelAndPurgeTasks();
125
126    /**
127     * This method will keep running reader tasks until the engine shows warmup
128     * is complete.
129     */
130    void runReadersUntilWarmedUp();
131
132    /**
133     * Destroy engine and replace it with a new engine that can be warmed up.
134     * Finally, run warmup.
135     */
136    void resetEngineAndWarmup(std::string new_config = "");
137
138    /*
139     * Fake callback emulating dcp_add_failover_log
140     */
141    static ENGINE_ERROR_CODE fakeDcpAddFailoverLog(
142            vbucket_failover_t* entry,
143            size_t nentries,
144            gsl::not_null<const void*> cookie) {
145        return ENGINE_SUCCESS;
146    }
147
148    void notifyAndStepToCheckpoint(MockDcpProducer& producer,
149                                   dcp_message_producers* producers);
150
151    SingleThreadedExecutorPool* task_executor;
152};
153
154/**
155 * Test fixture for single-threaded tests on EPBucket.
156 */
157class SingleThreadedEPBucketTest : public SingleThreadedKVBucketTest {
158protected:
159    EPBucket& getEPBucket() {
160        return dynamic_cast<EPBucket&>(*store);
161    }
162};
163
164/**
165 * Test fixture for KVBucket tests running in single-threaded mode.
166 *
167 * Parameterised on a pair of:
168 * - bucket_type (ephemeral of persistent)
169 * - ephemeral_full_policy (for specifying ephemeral auto-delete & fail_new_data
170 *   eviction modes). If empty then unused (persistent buckets).
171 */
172class STParameterizedBucketTest
173        : public SingleThreadedEPBucketTest,
174          public ::testing::WithParamInterface<
175                  std::tuple<std::string, std::string>> {
176public:
177    bool persistent() {
178        return std::get<0>(GetParam()) == "persistent";
179    }
180
181protected:
182    void SetUp() {
183        if (!config_string.empty()) {
184            config_string += ";";
185        }
186        config_string += "bucket_type=" + std::get<0>(GetParam());
187        auto evictionPolicy = std::get<1>(GetParam());
188        if (!evictionPolicy.empty()) {
189            if (persistent()) {
190                config_string += ";item_eviction_policy=" + evictionPolicy;
191            } else {
192                config_string += ";ephemeral_full_policy=" + evictionPolicy;
193            }
194        }
195        SingleThreadedEPBucketTest::SetUp();
196    }
197};
198