| #include "aos/testing/test_logging.h" |
| |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <cstdio> |
| #include <memory> |
| #include <mutex> |
| #include <string_view> |
| #include <vector> |
| |
| #include "absl/base/call_once.h" |
| #include "gtest/gtest.h" |
| |
| #include "aos/logging/implementations.h" |
| #include "aos/stl_mutex/stl_mutex.h" |
| #include "aos/time/time.h" |
| |
| using ::aos::logging::LogMessage; |
| |
| namespace aos::testing { |
| namespace { |
| |
| class TestLogImplementation : public logging::HandleMessageLogImplementation { |
| public: |
| const ::std::vector<LogMessage> &messages() { return messages_; } |
| |
| ::aos::monotonic_clock::time_point monotonic_now() const override { |
| return ::aos::monotonic_clock::now(); |
| } |
| |
| std::string_view MyName() override { return "Name"; } |
| |
| // This class has to be a singleton so that everybody can get access to the |
| // same instance to read out the messages etc. |
| static std::shared_ptr<TestLogImplementation> GetInstance() { |
| static std::shared_ptr<TestLogImplementation> instance = |
| std::make_unique<TestLogImplementation>(); |
| return instance; |
| } |
| |
| // Clears out all of the messages already recorded. |
| void ClearMessages() { |
| std::unique_lock<aos::stl_mutex> locker(messages_mutex_); |
| messages_.clear(); |
| } |
| |
| // Prints out all of the messages (like when a test fails). |
| void PrintAllMessages() { |
| std::unique_lock<aos::stl_mutex> locker(messages_mutex_); |
| for (auto it = messages_.begin(); it != messages_.end(); ++it) { |
| logging::internal::PrintMessage(stdout, *it); |
| } |
| } |
| |
| void SetOutputFile(const char *filename) { |
| if (strcmp("-", filename) != 0) { |
| FILE *newfile = fopen(filename, "w"); |
| |
| if (newfile) { |
| output_file_ = newfile; |
| } |
| } |
| } |
| |
| void PrintMessagesAsTheyComeIn() { print_as_messages_come_in_ = true; } |
| |
| // Don't call these from outside this class. |
| ~TestLogImplementation() { |
| if (output_file_ != stdout) { |
| fclose(output_file_); |
| } |
| } |
| |
| private: |
| virtual void HandleMessage(const LogMessage &message) override { |
| std::unique_lock<aos::stl_mutex> locker(messages_mutex_); |
| if (message.level == FATAL || print_as_messages_come_in_) { |
| logging::internal::PrintMessage(output_file_, message); |
| } |
| |
| messages_.push_back(message); |
| } |
| |
| ::std::vector<LogMessage> messages_; |
| bool print_as_messages_come_in_ = false; |
| FILE *output_file_ = stdout; |
| aos::stl_mutex messages_mutex_; |
| }; |
| |
| class MyTestEventListener : public ::testing::EmptyTestEventListener { |
| virtual void OnTestStart(const ::testing::TestInfo & /*test_info*/) { |
| TestLogImplementation::GetInstance()->ClearMessages(); |
| } |
| virtual void OnTestEnd(const ::testing::TestInfo &test_info) { |
| if (test_info.result()->Failed()) { |
| printf("Test %s failed. Use '--print-logs' to see all log messages.\n", |
| test_info.name()); |
| } |
| } |
| |
| virtual void OnTestPartResult(const ::testing::TestPartResult &result) { |
| if (result.failed()) { |
| const char *failure_type = "unknown"; |
| switch (result.type()) { |
| case ::testing::TestPartResult::Type::kNonFatalFailure: |
| failure_type = "EXPECT"; |
| break; |
| case ::testing::TestPartResult::Type::kFatalFailure: |
| failure_type = "ASSERT"; |
| break; |
| case ::testing::TestPartResult::Type::kSuccess: |
| case ::testing::TestPartResult::Type::kSkip: |
| break; |
| } |
| log_do(ERROR, "%s: %d: gtest %s failure\n%s\n", result.file_name(), |
| result.line_number(), failure_type, result.message()); |
| } |
| } |
| }; |
| |
| void *DoEnableTestLogging() { |
| logging::SetImplementation(TestLogImplementation::GetInstance()); |
| |
| ::testing::UnitTest::GetInstance()->listeners().Append( |
| new MyTestEventListener()); |
| |
| return nullptr; |
| } |
| |
| static absl::once_flag enable_test_logging_once; |
| |
| } // namespace |
| |
| void EnableTestLogging() { |
| absl::call_once(enable_test_logging_once, DoEnableTestLogging); |
| } |
| |
| void SetLogFileName(const char *filename) { |
| TestLogImplementation::GetInstance()->SetOutputFile(filename); |
| } |
| |
| void ForcePrintLogsDuringTests() { |
| TestLogImplementation::GetInstance()->PrintMessagesAsTheyComeIn(); |
| } |
| |
| } // namespace aos::testing |