1 /*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <folly/experimental/settings/Settings.h>
18
19 #include <folly/Format.h>
20 #include <folly/portability/GTest.h>
21
22 #include <folly/experimental/settings/test/a.h>
23 #include <folly/experimental/settings/test/b.h>
24
25 namespace some_ns {
26 FOLLY_SETTING_DEFINE(
27 follytest, some_flag, std::string, "default", "Description");
28 FOLLY_SETTING_DEFINE(
29 follytest,
30 unused,
31 std::string,
32 "unused_default",
33 "Not used, but should still be in the list");
34 FOLLY_SETTING_DEFINE(
35 follytest,
36 multi_token_type,
37 unsigned int,
38 123,
39 "Test that multi-token type names can be used");
40 // Enable to test runtime collision checking logic
41 #if 0
42 FOLLY_SETTING_DEFINE(follytest, internal_flag_to_a, std::string,
43 "collision_with_a",
44 "Collision_with_a");
45 #endif
46
47 /* Test user defined type support */
48 struct UserDefinedType {
UserDefinedTypesome_ns::UserDefinedType49 explicit UserDefinedType(folly::StringPiece value) {
50 if (value == "a") {
51 value_ = 0;
52 } else if (value == "b") {
53 value_ = 100;
54 } else {
55 throw std::runtime_error("Invalid value passed to UserDefinedType ctor");
56 }
57 }
58
operator ==some_ns::UserDefinedType59 bool operator==(const UserDefinedType& other) const {
60 return value_ == other.value_;
61 }
62
63 int value_;
64 };
65 /* Note: conversion intentionally to different strings to test that this
66 function is called */
67 template <class String>
toAppend(const UserDefinedType & t,String * out)68 void toAppend(const UserDefinedType& t, String* out) {
69 if (t.value_ == 0) {
70 out->append("a_out");
71 } else if (t.value_ == 100) {
72 out->append("b_out");
73 } else {
74 throw std::runtime_error("Can't convert UserDefinedType to string");
75 }
76 }
77
78 FOLLY_SETTING_DEFINE(
79 follytest,
80 user_defined,
81 UserDefinedType,
82 "b",
83 "User defined type constructed from string");
84
85 } // namespace some_ns
86
TEST(Settings,user_defined)87 TEST(Settings, user_defined) {
88 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 100);
89 {
90 folly::settings::Snapshot sn;
91 EXPECT_TRUE(sn.setFromString("follytest_user_defined", "a", "test"));
92 sn.publish();
93 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 0);
94 }
95 {
96 folly::settings::Snapshot sn;
97 auto info = sn.getAsString("follytest_user_defined");
98 EXPECT_TRUE(info.has_value());
99 EXPECT_EQ(info->first, "a_out");
100 EXPECT_EQ(info->second, "test");
101 }
102 {
103 folly::settings::Snapshot sn;
104 EXPECT_THROW(
105 sn.setFromString("follytest_user_defined", "c", "test2"),
106 std::runtime_error);
107 sn.publish();
108 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 0);
109 }
110 {
111 folly::settings::Snapshot sn;
112 auto info = sn.getAsString("follytest_user_defined");
113 EXPECT_TRUE(info.has_value());
114 EXPECT_EQ(info->first, "a_out");
115 EXPECT_EQ(info->second, "test");
116 }
117 {
118 folly::settings::Snapshot sn;
119 EXPECT_TRUE(sn.resetToDefault("follytest_user_defined"));
120 sn.publish();
121 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 100);
122 }
123 {
124 folly::settings::Snapshot sn;
125 auto info = sn.getAsString("follytest_user_defined");
126 EXPECT_TRUE(info.has_value());
127 EXPECT_EQ(info->first, "b_out");
128 EXPECT_EQ(info->second, "default");
129 }
130 /* Test that intentionally setting to something non-converteable fails */
131 some_ns::UserDefinedType bad("a");
132 bad.value_ = 50;
133 EXPECT_THROW(
134 some_ns::FOLLY_SETTING(follytest, user_defined).set(bad),
135 std::runtime_error);
136 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, user_defined)->value_, 100);
137 {
138 folly::settings::Snapshot sn;
139 auto info = sn.getAsString("follytest_user_defined");
140 EXPECT_TRUE(info.has_value());
141 EXPECT_EQ(info->first, "b_out");
142 EXPECT_EQ(info->second, "default");
143 }
144 }
145
TEST(Settings,basic)146 TEST(Settings, basic) {
147 EXPECT_EQ(a_ns::a_func(), 1245);
148 EXPECT_EQ(b_ns::b_func(), "testbasdf");
149 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "default");
150 // Test -> API
151 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, some_flag)->size(), 7);
152 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).set(100);
153 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 100);
154 EXPECT_EQ(a_ns::getRemote(), 100);
155 a_ns::setRemote(200);
156 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 200);
157 EXPECT_EQ(a_ns::getRemote(), 200);
158 {
159 folly::settings::Snapshot sn;
160 auto res = sn.getAsString("follytest_public_flag_to_a");
161 EXPECT_TRUE(res.has_value());
162 EXPECT_EQ(res->first, "200");
163 EXPECT_EQ(res->second, "remote_set");
164 }
165 {
166 auto meta = folly::settings::getSettingsMeta("follytest_public_flag_to_a");
167 EXPECT_TRUE(meta.has_value());
168 const auto& md = meta.value();
169 EXPECT_EQ(md.project, "follytest");
170 EXPECT_EQ(md.name, "public_flag_to_a");
171 EXPECT_EQ(md.typeStr, "int");
172 EXPECT_EQ(md.typeId, typeid(int));
173 }
174 {
175 auto meta = folly::settings::getSettingsMeta("follytest_some_flag");
176 EXPECT_TRUE(meta.has_value());
177 const auto& md = meta.value();
178 EXPECT_EQ(md.project, "follytest");
179 EXPECT_EQ(md.name, "some_flag");
180 EXPECT_EQ(md.typeStr, "std::string");
181 EXPECT_EQ(md.typeId, typeid(std::string));
182 }
183 {
184 folly::settings::Snapshot sn;
185 auto res = sn.getAsString("follytest_nonexisting");
186 EXPECT_FALSE(res.has_value());
187 }
188 {
189 folly::settings::Snapshot sn;
190 EXPECT_TRUE(
191 sn.setFromString("follytest_public_flag_to_a", "300", "from_string"));
192 sn.publish();
193 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 300);
194 }
195 EXPECT_EQ(a_ns::getRemote(), 300);
196 {
197 folly::settings::Snapshot sn;
198 auto res = sn.getAsString("follytest_public_flag_to_a");
199 EXPECT_TRUE(res.has_value());
200 EXPECT_EQ(res->first, "300");
201 EXPECT_EQ(res->second, "from_string");
202 }
203 {
204 folly::settings::Snapshot sn;
205 EXPECT_FALSE(
206 sn.setFromString("follytest_nonexisting", "300", "from_string"));
207 }
208 EXPECT_EQ(
209 some_ns::FOLLY_SETTING(follytest, multi_token_type).defaultValue(), 123);
210 EXPECT_EQ(
211 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).defaultValue(), 456);
212 EXPECT_EQ(
213 b_ns::FOLLY_SETTING(follytest, public_flag_to_b).defaultValue(), "basdf");
214 EXPECT_EQ(
215 some_ns::FOLLY_SETTING(follytest, some_flag).defaultValue(), "default");
216 EXPECT_EQ(
217 some_ns::FOLLY_SETTING(follytest, user_defined).defaultValue(),
218 some_ns::UserDefinedType("b"));
219 {
220 std::string allFlags;
221 folly::settings::Snapshot sn;
222 sn.forEachSetting([&allFlags](
223 const folly::settings::SettingMetadata& meta,
224 folly::StringPiece value,
225 folly::StringPiece reason) {
226 if (meta.typeId == typeid(int)) {
227 EXPECT_EQ(meta.typeStr, "int");
228 } else if (meta.typeId == typeid(std::string)) {
229 EXPECT_EQ(meta.typeStr, "std::string");
230 } else if (meta.typeId == typeid(unsigned int)) {
231 EXPECT_EQ(meta.typeStr, "unsigned int");
232 } else if (meta.typeId == typeid(some_ns::UserDefinedType)) {
233 EXPECT_EQ(meta.typeStr, "UserDefinedType");
234 } else {
235 ASSERT_FALSE(true);
236 }
237 allFlags += folly::sformat(
238 "{}/{}/{}/{}/{}/{}/{}\n",
239 meta.project,
240 meta.name,
241 meta.typeStr,
242 meta.defaultStr,
243 meta.description,
244 value,
245 reason);
246 });
247 EXPECT_EQ(
248 allFlags,
249 "follytest/internal_flag_to_a/int/789/Desc of int/789/default\n"
250 "follytest/internal_flag_to_b/std::string/\"test\"/Desc of str/test/default\n"
251 "follytest/multi_token_type/unsigned int/123/Test that multi-token type names can be used/123/default\n"
252 "follytest/public_flag_to_a/int/456/Public flag to a/300/from_string\n"
253 "follytest/public_flag_to_b/std::string/\"basdf\"/Public flag to b/basdf/default\n"
254 "follytest/some_flag/std::string/\"default\"/Description/default/default\n"
255 "follytest/unused/std::string/\"unused_default\"/Not used, but should still be in the list/unused_default/default\n"
256 "follytest/user_defined/UserDefinedType/\"b\"/User defined type constructed from string/b_out/default\n");
257 }
258 {
259 folly::settings::Snapshot sn;
260 EXPECT_TRUE(sn.resetToDefault("follytest_public_flag_to_a"));
261 sn.publish();
262 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 456);
263 EXPECT_EQ(a_ns::getRemote(), 456);
264 }
265 {
266 folly::settings::Snapshot sn;
267 EXPECT_FALSE(sn.resetToDefault("follytest_nonexisting"));
268 }
269 }
270
TEST(Settings,snapshot)271 TEST(Settings, snapshot) {
272 // Test discarding a snapshot
273 {
274 folly::settings::Snapshot snapshot;
275
276 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "default");
277 EXPECT_EQ(
278 *snapshot(some_ns::FOLLY_SETTING(follytest, some_flag)), "default");
279
280 // Set the global value, snapshot doesn't see it
281 some_ns::FOLLY_SETTING(follytest, some_flag).set("global_value");
282 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "global_value");
283 EXPECT_EQ(
284 *snapshot(some_ns::FOLLY_SETTING(follytest, some_flag)), "default");
285 EXPECT_EQ(
286 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot),
287 "default");
288
289 // Set the value in the snapshot only
290 snapshot(some_ns::FOLLY_SETTING(follytest, some_flag))
291 .set("snapshot_value");
292 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "global_value");
293 EXPECT_EQ(
294 some_ns::FOLLY_SETTING(follytest, some_flag).value(), "global_value");
295 EXPECT_EQ(
296 *snapshot(some_ns::FOLLY_SETTING(follytest, some_flag)),
297 "snapshot_value");
298 EXPECT_EQ(
299 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot),
300 "snapshot_value");
301 }
302 // Discard the snapshot
303 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "global_value");
304
305 // Test publishing a snapshot
306 {
307 folly::settings::Snapshot snapshot;
308
309 // Set the value in the snapshot only
310 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "global_value");
311 EXPECT_EQ(
312 *snapshot(some_ns::FOLLY_SETTING(follytest, some_flag)),
313 "global_value");
314 EXPECT_EQ(
315 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot),
316 "global_value");
317 snapshot(some_ns::FOLLY_SETTING(follytest, some_flag))
318 .set("snapshot_value2");
319 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "global_value");
320 EXPECT_EQ(
321 *snapshot(some_ns::FOLLY_SETTING(follytest, some_flag)),
322 "snapshot_value2");
323 EXPECT_EQ(
324 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot),
325 "snapshot_value2");
326
327 // Set the global value, snapshot doesn't see it
328 some_ns::FOLLY_SETTING(follytest, some_flag).set("global_value2");
329 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "global_value2");
330 EXPECT_EQ(
331 *snapshot(some_ns::FOLLY_SETTING(follytest, some_flag)),
332 "snapshot_value2");
333 EXPECT_EQ(
334 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot),
335 "snapshot_value2");
336 snapshot.publish();
337 }
338
339 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "snapshot_value2");
340
341 // Snapshots at different points in time
342 {
343 some_ns::FOLLY_SETTING(follytest, some_flag).set("a");
344 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).set(123);
345
346 folly::settings::Snapshot snapshot_1;
347
348 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "a");
349 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, some_flag).value(), "a");
350 EXPECT_EQ(*snapshot_1(some_ns::FOLLY_SETTING(follytest, some_flag)), "a");
351 EXPECT_EQ(
352 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_1), "a");
353
354 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 123);
355 EXPECT_EQ(a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(), 123);
356 EXPECT_EQ(
357 *snapshot_1(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
358 EXPECT_EQ(
359 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_1),
360 123);
361
362 some_ns::FOLLY_SETTING(follytest, some_flag).set("b");
363 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "b");
364 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, some_flag).value(), "b");
365 EXPECT_EQ(*snapshot_1(some_ns::FOLLY_SETTING(follytest, some_flag)), "a");
366 EXPECT_EQ(
367 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_1), "a");
368
369 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 123);
370 EXPECT_EQ(a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(), 123);
371 EXPECT_EQ(
372 *snapshot_1(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
373 EXPECT_EQ(
374 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_1),
375 123);
376
377 folly::settings::Snapshot snapshot_2;
378 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "b");
379 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, some_flag).value(), "b");
380 EXPECT_EQ(*snapshot_1(some_ns::FOLLY_SETTING(follytest, some_flag)), "a");
381 EXPECT_EQ(*snapshot_2(some_ns::FOLLY_SETTING(follytest, some_flag)), "b");
382 EXPECT_EQ(
383 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_1), "a");
384 EXPECT_EQ(
385 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_2), "b");
386
387 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 123);
388 EXPECT_EQ(a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(), 123);
389 EXPECT_EQ(
390 *snapshot_1(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
391 EXPECT_EQ(
392 *snapshot_2(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
393 EXPECT_EQ(
394 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_1),
395 123);
396 EXPECT_EQ(
397 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_2),
398 123);
399
400 some_ns::FOLLY_SETTING(follytest, some_flag).set("c");
401 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "c");
402 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, some_flag).value(), "c");
403 EXPECT_EQ(*snapshot_1(some_ns::FOLLY_SETTING(follytest, some_flag)), "a");
404 EXPECT_EQ(*snapshot_2(some_ns::FOLLY_SETTING(follytest, some_flag)), "b");
405 EXPECT_EQ(
406 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_1), "a");
407 EXPECT_EQ(
408 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_2), "b");
409
410 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 123);
411 EXPECT_EQ(a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(), 123);
412 EXPECT_EQ(
413 *snapshot_1(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
414 EXPECT_EQ(
415 *snapshot_2(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
416 EXPECT_EQ(
417 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_1),
418 123);
419 EXPECT_EQ(
420 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_2),
421 123);
422
423 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).set(456);
424 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "c");
425 EXPECT_EQ(some_ns::FOLLY_SETTING(follytest, some_flag).value(), "c");
426 EXPECT_EQ(*snapshot_1(some_ns::FOLLY_SETTING(follytest, some_flag)), "a");
427 EXPECT_EQ(*snapshot_2(some_ns::FOLLY_SETTING(follytest, some_flag)), "b");
428 EXPECT_EQ(
429 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_1), "a");
430 EXPECT_EQ(
431 some_ns::FOLLY_SETTING(follytest, some_flag).value(snapshot_2), "b");
432
433 EXPECT_EQ(*a_ns::FOLLY_SETTING(follytest, public_flag_to_a), 456);
434 EXPECT_EQ(a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(), 456);
435 EXPECT_EQ(
436 *snapshot_1(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
437 EXPECT_EQ(
438 *snapshot_2(a_ns::FOLLY_SETTING(follytest, public_flag_to_a)), 123);
439 EXPECT_EQ(
440 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_1),
441 123);
442 EXPECT_EQ(
443 a_ns::FOLLY_SETTING(follytest, public_flag_to_a).value(snapshot_2),
444 123);
445 }
446 }
447
TEST(SettingsTest,callback)448 TEST(SettingsTest, callback) {
449 size_t callbackInvocations = 0;
450 std::string lastCallbackValue;
451
452 EXPECT_EQ(*some_ns::FOLLY_SETTING(follytest, some_flag), "default");
453 {
454 auto handle = some_ns::FOLLY_SETTING(follytest, some_flag)
455 .addCallback([&](const auto& contents) {
456 ++callbackInvocations;
457 lastCallbackValue = contents.value;
458 });
459
460 some_ns::FOLLY_SETTING(follytest, some_flag).set("a");
461 EXPECT_EQ(callbackInvocations, 1);
462 EXPECT_EQ(lastCallbackValue, "a");
463
464 size_t secondCallbackInvocations = 0;
465 // Test adding multiple callbacks and letting the handle go out of scope
466 {
467 auto secondHandle = some_ns::FOLLY_SETTING(follytest, some_flag)
468 .addCallback([&](const auto& /* contents */) {
469 ++secondCallbackInvocations;
470 });
471 some_ns::FOLLY_SETTING(follytest, some_flag).set("b");
472 EXPECT_EQ(callbackInvocations, 2);
473 EXPECT_EQ(lastCallbackValue, "b");
474 EXPECT_EQ(secondCallbackInvocations, 1);
475 }
476
477 some_ns::FOLLY_SETTING(follytest, some_flag).set("c");
478 EXPECT_EQ(callbackInvocations, 3);
479 EXPECT_EQ(lastCallbackValue, "c");
480 // Second callback no longer invoked
481 EXPECT_EQ(secondCallbackInvocations, 1);
482
483 auto movedHandle = std::move(handle);
484 some_ns::FOLLY_SETTING(follytest, some_flag).set("d");
485 EXPECT_EQ(callbackInvocations, 4);
486 EXPECT_EQ(lastCallbackValue, "d");
487 }
488 // Main callback no longer invoked
489 some_ns::FOLLY_SETTING(follytest, some_flag).set("e");
490 EXPECT_EQ(callbackInvocations, 4);
491 EXPECT_EQ(lastCallbackValue, "d");
492 }
493