|
|
@@ -0,0 +1,222 @@
|
|
|
+//
|
|
|
+// properties_test.cpp
|
|
|
+// logger-test
|
|
|
+//
|
|
|
+// Created by Sam Jaffe on 8/12/20.
|
|
|
+//
|
|
|
+
|
|
|
+#include "logger/properties.h"
|
|
|
+
|
|
|
+#include <gmock/gmock.h>
|
|
|
+
|
|
|
+#include "logger/exception.h"
|
|
|
+
|
|
|
+namespace logging {
|
|
|
+ void indent(int i, std::ostream * os) {
|
|
|
+ while (i-->0) {
|
|
|
+ (*os) << ' ' << ' ';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ void PrintTo(properties const & props, std::ostream * os, int level = 1) {
|
|
|
+ switch (props.data.index()) {
|
|
|
+ case 0:
|
|
|
+ (*os) << '{' << '\n';
|
|
|
+ for (auto & [k, v] : props.object()) {
|
|
|
+ indent(level + 1, os);
|
|
|
+ (*os) << k << ':' << ' ';
|
|
|
+ PrintTo(v, os, level + 1);
|
|
|
+ }
|
|
|
+ (*os) << '\n';
|
|
|
+ indent(level, os);
|
|
|
+ (*os) << '}' << '\n';
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ (*os) << '[' << '\n';
|
|
|
+ for (auto & v : props.array()) {
|
|
|
+ indent(level + 1, os);
|
|
|
+ PrintTo(v, os, level + 1);
|
|
|
+ }
|
|
|
+ (*os) << '\n';
|
|
|
+ indent(level, os);
|
|
|
+ (*os) << ']' << '\n';
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ (*os) << props.str();
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ (*os) << int(props);
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ (*os) << std::boolalpha << bool(props);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ (*os) << "null";
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+MATCHER(Valid, "") {
|
|
|
+ return arg.data.valid();
|
|
|
+}
|
|
|
+
|
|
|
+MATCHER_P(Contains, key, "") {
|
|
|
+ return arg.contains(key);
|
|
|
+}
|
|
|
+
|
|
|
+MATCHER_P(IsOfType, type, "") {
|
|
|
+ return arg.data.template is<decltype(type)>();
|
|
|
+}
|
|
|
+
|
|
|
+MATCHER_P(ArraySizeIs, size, "") {
|
|
|
+ return arg.data.template is<logging::array_t>() && arg.array().size() == size;
|
|
|
+}
|
|
|
+
|
|
|
+template <typename T>
|
|
|
+auto IsOfType() { return IsOfType(T()); }
|
|
|
+
|
|
|
+using namespace logging;
|
|
|
+using namespace ::logging::property;
|
|
|
+
|
|
|
+TEST(PropertiesTest, DefaultPropertyIsInvalidType) {
|
|
|
+ EXPECT_THAT(properties(), testing::Not(Valid()));
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, CanStoreBoolean) {
|
|
|
+ EXPECT_THAT(_v(true), IsOfType<bool>());
|
|
|
+ EXPECT_THAT(_v(true), Valid());
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, CanStoreInteger) {
|
|
|
+ EXPECT_THAT(_v(0), IsOfType<int>());
|
|
|
+ EXPECT_THAT(_v(0), Valid());
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, CanStoreString) {
|
|
|
+ EXPECT_THAT(_v("hello"), IsOfType<std::string>());
|
|
|
+ EXPECT_THAT(_v("hello"), Valid());
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, CanStoreArray) {
|
|
|
+ EXPECT_THAT(_arr({}), IsOfType<array_t>());
|
|
|
+ EXPECT_THAT(_arr({}), Valid());
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, CanStoreObject) {
|
|
|
+ EXPECT_THAT(_obj({}), IsOfType<object_t>());
|
|
|
+ EXPECT_THAT(_obj({}), Valid());
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, ArrContainsReturnsTrueEvenWhenUnderlyingIsInvalid) {
|
|
|
+ properties const props = _arr({{}});
|
|
|
+ EXPECT_THAT(props, Contains(0));
|
|
|
+ EXPECT_THAT(props[0ul], testing::Not(Valid()));
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, ObjContainsReturnsTrueEvenWhenUnderlyingIsInvalid) {
|
|
|
+ properties const props = _obj({{"data", {}}});
|
|
|
+ EXPECT_THAT(props, Contains("data"));
|
|
|
+ EXPECT_THAT(props["data"], testing::Not(Valid()));
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, ContainsAlwaysFalseForNonContainer) {
|
|
|
+ EXPECT_THAT(_v(true), testing::Not(Contains(0)));
|
|
|
+ EXPECT_THAT(_v(true), testing::Not(Contains("")));
|
|
|
+ EXPECT_THAT(_v(0), testing::Not(Contains(0)));
|
|
|
+ EXPECT_THAT(_v(0), testing::Not(Contains("")));
|
|
|
+ EXPECT_THAT(_v(""), testing::Not(Contains(0)));
|
|
|
+ EXPECT_THAT(_v(""), testing::Not(Contains("")));
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, ArrayCannotContainKey) {
|
|
|
+ properties const props = _arr({{}});
|
|
|
+ EXPECT_THAT(props, testing::Not(Contains("")));
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, ObjectCannotContainIndex) {
|
|
|
+ properties const props = _obj({{"data", {}}});
|
|
|
+ EXPECT_THAT(props, testing::Not(Contains(0)));
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, MistypingWillThrow) {
|
|
|
+ properties prop;
|
|
|
+ // Avoid most vexing parse/unused variable
|
|
|
+ EXPECT_THROW((void) bool(prop), invalid_property_type);
|
|
|
+ EXPECT_THROW((void) int(prop), invalid_property_type);
|
|
|
+ EXPECT_THROW(prop.str(), invalid_property_type);
|
|
|
+ EXPECT_THROW(prop.array(), invalid_property_type);
|
|
|
+ EXPECT_THROW(prop.object(), invalid_property_type);
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, IndexingNonContainerWillThrow) {
|
|
|
+ properties prop;
|
|
|
+ EXPECT_THROW(prop[0ul], invalid_property_type);
|
|
|
+ EXPECT_THROW(prop[""], invalid_property_type);
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, IndexingContainerWithWrongTypeWillThrow) {
|
|
|
+ EXPECT_THROW(_obj({{"", {}}})[0ul], invalid_property_type);
|
|
|
+ EXPECT_THROW(_arr({{}})[""], invalid_property_type);
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, IndexingNonExistantPathWillThrouw) {
|
|
|
+ EXPECT_THROW(_obj({{"", {}}})["data"], missing_property);
|
|
|
+ EXPECT_THROW(_arr({{}})[1], missing_property);
|
|
|
+}
|
|
|
+
|
|
|
+properties const EXAMPLE_PROPERTIES = _obj({
|
|
|
+ {"data", _obj({
|
|
|
+ {"files", _arr({_v("a.txt")})},
|
|
|
+ {"config", _obj({
|
|
|
+ {"append", _v(false)}
|
|
|
+ })}
|
|
|
+ })}
|
|
|
+});
|
|
|
+
|
|
|
+TEST(PropertiesTest, MergeWithWillNotAddNewProperties) {
|
|
|
+ properties const input = _obj({
|
|
|
+ {"data", _obj({
|
|
|
+ {"config", _obj({
|
|
|
+ {"compact", _v(true)}
|
|
|
+ })}
|
|
|
+ })}
|
|
|
+ });
|
|
|
+ properties const output = EXAMPLE_PROPERTIES.mergedWith(input);
|
|
|
+ EXPECT_THAT(output, Contains("data"));
|
|
|
+ EXPECT_THAT(output["data"], Contains("files"));
|
|
|
+ EXPECT_THAT(output["data"]["files"], ArraySizeIs(1));
|
|
|
+ EXPECT_THAT(output["data"], Contains("config"));
|
|
|
+ EXPECT_THAT(output["data"]["config"], Contains("append"));
|
|
|
+ EXPECT_THAT(output["data"]["config"], testing::Not(Contains("compact")));
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, MergeWithWillOverwriteProperties) {
|
|
|
+ properties const input = _obj({
|
|
|
+ {"data", _obj({
|
|
|
+ {"config", _obj({
|
|
|
+ {"append", _v(true)}
|
|
|
+ })}
|
|
|
+ })}
|
|
|
+ });
|
|
|
+ properties const output = EXAMPLE_PROPERTIES.mergedWith(input);
|
|
|
+ EXPECT_THAT(output, Contains("data"));
|
|
|
+ EXPECT_THAT(output["data"], Contains("files"));
|
|
|
+ EXPECT_THAT(output["data"]["files"], ArraySizeIs(1));
|
|
|
+ EXPECT_THAT(output["data"], Contains("config"));
|
|
|
+ EXPECT_THAT(output["data"]["config"], Contains("append"));
|
|
|
+ EXPECT_TRUE(output["data"]["config"]["append"]);
|
|
|
+}
|
|
|
+
|
|
|
+TEST(PropertiesTest, ArraysWillBeOverwritten) {
|
|
|
+ properties const input = _obj({
|
|
|
+ {"data", _obj({
|
|
|
+ {"files", _arr({_v("b.txt"), _v("c.txt")})}
|
|
|
+ })}
|
|
|
+ });
|
|
|
+ properties const output = EXAMPLE_PROPERTIES.mergedWith(input);
|
|
|
+ EXPECT_THAT(output, Contains("data"));
|
|
|
+ EXPECT_THAT(output["data"], Contains("files"));
|
|
|
+ EXPECT_THAT(output["data"]["files"], ArraySizeIs(2));
|
|
|
+ EXPECT_THAT(output["data"]["files"][0ul].str(), "b.txt");
|
|
|
+ EXPECT_THAT(output["data"]["files"][1ul].str(), "c.txt");
|
|
|
+}
|