Bladeren bron

Move exceptions to own file.
Add tests for manager code

Sam Jaffe 6 jaren geleden
bovenliggende
commit
90a1e3e5ed

+ 24 - 0
include/logger/exception.h

@@ -0,0 +1,24 @@
+
+#pragma once
+
+namespace logging {
+  class format_parsing_exception : public std::logic_error {
+  public:
+    using std::logic_error::logic_error;
+  };
+
+  class unknown_format_specifier : public std::logic_error {
+  public:
+    using std::logic_error::logic_error;
+  };
+
+  class missing_property : public std::logic_error {
+  public:
+    using std::logic_error::logic_error;
+  };
+
+  class invalid_property_type : public std::logic_error {
+  public:
+    using std::logic_error::logic_error;
+  };
+}

+ 1 - 1
include/logger/log_manager.h

@@ -31,8 +31,8 @@ namespace logging {
 
     void configure(properties const & props);
 
-  private:
     manager();
+    ~manager();
     
   private:
     std::unique_ptr<struct manager_impl> pimpl_;

+ 8 - 0
logger.xcodeproj/project.pbxproj

@@ -11,6 +11,8 @@
 		CD1CDE892252E60900E5B6B2 /* file_logger.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE882252E60900E5B6B2 /* file_logger.cxx */; };
 		CD1CDE8B2252E61800E5B6B2 /* console_logger.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8A2252E61800E5B6B2 /* console_logger.cxx */; };
 		CD1CDE8D22540D9B00E5B6B2 /* c_logger_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8C22540D9B00E5B6B2 /* c_logger_test.cxx */; };
+		CD1CDE9022542CC500E5B6B2 /* log_manager_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE8F22542CC500E5B6B2 /* log_manager_test.cxx */; };
+		CD1CDE9222543E7E00E5B6B2 /* test_properties.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD1CDE9122543E7E00E5B6B2 /* test_properties.cxx */; };
 		CD29739B1D7B401F00E37217 /* logger.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD2973991D7B401F00E37217 /* logger.cxx */; };
 		CD3C80C01D6A2CA300ACC795 /* format.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD3C80BE1D6A2CA300ACC795 /* format.cxx */; };
 		CD6F73EC225187BE0081ED74 /* logger in Headers */ = {isa = PBXBuildFile; fileRef = CD6F73EA225187A10081ED74 /* logger */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -68,6 +70,8 @@
 		CD1CDE8A2252E61800E5B6B2 /* console_logger.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = console_logger.cxx; sourceTree = "<group>"; };
 		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>"; };
 		CD2973991D7B401F00E37217 /* logger.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = logger.cxx; sourceTree = "<group>"; };
 		CD3C80BE1D6A2CA300ACC795 /* format.cxx */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = format.cxx; sourceTree = "<group>"; };
 		CD6F73EA225187A10081ED74 /* logger */ = {isa = PBXFileReference; lastKnownFileType = folder; name = logger; path = include/logger; sourceTree = "<group>"; };
@@ -154,6 +158,8 @@
 			children = (
 				CD6F73FC225187E10081ED74 /* logger_test.cxx */,
 				CD1CDE8C22540D9B00E5B6B2 /* c_logger_test.cxx */,
+				CD1CDE8F22542CC500E5B6B2 /* log_manager_test.cxx */,
+				CD1CDE9122543E7E00E5B6B2 /* test_properties.cxx */,
 				CD1CDE8E22540DEA00E5B6B2 /* mock_logger.h */,
 			);
 			path = test;
@@ -340,7 +346,9 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD1CDE9022542CC500E5B6B2 /* log_manager_test.cxx in Sources */,
 				CD6F740C225187FD0081ED74 /* logger_test.cxx in Sources */,
+				CD1CDE9222543E7E00E5B6B2 /* test_properties.cxx in Sources */,
 				CD1CDE8D22540D9B00E5B6B2 /* c_logger_test.cxx in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;

+ 3 - 13
src/format.cxx

@@ -5,6 +5,8 @@
 //  Created by Sam Jaffe on 8/21/16.
 //
 
+#include "logger/format.h"
+
 #include <cstdint>
 #include <cstring>
 #include <ctime>
@@ -14,8 +16,8 @@
 #include <stdexcept>
 #include <string>
 
-#include "logger/format.h"
 #include "common.h"
+#include "logger/exception.h"
 #include "logger/logger.h"
 
 #if defined( _WIN32 )
@@ -24,18 +26,6 @@
 # define NEWLINE "\n"
 #endif
 
-namespace logging {
-  class format_parsing_exception : public std::logic_error {
-  public:
-    using std::logic_error::logic_error;
-  };
-  
-  class unknown_format_specifier : public std::logic_error {
-  public:
-    using std::logic_error::logic_error;
-  };
-}
-
 namespace logging {
   namespace {
     std::string fmt_time(struct timeval round, char const * const fmt) {

+ 11 - 8
src/log_manager.cxx

@@ -14,6 +14,7 @@
 #include "../../../types/resource_factory/include/resource_factory/prototype_factory.hpp"
 
 #include "logger/c_logger.h"
+#include "logger/exception.h"
 #include "logger/logger.h"
 #include "logger/logger_impl.h"
 #include "logger/properties.h"
@@ -32,6 +33,8 @@ struct logging::manager_impl {
 
 manager::manager() : pimpl_(new manager_impl) {}
 
+manager::~manager() {}
+
 logger manager::get() {
   return logger("", pimpl_->default_logger);
 }
@@ -63,18 +66,17 @@ struct compound_logger : public logger_impl {
 
 std::shared_ptr<logger_impl>
 manager_impl::get_logger(properties const & props) {
-  auto & appends = props["appenders"];
-  if (appends.type == properties::OBJECT) {
+  if (props.type == properties::OBJECT) {
     // TODO include settings on the parent
-    return appenders.at(appends["ref"].value);
-  } else if (appends.type == properties::ARRAY) {
+    return appenders.at(props["ref"].value);
+  } else if (props.type == properties::ARRAY) {
     auto out = std::make_shared<compound_logger>();
-    for (auto & part : appends.array) {
+    for (auto & part : props.array) {
       out->values.push_back(get_logger(part));
     }
     return out;
   } else {
-    throw;
+    throw invalid_property_type{"appenders cannot be a STRING"};
   }
 }
 
@@ -92,7 +94,8 @@ void manager::configure(properties const & props) {
   auto & configs = props["configuration"]["loggers"];
   expects(configs.type == properties::OBJECT);
   for (auto & log : configs.obj) {
-    pimpl_->loggers.emplace(log.first, pimpl_->get_logger(log.second));
+    auto logimpl = pimpl_->get_logger(log.second["appenders"]);
+    pimpl_->loggers.emplace(log.first, logimpl);
   }
-  pimpl_->default_logger = pimpl_->get_logger(configs["root"]);
+  pimpl_->default_logger = pimpl_->get_logger(configs["root"]["appenders"]);
 }

+ 9 - 3
src/loggers/properties.cxx

@@ -9,6 +9,8 @@
 
 #include "../../../../paradigm/declarative/expect/include/expect/expect.hpp"
 
+#include "logger/exception.h"
+
 /*
  {
  {"pattern",{{},"%d{%h:%M:%s.%_ms} [%t] %-5.5p %.36c - %m%n"}}
@@ -80,15 +82,19 @@ namespace logging {
   };
   
   properties::operator std::string const &() const {
-    expects(type == STRING);
+    expects(type == STRING, invalid_property_type, "expected STRING");
     return value;
   }
   properties const & properties::operator[](std::size_t idx) const {
-    expects(type == ARRAY);
+    expects(type == ARRAY, invalid_property_type, "expected ARRAY");
+    expects(idx < array.size(), missing_property,
+            "Out of bounds: " + std::to_string(idx));
     return array.at(idx);
   }
   properties const & properties::operator[](std::string const & key) const {
-    expects(type == OBJECT);
+    expects(type == OBJECT, invalid_property_type, "expected OBJECT");
+    expects(obj.find(key) != obj.end(), missing_property,
+            "Missing key: " + key);
     return obj.at(key);
   }
 }

+ 75 - 0
test/log_manager_test.cxx

@@ -0,0 +1,75 @@
+//
+//  log_manager_test.cxx
+//  logger_test
+//
+//  Created by Sam Jaffe on 4/2/19.
+//
+
+#include "../../../types/resource_factory/include/resource_factory/prototype_factory.hpp"
+
+#include "logger/c_logger.h"
+#include "logger/log_manager.h"
+#include "logger/logger_impl.h"
+#include "logger/properties.h"
+
+#include "mock_logger.h"
+
+using namespace logging;
+
+class LogManagerTest : public ::testing::Test {
+public:
+  void SetUp() override;
+  void TearDown() override;
+protected:
+  std::shared_ptr<MockLoggerImpl> logger;
+private:
+  impl_factory::scoped_binding binding_;
+};
+
+void LogManagerTest::SetUp() {
+  auto GetMock = [this](properties const &) {
+    return logger = std::make_shared<MockLoggerImpl>();
+  };
+  binding_ = impl_factory::instance().bind_scoped("Mock", GetMock);
+}
+
+void LogManagerTest::TearDown() {
+  binding_.reset();
+}
+
+extern properties const MIN_PROPERTY_SCHEMA;
+extern properties const MULTIPLEX_PROPERTY_SCHEMA;
+
+TEST_F(LogManagerTest, CanInjectMock) {
+  manager().configure(MIN_PROPERTY_SCHEMA);
+
+  using ::testing::NotNull;
+
+  EXPECT_THAT(logger, ::testing::NotNull());
+}
+
+TEST_F(LogManagerTest, CanFetchInjectedMock) {
+  manager mgr;
+  mgr.configure(MIN_PROPERTY_SCHEMA);
+  
+  using ::testing::_;
+  using ::testing::AnyNumber;
+  EXPECT_CALL(*logger, flush()).Times(AnyNumber());
+  EXPECT_CALL(*logger, write(_, "TEST MESSAGE"));
+
+  c_logger l = mgr.c_get();
+  l.error("TEST MESSAGE");
+}
+
+TEST_F(LogManagerTest, MultiplexMockLogsToMultipleImpls) {
+  manager mgr;
+  mgr.configure(MULTIPLEX_PROPERTY_SCHEMA);
+  
+  using ::testing::_;
+  using ::testing::AnyNumber;
+  EXPECT_CALL(*logger, flush()).Times(AnyNumber());
+  EXPECT_CALL(*logger, write(_, "TEST MESSAGE")).Times(2);
+  
+  c_logger l = mgr.c_get();
+  l.error("TEST MESSAGE");
+}

+ 56 - 0
test/test_properties.cxx

@@ -0,0 +1,56 @@
+//
+//  mock_properties.cxx
+//  logger_test
+//
+//  Created by Sam Jaffe on 4/2/19.
+//
+
+#include "logger/properties.h"
+
+using logging::properties;
+
+namespace {
+  properties _property(std::map<std::string, properties> const& m) {
+    return properties{properties::OBJECT, m, {}, {}};
+  }
+  properties _list(std::vector<properties> const& l) {
+    return properties{properties::ARRAY, {}, l, {}};
+  }
+  properties _value(std::string const& s) {
+    return properties{properties::STRING, {}, {}, s};
+  }
+}
+
+extern properties const MIN_PROPERTY_SCHEMA;
+extern properties const MULTIPLEX_PROPERTY_SCHEMA;
+
+properties const MIN_PROPERTY_SCHEMA{_property({
+  {"configuration", _property({
+    {"appenders", _property({
+      {"Mock", _property({})}
+    })},
+    {"loggers", _property({
+      {"root", _property({
+        {"appenders", _property({
+          {"ref", _value("Mock")}
+        })}
+      })}
+    })}
+  })}
+})};
+
+properties const MULTIPLEX_PROPERTY_SCHEMA{_property({
+  {"configuration", _property({
+    {"appenders", _property({
+      {"Mock", _property({})}
+    })},
+    {"loggers", _property({
+      {"root", _property({
+        {"appenders", _list({
+          _property({{"ref", _value("Mock")}}),
+          _property({{"ref", _value("Mock")}})
+        })}
+      })}
+    })}
+  })}
+})};