3dae2ef937bcc1e011d31c7742384104671465e4
[ep-engine.git] / tests / module_tests / configuration_test.cc
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 #include "configuration.h"
19
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22
23 /* Like EXPECT_THROW except you can check the exception's `what()` */
24 # define CB_EXPECT_THROW_MSG(EXPR, ETYPE, MSG) \
25     EXPECT_THROW(EXPR, ETYPE); \
26     try { \
27         EXPR; \
28         ADD_FAILURE() << "Expected: " #EXPR " throws an exception of type " \
29           #ETYPE ".\n  Actual: it throws nothing."; \
30     } catch (const ETYPE& EXCEPTION) { \
31         EXPECT_STREQ(MSG, EXCEPTION.what()) << "Wrong exception message!" ; \
32     } catch (...) { \
33         ADD_FAILURE() << "Expected: " #EXPR " throws an exception of type " \
34           #ETYPE ".\n  Actual: it throws different type."; \
35     } \
36
37 TEST(ValueChangedValidatorTest, AllMethodsThrow) {
38     ValueChangedValidator validator;
39     std::string key{"test_key"};
40
41     EXPECT_THROW(validator.validateBool(key, false), std::runtime_error);
42     EXPECT_THROW(validator.validateSize(key, 0), std::runtime_error);
43     EXPECT_THROW(validator.validateSSize(key, 0), std::runtime_error);
44     EXPECT_THROW(validator.validateFloat(key, 0.0), std::runtime_error);
45     EXPECT_THROW(validator.validateString(key, ""), std::runtime_error);
46 }
47
48 TEST(SizeRangeValidatorTest, UninitialisedIsOnlyZero) {
49     SizeRangeValidator validator;
50     std::string key{"test_key"};
51
52     EXPECT_NO_THROW(validator.validateSize(key, 0));
53     EXPECT_NO_THROW(validator.validateSSize(key, 0));
54
55     CB_EXPECT_THROW_MSG(
56             validator.validateSize(key, 1),
57             std::range_error,
58             "Validation Error, test_key takes values between 0 and 0 (Got: 1)"
59     );
60     CB_EXPECT_THROW_MSG(
61             validator.validateSSize(key, 1),
62             std::range_error,
63             "Validation Error, test_key takes values between 0 and 0 (Got: 1)"
64     );
65     CB_EXPECT_THROW_MSG(
66             validator.validateSSize(key, -1),
67             std::range_error,
68             "Validation Error, test_key takes values between 0 and 0 (Got: -1)"
69     );
70 }
71
72 TEST(SizeRangeValidatorTest, UnsignedBoundsWorks) {
73     SizeRangeValidator validator;
74     std::string key{"test_key"};
75
76     (&validator)->min(100)->max(1000);
77
78     EXPECT_NO_THROW(validator.validateSize(key, 100));
79     EXPECT_NO_THROW(validator.validateSize(key, 1000));
80     EXPECT_NO_THROW(validator.validateSize(key, 101));
81     EXPECT_NO_THROW(validator.validateSize(key, 999));
82
83     CB_EXPECT_THROW_MSG(
84             validator.validateSize(key, 99),
85             std::range_error,
86             "Validation Error, test_key takes values between 100 and 1000 (Got: 99)"
87     );
88     CB_EXPECT_THROW_MSG(
89             validator.validateSize(key, 1001),
90             std::range_error,
91             "Validation Error, test_key takes values between 100 and 1000 (Got: 1001)"
92     );
93 }
94
95 TEST(SizeRangeValidatorTest, SignedBoundsWorks) {
96     SizeRangeValidator validator;
97     std::string key{"test_key"};
98
99     (&validator)->min(-100)->max(1000);
100
101     EXPECT_NO_THROW(validator.validateSSize(key, -100));
102     EXPECT_NO_THROW(validator.validateSSize(key, 1000));
103     EXPECT_NO_THROW(validator.validateSSize(key, -99));
104     EXPECT_NO_THROW(validator.validateSSize(key, 999));
105
106     CB_EXPECT_THROW_MSG(
107             validator.validateSSize(key, -101),
108             std::range_error,
109             "Validation Error, test_key takes values between -100 and 1000 (Got: -101)"
110     );
111     CB_EXPECT_THROW_MSG(
112             validator.validateSSize(key, 1001),
113             std::range_error,
114             "Validation Error, test_key takes values between -100 and 1000 (Got: 1001)"
115     );
116 }
117
118 TEST(SSizeRangeValidatorTest, UninitialisedIsOnlyZero) {
119     SizeRangeValidator validator;
120     std::string key{"test_key"};
121
122     EXPECT_NO_THROW(validator.validateSSize(key, 0));
123
124     CB_EXPECT_THROW_MSG(
125             validator.validateSSize(key, 1),
126             std::range_error,
127             "Validation Error, test_key takes values between 0 and 0 (Got: 1)"
128     );
129     CB_EXPECT_THROW_MSG(
130             validator.validateSSize(key, -1),
131             std::range_error,
132             "Validation Error, test_key takes values between 0 and 0 (Got: -1)"
133     );
134 }
135
136 TEST(SSizeRangeValidatorTest, SignedBoundsWork) {
137     SSizeRangeValidator validator;
138     std::string key{"test_key"};
139
140     (&validator)->min(-100)->max(1000);
141
142     EXPECT_NO_THROW(validator.validateSSize(key, -100));
143     EXPECT_NO_THROW(validator.validateSSize(key, 1000));
144     EXPECT_NO_THROW(validator.validateSSize(key, -99));
145     EXPECT_NO_THROW(validator.validateSSize(key, 999));
146
147     CB_EXPECT_THROW_MSG(
148             validator.validateSSize(key, -101),
149             std::range_error,
150             "Validation Error, test_key takes values between -100 and 1000 (Got: -101)"
151     );
152     CB_EXPECT_THROW_MSG(
153             validator.validateSSize(key, 1001),
154             std::range_error,
155             "Validation Error, test_key takes values between -100 and 1000 (Got: 1001)"
156     );
157 }
158
159 TEST(FloatRangeValidatorTest, UninitialisedIsZero) {
160     FloatRangeValidator validator;
161     std::string key{"test_key"};
162
163     validator.validateFloat(key, 0.0);
164     CB_EXPECT_THROW_MSG(
165             validator.validateFloat(key, 1.0),
166             std::range_error,
167             "Validation Error, test_key takes values between 0.000000 and 0.000000 (Got: 1.000000)"
168     );
169
170 }
171
172 TEST(FloatRangeValidatorTest, FloatBoundsWork) {
173     FloatRangeValidator validator;
174     std::string key{"test_key"};
175
176     (&validator)->min(-100.1)->max(1000.1);
177
178     /* In-bounds */
179     EXPECT_NO_THROW(validator.validateFloat(key, -100.1));
180     EXPECT_NO_THROW(validator.validateFloat(key, 100.00));
181     EXPECT_NO_THROW(validator.validateFloat(key, 101.0));
182     EXPECT_NO_THROW(validator.validateFloat(key, 999.0));
183
184     /* Out-of bounds */
185     CB_EXPECT_THROW_MSG(
186             validator.validateFloat(key, -100.2),
187             std::range_error,
188             ("Validation Error, test_key takes values between "
189              + std::to_string(-100.1f)
190              + " and " + std::to_string(1000.1f)
191              + " (Got: " + std::to_string(-100.2f) + ")").c_str()
192     );
193     CB_EXPECT_THROW_MSG(
194             validator.validateFloat(key, 1000.2),
195             std::range_error,
196             ("Validation Error, test_key takes values between "
197              + std::to_string(-100.1f)
198              + " and " + std::to_string(1000.1f)
199              + " (Got: " + std::to_string(1000.2f) + ")").c_str()
200     );
201 }
202
203 TEST(EnumValidatorTest, EmptyWorks) {
204     EnumValidator validator;
205     std::string key{"test_key"};
206
207     /* Empty test */
208     CB_EXPECT_THROW_MSG(
209             validator.validateString(key, "my_enum"),
210             std::range_error,
211             "Validation Error, test_key takes one of [] (Got: my_enum)"
212     );
213 }
214
215 TEST(EnumValidatorTest, AddOneWorks) {
216     EnumValidator validator;
217     std::string key{"test_key"};
218
219     /* Single test, in-bounds */
220     validator.add("enum_one");
221     EXPECT_NO_THROW(validator.validateString(key, "enum_one"));
222
223     /* Single test, out of bounds */
224     CB_EXPECT_THROW_MSG(
225             validator.validateString(key, "my_enum"),
226             std::range_error,
227             "Validation Error, test_key takes one of [enum_one] (Got: my_enum)"
228     );
229 }
230
231 TEST(EnumValidatorTest, OverwriteWorks) {
232     EnumValidator validator;
233     std::string key{"test_key"};
234
235     validator.add("enum_one");
236     validator.add("enum_one");
237     EXPECT_NO_THROW(validator.validateString(key, "enum_one"));
238 }
239
240 TEST(EnumValidatorTest, MultipleWorks) {
241     EnumValidator validator;
242     std::string key{"test_key"};
243
244     validator.add("enum_1");
245     validator.add("enum_2");
246     validator.add("enum_3");
247     EXPECT_NO_THROW(validator.validateString(key, "enum_1"));
248     EXPECT_NO_THROW(validator.validateString(key, "enum_2"));
249     EXPECT_NO_THROW(validator.validateString(key, "enum_3"));
250
251     /* Multi, out of bounds */
252     CB_EXPECT_THROW_MSG(
253             validator.validateString(key, "my_enum"),
254             std::range_error,
255             "Validation Error, test_key takes one of [enum_1, enum_2, enum_3] (Got: my_enum)"
256     );
257 }
258
259 TEST(EnumValidatorTest, EnMassWorks) {
260     EnumValidator validator;
261     std::string key{"test_key"};
262
263     /* Mass test */
264     std::vector<std::string> mass;
265     for(auto i = 0; i < 100000; i++) {
266         mass.push_back(std::to_string(i));
267     }
268
269     for(auto i : mass) {
270         validator.add(i.c_str());
271     }
272     for(auto i : mass) {
273         EXPECT_NO_THROW(validator.validateString(key, i.c_str()));
274     }
275 }
276
277
278 class ConfigurationShim : public Configuration {
279     /**
280      * Shim class to allow testing the protected methods which are usually
281      * exposed through the generated configuration.
282      */
283
284 public:
285     using Configuration::setParameter;
286     using Configuration::getParameter;
287 };
288
289 TEST(ConfigurationTest, SetGetWorks) {
290     ConfigurationShim configuration;
291
292     configuration.setParameter("bool", false);
293     EXPECT_EQ(configuration.getParameter<bool>("bool"), false);
294
295     configuration.setParameter("size", (size_t)100);
296     EXPECT_EQ(configuration.getParameter<size_t>("size"), 100);
297
298     configuration.setParameter("ssize", (ssize_t)-100);
299     EXPECT_EQ(configuration.getParameter<ssize_t>("ssize"), -100);
300
301     configuration.setParameter("float", (float)123.5);
302     EXPECT_EQ(configuration.getParameter<float>("float"), 123.5);
303
304     configuration.setParameter("char*", "hello");
305     EXPECT_EQ(configuration.getParameter<std::string>("char*"), "hello");
306
307     configuration.setParameter("string", std::string("hello"));
308     EXPECT_EQ(configuration.getParameter<std::string>("string"), "hello");
309 }
310
311 TEST(ConfigurationTest, ValidatorWorks) {
312     ConfigurationShim configuration;
313     std::string key{"test_key"};
314
315     configuration.setParameter(key, (size_t)110);
316     EXPECT_NO_THROW(configuration.setValueValidator(key, (new SizeRangeValidator())->min(10)->max(100)));
317     EXPECT_NO_THROW(configuration.setParameter(key, (size_t)10));
318     EXPECT_NO_THROW(configuration.setParameter(key, (size_t)100));
319     EXPECT_THROW(configuration.setParameter(key, (size_t)9), std::range_error);
320
321     CB_EXPECT_THROW_MSG(configuration.setParameter(key, (size_t)9),
322                         std::range_error,
323                         "Validation Error, test_key takes values between 10 "
324                         "and 100 (Got: 9)");
325 }
326
327 class MockValueChangedListener : public ValueChangedListener {
328 public:
329     MOCK_METHOD2(booleanValueChanged, void(const std::string&, bool));
330     MOCK_METHOD2(sizeValueChanged, void(const std::string&, size_t));
331     MOCK_METHOD2(ssizeValueChanged, void(const std::string&, ssize_t));
332     MOCK_METHOD2(floatValueChanged, void(const std::string&, float));
333     MOCK_METHOD2(stringValueChanged, void(const std::string&, const char*));
334 };
335
336 using ::testing::_;
337
338 TEST(ChangeListenerTest, ChangeListenerSSizeRegression) {
339     /*
340      * Confirming that setting a config parameter of type ssize_t
341      * correctly calls ssizeValueChanged on Changelisteners. Previously, it
342      * instead called sizeValueChanged - and some code had become dependent on
343      * this.
344      */
345     ConfigurationShim configuration;
346     std::string key{"test_key"};
347
348     // Create listeners
349     auto mvcl = new MockValueChangedListener;
350     // set parameter once so entry in attributes is present to add a listener
351     configuration.setParameter(key, (ssize_t)1);
352
353     EXPECT_CALL(*mvcl, ssizeValueChanged("test_key", 2)).Times(1);
354     EXPECT_CALL(*mvcl, sizeValueChanged(_, _)).Times(0);
355
356     // add listeners
357     configuration.addValueChangedListener(key, mvcl);
358
359     // change parameters
360     configuration.setParameter(key, (ssize_t)2);
361 }