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