Ver código fonte

Add test coverage for properties.cxx

Sam Jaffe 5 anos atrás
pai
commit
62bd2bdf46

+ 8 - 4
logger.xcodeproj/project.pbxproj

@@ -30,9 +30,10 @@
 		CDCB3C6D24E481E70029B771 /* file_appender_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD760CC0226226CC008A62DE /* file_appender_test.cxx */; };
 		CDCB3C6E24E481E70029B771 /* c_logger_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8C22540D9B00E5B6B2 /* c_logger_test.cxx */; };
 		CDCB3C6F24E481E70029B771 /* log_manager_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8F22542CC500E5B6B2 /* log_manager_test.cxx */; };
-		CDCB3C7024E481E70029B771 /* test_properties.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE9122543E7E00E5B6B2 /* test_properties.cxx */; };
+		CDCB3C7024E481E70029B771 /* mock_properties.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE9122543E7E00E5B6B2 /* mock_properties.cxx */; };
 		CDCB3C7124E481F50029B771 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6F7437225189470081ED74 /* GoogleMock.framework */; };
 		CDCB3C7224E482020029B771 /* libjsoncpp.1.9.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CDCB3C5724E480E70029B771 /* libjsoncpp.1.9.2.dylib */; };
+		CDCB3C9C24E4C8510029B771 /* properties_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDCB3C9B24E4C8510029B771 /* properties_test.cxx */; };
 		CDEA62D5225A3B0B00A6FAE0 /* json_layout.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDEA62D4225A3B0B00A6FAE0 /* json_layout.cxx */; };
 /* End PBXBuildFile section */
 
@@ -83,7 +84,7 @@
 		CD1CDE8C22540D9B00E5B6B2 /* c_logger_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = c_logger_test.cxx; sourceTree = "<group>"; };
 		CD1CDE8E22540DEA00E5B6B2 /* mock_logger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mock_logger.h; sourceTree = "<group>"; };
 		CD1CDE8F22542CC500E5B6B2 /* log_manager_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = log_manager_test.cxx; sourceTree = "<group>"; };
-		CD1CDE9122543E7E00E5B6B2 /* test_properties.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = test_properties.cxx; sourceTree = "<group>"; };
+		CD1CDE9122543E7E00E5B6B2 /* mock_properties.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = mock_properties.cxx; sourceTree = "<group>"; };
 		CD1CDEAE22556B7E00E5B6B2 /* logger_impl.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = logger_impl.cxx; sourceTree = "<group>"; };
 		CD1CDEB022557FB600E5B6B2 /* default_layout.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = default_layout.cxx; sourceTree = "<group>"; };
 		CD1CDEB42256C94000E5B6B2 /* pattern_layout.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = pattern_layout.cxx; sourceTree = "<group>"; };
@@ -112,6 +113,7 @@
 		CDCB3C5724E480E70029B771 /* libjsoncpp.1.9.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libjsoncpp.1.9.2.dylib; path = ../../../../../../../../opt/local/lib/libjsoncpp.1.9.2.dylib; sourceTree = "<group>"; };
 		CDCB3C5E24E481B10029B771 /* logger-test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "logger-test.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDCB3C6224E481B10029B771 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		CDCB3C9B24E4C8510029B771 /* properties_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = properties_test.cxx; sourceTree = "<group>"; };
 		CDEA62D4225A3B0B00A6FAE0 /* json_layout.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = json_layout.cxx; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -195,13 +197,14 @@
 			isa = PBXGroup;
 			children = (
 				CD6F73FC225187E10081ED74 /* logger_test.cxx */,
+				CDCB3C9B24E4C8510029B771 /* properties_test.cxx */,
 				CD760CB822621776008A62DE /* pattern_layout_test.cxx */,
 				CD760CC822627202008A62DE /* json_layout_test.cxx */,
 				CD760CBE226221F6008A62DE /* console_appender_test.cxx */,
 				CD760CC0226226CC008A62DE /* file_appender_test.cxx */,
 				CD1CDE8C22540D9B00E5B6B2 /* c_logger_test.cxx */,
 				CD1CDE8F22542CC500E5B6B2 /* log_manager_test.cxx */,
-				CD1CDE9122543E7E00E5B6B2 /* test_properties.cxx */,
+				CD1CDE9122543E7E00E5B6B2 /* mock_properties.cxx */,
 				CD1CDE8E22540DEA00E5B6B2 /* mock_logger.h */,
 				CDC0E06022694966001EDAB7 /* logger_test_obj.h */,
 				CDC0E0652269EE9E001EDAB7 /* header_test_obj.h */,
@@ -399,10 +402,11 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CDCB3C7024E481E70029B771 /* test_properties.cxx in Sources */,
+				CDCB3C7024E481E70029B771 /* mock_properties.cxx in Sources */,
 				CDCB3C6D24E481E70029B771 /* file_appender_test.cxx in Sources */,
 				CDCB3C6F24E481E70029B771 /* log_manager_test.cxx in Sources */,
 				CDCB3C6B24E481E70029B771 /* json_layout_test.cxx in Sources */,
+				CDCB3C9C24E4C8510029B771 /* properties_test.cxx in Sources */,
 				CDCB3C6A24E481E70029B771 /* pattern_layout_test.cxx in Sources */,
 				CDCB3C6C24E481E70029B771 /* console_appender_test.cxx in Sources */,
 				CDCB3C6924E481E70029B771 /* logger_test.cxx in Sources */,

+ 1 - 1
src/loggers/properties.cxx

@@ -81,7 +81,7 @@ namespace logging {
   }
   
   bool properties::contains(std::size_t idx) const {
-    return data.is<array_t>() && array().size() < idx;
+    return data.is<array_t>() && array().size() > idx;
   }
   
   object_t const & properties::object() const {

test/test_properties.cxx → test/mock_properties.cxx


+ 222 - 0
test/properties_test.cxx

@@ -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");
+}