Browse Source

Add tests for file and console appenders, fixing two errors identified in FileAppenderTest.
- Fix null property testing in operator[].
- Fix merging properties.

Sam Jaffe 6 năm trước cách đây
mục cha
commit
5c453800b0

+ 8 - 0
logger.xcodeproj/project.pbxproj

@@ -24,6 +24,8 @@
 		CD6F740C225187FD0081ED74 /* logger_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD6F73FC225187E10081ED74 /* logger_test.cxx */; };
 		CD6F746C22518A2C0081ED74 /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CD6F746B22518A2C0081ED74 /* GoogleMock.framework */; };
 		CD760CB922621776008A62DE /* pattern_layout_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD760CB822621776008A62DE /* pattern_layout_test.cxx */; };
+		CD760CBF226221F6008A62DE /* console_appender_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD760CBE226221F6008A62DE /* console_appender_test.cxx */; };
+		CD760CC1226226CC008A62DE /* file_appender_test.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD760CC0226226CC008A62DE /* file_appender_test.cxx */; };
 		CD88E9572252BDFC00927F40 /* log_manager.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD88E9552252BDFC00927F40 /* log_manager.cxx */; };
 		CD88E95F2252D3EF00927F40 /* c_logger.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD88E95D2252D3EF00927F40 /* c_logger.cxx */; };
 		CD88E9632252D67A00927F40 /* common.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CD88E9612252D67A00927F40 /* common.cxx */; };
@@ -92,6 +94,8 @@
 		CD6F742F225189470081ED74 /* GoogleMock.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = GoogleMock.xcodeproj; path = "../../../gmock-xcode-master/GoogleMock.xcodeproj"; sourceTree = "<group>"; };
 		CD6F746B22518A2C0081ED74 /* GoogleMock.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GoogleMock.framework; path = "../../../gmock-xcode-master/build/Release/GoogleMock.framework"; sourceTree = "<group>"; };
 		CD760CB822621776008A62DE /* pattern_layout_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = pattern_layout_test.cxx; sourceTree = "<group>"; };
+		CD760CBE226221F6008A62DE /* console_appender_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = console_appender_test.cxx; sourceTree = "<group>"; };
+		CD760CC0226226CC008A62DE /* file_appender_test.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = file_appender_test.cxx; sourceTree = "<group>"; };
 		CD88E9552252BDFC00927F40 /* log_manager.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = log_manager.cxx; sourceTree = "<group>"; };
 		CD88E95D2252D3EF00927F40 /* c_logger.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = c_logger.cxx; sourceTree = "<group>"; };
 		CD88E9612252D67A00927F40 /* common.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = common.cxx; sourceTree = "<group>"; };
@@ -175,6 +179,8 @@
 				CD6F73FC225187E10081ED74 /* logger_test.cxx */,
 				CD1CDEB22256B04600E5B6B2 /* format_test.cxx */,
 				CD760CB822621776008A62DE /* pattern_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 */,
@@ -368,9 +374,11 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD760CBF226221F6008A62DE /* console_appender_test.cxx in Sources */,
 				CD1CDE9022542CC500E5B6B2 /* log_manager_test.cxx in Sources */,
 				CD6F740C225187FD0081ED74 /* logger_test.cxx in Sources */,
 				CD760CB922621776008A62DE /* pattern_layout_test.cxx in Sources */,
+				CD760CC1226226CC008A62DE /* file_appender_test.cxx in Sources */,
 				CD1CDE9222543E7E00E5B6B2 /* test_properties.cxx in Sources */,
 				CD1CDEB32256B04600E5B6B2 /* format_test.cxx in Sources */,
 				CD1CDE8D22540D9B00E5B6B2 /* c_logger_test.cxx in Sources */,

+ 5 - 4
src/loggers/properties.cxx

@@ -65,7 +65,7 @@ namespace logging {
   
   properties properties::mergedWith(properties const & other) const {
     if (data.is<object_t>()) {
-      properties out;
+      properties out = property::_obj({});
       for (auto & pair : object()) {
         auto & to = out.data.get<object_t>()[pair.first];
         to = mergeKey(pair.first, pair.second, other);
@@ -96,14 +96,15 @@ namespace logging {
 
   properties const & properties::operator[](std::string const & key) const {
     expects(data.is<object_t>(), invalid_property_type, "expected OBJECT");
-    expects(contains(key), missing_property, "Missing key: " + key);
+    expects(contains(key) && object().at(key).data.valid(),
+            missing_property, "Missing key: " + key);
     return object().at(key);
   }
 
   properties const & properties::operator[](std::size_t idx) const {
     expects(data.is<array_t>(), invalid_property_type, "expected ARRAY");
-    expects(contains(idx), missing_property,
-            "Out of bounds: " + std::to_string(idx));
+    expects(contains(idx) && array().at(idx).data.valid(),
+            missing_property, "Out of bounds: " + std::to_string(idx));
     return array().at(idx);
   }
   

+ 87 - 0
test/console_appender_test.cxx

@@ -0,0 +1,87 @@
+//
+//  console_appender_test.cxx
+//  logger_test
+//
+//  Created by Sam Jaffe on 4/13/19.
+//
+
+#include <gmock/gmock.h>
+
+#include "resource_factory/prototype_factory.hpp"
+#include "../../../paradigm/declarative/scoped_buffer_capture/include/scoped_buffer_capture.h"
+
+#include "logger/log_manager.h"
+#include "logger/properties.h"
+#include "mock_logger.h"
+
+class ConsoleAppenderTest : public testing::Test {
+protected:
+  void SetUp() override;
+  void TearDown() override;
+  
+  std::string cout() const { return cout_->str(); }
+  std::string cerr() const { return cerr_->str(); }
+  std::shared_ptr<logging::appender> get(logging::properties const &) const;
+private:
+  std::shared_ptr<StubLayout> playout;
+  std::unique_ptr<scoped_buffer_capture_t> cout_, cerr_;
+};
+
+void ConsoleAppenderTest::SetUp() {
+  playout.reset(new StubLayout);
+  cout_.reset(new scoped_buffer_capture_t(std::cout));
+  cerr_.reset(new scoped_buffer_capture_t(std::cerr));
+}
+
+void ConsoleAppenderTest::TearDown() {
+  cerr_.reset();
+  cout_.reset();
+  playout.reset();
+}
+
+std::shared_ptr<logging::appender>
+ConsoleAppenderTest::get(logging::properties const & props) const {
+  auto pappender = logging::appenders::instance().get("Console", props);
+  pappender->layout = playout;
+  return pappender;
+}
+
+TEST_F(ConsoleAppenderTest, ErrorOnUnknownTarget) {
+  using namespace logging::property;
+  logging::properties props{_obj({
+    {"target", _v("COUT")}
+  })};
+  
+  EXPECT_THROW(logging::appenders::instance().get("Console", props),
+               std::logic_error);
+}
+
+TEST_F(ConsoleAppenderTest, LogsToStdOut) {
+  using namespace logging;
+  using namespace logging::property;
+  properties props{_obj({
+    {"target", _v("SYSTEM_OUT")}
+  })};
+  
+  using testing::Eq;
+  using testing::IsEmpty;
+  logpacket pkt{{}, level::error, {}, {}, "This is a test message"};
+  EXPECT_NO_THROW(get(props)->write(pkt));
+  EXPECT_THAT(cout(), Eq("This is a test message"));
+  EXPECT_THAT(cerr(), IsEmpty());
+}
+
+TEST_F(ConsoleAppenderTest, LogsToStdErr) {
+  using namespace logging;
+  using namespace logging::property;
+  properties props{_obj({
+    {"target", _v("SYSTEM_ERR")}
+  })};
+  
+  using testing::Eq;
+  using testing::IsEmpty;
+  logpacket pkt{{}, level::error, {}, {}, "This is a test message"};
+  EXPECT_NO_THROW(get(props)->write(pkt));
+  EXPECT_THAT(cout(), IsEmpty());
+  EXPECT_THAT(cerr(), Eq("This is a test message"));
+}

+ 109 - 0
test/file_appender_test.cxx

@@ -0,0 +1,109 @@
+//
+//  file_appender_test.cxx
+//  logger_test
+//
+//  Created by Sam Jaffe on 4/13/19.
+//
+
+#include <cstdlib>
+#include <fstream>
+#include <gmock/gmock.h>
+
+#include "resource_factory/prototype_factory.hpp"
+
+#include "logger/exception.h"
+#include "logger/log_manager.h"
+#include "logger/properties.h"
+#include "mock_logger.h"
+
+class FileAppenderTest : public testing::Test {
+protected:
+  void SetUp() override;
+  void TearDown() override;
+  
+  std::string filename() const { return data; }
+  std::shared_ptr<logging::appender> get(logging::properties const &) const;
+private:
+  std::shared_ptr<StubLayout> playout;
+  char data[24];
+};
+
+void FileAppenderTest::SetUp() {
+  strncpy(data, "test_log_fileXXXXXX.log", sizeof(data));
+  int fd = -1;
+  if ((fd = mkstemps(data, 4)) != -1) {
+    close(fd); // We'll open this elsewhere
+  } else {
+    throw std::runtime_error(strerror(errno));
+  }
+  playout.reset(new StubLayout);
+}
+
+void FileAppenderTest::TearDown() {
+  playout.reset();
+  remove(data);
+  memset(data, 0, sizeof(data));
+}
+
+std::shared_ptr<logging::appender>
+FileAppenderTest::get(logging::properties const & props) const {
+  auto pappender = logging::appenders::instance().get("File", props);
+  pappender->layout = playout;
+  return pappender;
+}
+
+TEST_F(FileAppenderTest, ThrowsIfNoFilename) {
+  EXPECT_THROW(logging::appenders::instance().get("File", {}),
+               logging::missing_property);
+}
+
+std::string slurp(std::string const & filename) {
+  std::ifstream in(filename);
+  std::stringstream ss;
+  ss << in.rdbuf();
+  return ss.str();
+}
+
+TEST_F(FileAppenderTest, WritesFile) {
+  using namespace logging;
+  using namespace logging::property;
+  properties props{_obj({
+    {"filename", _v(filename())}
+  })};
+  
+  using testing::Eq;
+  logpacket pkt{{}, level::error, {}, {}, "This is a test message"};
+  EXPECT_NO_THROW(get(props)->write(pkt));
+  EXPECT_THAT(slurp(filename()), Eq("This is a test message"));
+}
+
+TEST_F(FileAppenderTest, AppendsToFile) {
+  using namespace logging;
+  using namespace logging::property;
+  properties props{_obj({
+    {"filename", _v(filename())}
+  })};
+  
+  using testing::Eq;
+  logpacket pkt{{}, level::error, {}, {}, "Test"};
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_NO_THROW(get(props)->write(pkt));
+  }
+  EXPECT_THAT(slurp(filename()), Eq("TestTest"));
+}
+
+TEST_F(FileAppenderTest, OverwritesFileWithSetting) {
+  using namespace logging;
+  using namespace logging::property;
+  properties props{_obj({
+    {"filename", _v(filename())},
+    {"fileAppend", _v(false)}
+  })};
+  
+  using testing::Eq;
+  logpacket pkt{{}, level::error, {}, {}, "Test"};
+  for (int i = 0; i < 2; ++i) {
+    EXPECT_NO_THROW(get(props)->write(pkt));
+  }
+  EXPECT_THAT(slurp(filename()), Eq("Test"));
+}

+ 6 - 0
test/mock_logger.h

@@ -55,6 +55,12 @@ struct MockLayout : public logging::layout {
   MOCK_CONST_METHOD2(format, void(std::ostream&, logging::logpacket const&));
 };
 
+struct StubLayout : public logging::layout {
+  void format(std::ostream& os, logging::logpacket const& pkt) const {
+    os << pkt.message.str();
+  }
+};
+
 ACTION(LogMessage) {
   arg0 << arg1.message.str();
 }