properties_test.cxx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. //
  2. // properties_test.cpp
  3. // logger-test
  4. //
  5. // Created by Sam Jaffe on 8/12/20.
  6. //
  7. #include "logger/properties.h"
  8. #include <gmock/gmock.h>
  9. #include "logger/exception.h"
  10. namespace logging {
  11. void indent(int i, std::ostream * os) {
  12. while (i-->0) {
  13. (*os) << ' ' << ' ';
  14. }
  15. }
  16. void PrintTo(properties const & props, std::ostream * os, int level = 1) {
  17. switch (props.data.index()) {
  18. case 0:
  19. (*os) << '{' << '\n';
  20. for (auto & [k, v] : props.object()) {
  21. indent(level + 1, os);
  22. (*os) << k << ':' << ' ';
  23. PrintTo(v, os, level + 1);
  24. }
  25. (*os) << '\n';
  26. indent(level, os);
  27. (*os) << '}' << '\n';
  28. break;
  29. case 1:
  30. (*os) << '[' << '\n';
  31. for (auto & v : props.array()) {
  32. indent(level + 1, os);
  33. PrintTo(v, os, level + 1);
  34. }
  35. (*os) << '\n';
  36. indent(level, os);
  37. (*os) << ']' << '\n';
  38. break;
  39. case 2:
  40. (*os) << props.str();
  41. break;
  42. case 3:
  43. (*os) << int(props);
  44. break;
  45. case 4:
  46. (*os) << std::boolalpha << bool(props);
  47. break;
  48. default:
  49. (*os) << "null";
  50. break;
  51. }
  52. }
  53. }
  54. MATCHER(Valid, "") {
  55. return arg.data.valid();
  56. }
  57. MATCHER_P(Contains, key, "") {
  58. return arg.contains(key);
  59. }
  60. MATCHER_P(IsOfType, type, "") {
  61. return arg.data.template is<decltype(type)>();
  62. }
  63. MATCHER_P(ArraySizeIs, size, "") {
  64. return arg.data.template is<logging::array_t>() && arg.array().size() == size;
  65. }
  66. template <typename T>
  67. auto IsOfType() { return IsOfType(T()); }
  68. using namespace logging;
  69. using namespace ::logging::property;
  70. TEST(PropertiesTest, DefaultPropertyIsInvalidType) {
  71. EXPECT_THAT(properties(), testing::Not(Valid()));
  72. }
  73. TEST(PropertiesTest, CanStoreBoolean) {
  74. EXPECT_THAT(_v(true), IsOfType<bool>());
  75. EXPECT_THAT(_v(true), Valid());
  76. }
  77. TEST(PropertiesTest, CanStoreInteger) {
  78. EXPECT_THAT(_v(0), IsOfType<int>());
  79. EXPECT_THAT(_v(0), Valid());
  80. }
  81. TEST(PropertiesTest, CanStoreString) {
  82. EXPECT_THAT(_v("hello"), IsOfType<std::string>());
  83. EXPECT_THAT(_v("hello"), Valid());
  84. }
  85. TEST(PropertiesTest, CanStoreArray) {
  86. EXPECT_THAT(_arr({}), IsOfType<array_t>());
  87. EXPECT_THAT(_arr({}), Valid());
  88. }
  89. TEST(PropertiesTest, CanStoreObject) {
  90. EXPECT_THAT(_obj({}), IsOfType<object_t>());
  91. EXPECT_THAT(_obj({}), Valid());
  92. }
  93. TEST(PropertiesTest, ArrContainsReturnsTrueEvenWhenUnderlyingIsInvalid) {
  94. properties const props = _arr({{}});
  95. EXPECT_THAT(props, Contains(0));
  96. EXPECT_THAT(props[0ul], testing::Not(Valid()));
  97. }
  98. TEST(PropertiesTest, ObjContainsReturnsTrueEvenWhenUnderlyingIsInvalid) {
  99. properties const props = _obj({{"data", {}}});
  100. EXPECT_THAT(props, Contains("data"));
  101. EXPECT_THAT(props["data"], testing::Not(Valid()));
  102. }
  103. TEST(PropertiesTest, ContainsAlwaysFalseForNonContainer) {
  104. EXPECT_THAT(_v(true), testing::Not(Contains(0)));
  105. EXPECT_THAT(_v(true), testing::Not(Contains("")));
  106. EXPECT_THAT(_v(0), testing::Not(Contains(0)));
  107. EXPECT_THAT(_v(0), testing::Not(Contains("")));
  108. EXPECT_THAT(_v(""), testing::Not(Contains(0)));
  109. EXPECT_THAT(_v(""), testing::Not(Contains("")));
  110. }
  111. TEST(PropertiesTest, ArrayCannotContainKey) {
  112. properties const props = _arr({{}});
  113. EXPECT_THAT(props, testing::Not(Contains("")));
  114. }
  115. TEST(PropertiesTest, ObjectCannotContainIndex) {
  116. properties const props = _obj({{"data", {}}});
  117. EXPECT_THAT(props, testing::Not(Contains(0)));
  118. }
  119. TEST(PropertiesTest, MistypingWillThrow) {
  120. properties prop;
  121. // Avoid most vexing parse/unused variable
  122. EXPECT_THROW((void) bool(prop), invalid_property_type);
  123. EXPECT_THROW((void) int(prop), invalid_property_type);
  124. EXPECT_THROW(prop.str(), invalid_property_type);
  125. EXPECT_THROW(prop.array(), invalid_property_type);
  126. EXPECT_THROW(prop.object(), invalid_property_type);
  127. }
  128. TEST(PropertiesTest, IndexingNonContainerWillThrow) {
  129. properties prop;
  130. EXPECT_THROW(prop[0ul], invalid_property_type);
  131. EXPECT_THROW(prop[""], invalid_property_type);
  132. }
  133. TEST(PropertiesTest, IndexingContainerWithWrongTypeWillThrow) {
  134. EXPECT_THROW(_obj({{"", {}}})[0ul], invalid_property_type);
  135. EXPECT_THROW(_arr({{}})[""], invalid_property_type);
  136. }
  137. TEST(PropertiesTest, IndexingNonExistantPathWillThrouw) {
  138. EXPECT_THROW(_obj({{"", {}}})["data"], missing_property);
  139. EXPECT_THROW(_arr({{}})[1], missing_property);
  140. }
  141. properties const EXAMPLE_PROPERTIES = _obj({
  142. {"data", _obj({
  143. {"files", _arr({_v("a.txt")})},
  144. {"config", _obj({
  145. {"append", _v(false)}
  146. })}
  147. })}
  148. });
  149. TEST(PropertiesTest, MergeWithWillNotAddNewProperties) {
  150. properties const input = _obj({
  151. {"data", _obj({
  152. {"config", _obj({
  153. {"compact", _v(true)}
  154. })}
  155. })}
  156. });
  157. properties const output = EXAMPLE_PROPERTIES.mergedWith(input);
  158. EXPECT_THAT(output, Contains("data"));
  159. EXPECT_THAT(output["data"], Contains("files"));
  160. EXPECT_THAT(output["data"]["files"], ArraySizeIs(1));
  161. EXPECT_THAT(output["data"], Contains("config"));
  162. EXPECT_THAT(output["data"]["config"], Contains("append"));
  163. EXPECT_THAT(output["data"]["config"], testing::Not(Contains("compact")));
  164. }
  165. TEST(PropertiesTest, MergeWithWillOverwriteProperties) {
  166. properties const input = _obj({
  167. {"data", _obj({
  168. {"config", _obj({
  169. {"append", _v(true)}
  170. })}
  171. })}
  172. });
  173. properties const output = EXAMPLE_PROPERTIES.mergedWith(input);
  174. EXPECT_THAT(output, Contains("data"));
  175. EXPECT_THAT(output["data"], Contains("files"));
  176. EXPECT_THAT(output["data"]["files"], ArraySizeIs(1));
  177. EXPECT_THAT(output["data"], Contains("config"));
  178. EXPECT_THAT(output["data"]["config"], Contains("append"));
  179. EXPECT_TRUE(output["data"]["config"]["append"]);
  180. }
  181. TEST(PropertiesTest, ArraysWillBeOverwritten) {
  182. properties const input = _obj({
  183. {"data", _obj({
  184. {"files", _arr({_v("b.txt"), _v("c.txt")})}
  185. })}
  186. });
  187. properties const output = EXAMPLE_PROPERTIES.mergedWith(input);
  188. EXPECT_THAT(output, Contains("data"));
  189. EXPECT_THAT(output["data"], Contains("files"));
  190. EXPECT_THAT(output["data"]["files"], ArraySizeIs(2));
  191. EXPECT_THAT(output["data"]["files"][0ul].str(), "b.txt");
  192. EXPECT_THAT(output["data"]["files"][1ul].str(), "c.txt");
  193. }