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