scoped_mock_log.h 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. // Copyright 2022 The Abseil Authors.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // https://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. //
  15. // -----------------------------------------------------------------------------
  16. // File: log/scoped_mock_log.h
  17. // -----------------------------------------------------------------------------
  18. //
  19. // This header declares `class y_absl::ScopedMockLog`, for use in testing.
  20. #ifndef Y_ABSL_LOG_SCOPED_MOCK_LOG_H_
  21. #define Y_ABSL_LOG_SCOPED_MOCK_LOG_H_
  22. #include <atomic>
  23. #include <util/generic/string.h>
  24. #include "gmock/gmock.h"
  25. #include "y_absl/base/config.h"
  26. #include "y_absl/base/log_severity.h"
  27. #include "y_absl/log/log_entry.h"
  28. #include "y_absl/log/log_sink.h"
  29. namespace y_absl {
  30. Y_ABSL_NAMESPACE_BEGIN
  31. // MockLogDefault
  32. //
  33. // Controls how ScopedMockLog responds to unexpected calls by default.
  34. enum class MockLogDefault { kIgnoreUnexpected, kDisallowUnexpected };
  35. // ScopedMockLog
  36. //
  37. // ScopedMockLog is a LogSink that intercepts LOG() messages issued during its
  38. // lifespan.
  39. //
  40. // Using this together with GoogleTest, it's easy to test how a piece of code
  41. // calls LOG(). The typical usage, noting the distinction between
  42. // "uninteresting" and "unexpected", looks like this:
  43. //
  44. // using ::testing::_;
  45. // using ::testing::AnyNumber;
  46. // using ::testing::EndsWith;
  47. // using ::testing::kDoNotCaptureLogsYet;
  48. // using ::testing::Lt;
  49. //
  50. // TEST(FooTest, LogsCorrectly) {
  51. // // Simple robust setup, ignores unexpected logs.
  52. // y_absl::ScopedMockLog log;
  53. //
  54. // // We expect the WARNING "Something bad!" exactly twice.
  55. // EXPECT_CALL(log, Log(y_absl::LogSeverity::kWarning, _, "Something bad!"))
  56. // .Times(2);
  57. //
  58. // // But we want no messages from foo.cc.
  59. // EXPECT_CALL(log, Log(_, EndsWith("/foo.cc"), _)).Times(0);
  60. //
  61. // log.StartCapturingLogs(); // Call this after done setting expectations.
  62. // Foo(); // Exercises the code under test.
  63. // }
  64. //
  65. // TEST(BarTest, LogsExactlyCorrectly) {
  66. // // Strict checking, fails for unexpected logs.
  67. // y_absl::ScopedMockLog log(y_absl::MockLogDefault::kDisallowUnexpected);
  68. //
  69. // // ... but ignore low severity messages
  70. // EXPECT_CALL(log, Log(Lt(y_absl::LogSeverity::kWarning), _, _))
  71. // .Times(AnyNumber());
  72. //
  73. // // We expect the ERROR "Something bad!" exactly once.
  74. // EXPECT_CALL(log, Log(y_absl::LogSeverity::kError, EndsWith("/foo.cc"),
  75. // "Something bad!"))
  76. // .Times(1);
  77. //
  78. // log.StartCapturingLogs(); // Call this after done setting expectations.
  79. // Bar(); // Exercises the code under test.
  80. // }
  81. //
  82. // Note that in a multi-threaded environment, all LOG() messages from a single
  83. // thread will be handled in sequence, but that cannot be guaranteed for
  84. // messages from different threads. In fact, if the same or multiple
  85. // expectations are matched on two threads concurrently, their actions will be
  86. // executed concurrently as well and may interleave.
  87. class ScopedMockLog final {
  88. public:
  89. // ScopedMockLog::ScopedMockLog()
  90. //
  91. // Sets up the log and adds default expectations.
  92. explicit ScopedMockLog(
  93. MockLogDefault default_exp = MockLogDefault::kIgnoreUnexpected);
  94. ScopedMockLog(const ScopedMockLog&) = delete;
  95. ScopedMockLog& operator=(const ScopedMockLog&) = delete;
  96. // ScopedMockLog::~ScopedMockLog()
  97. //
  98. // Stops intercepting logs and destroys this ScopedMockLog.
  99. ~ScopedMockLog();
  100. // ScopedMockLog::StartCapturingLogs()
  101. //
  102. // Starts log capturing if the object isn't already doing so. Otherwise
  103. // crashes.
  104. //
  105. // Usually this method is called in the same thread that created this
  106. // ScopedMockLog. It is the user's responsibility to not call this method if
  107. // another thread may be calling it or StopCapturingLogs() at the same time.
  108. // It is undefined behavior to add expectations while capturing logs is
  109. // enabled.
  110. void StartCapturingLogs();
  111. // ScopedMockLog::StopCapturingLogs()
  112. //
  113. // Stops log capturing if the object is capturing logs. Otherwise crashes.
  114. //
  115. // Usually this method is called in the same thread that created this object.
  116. // It is the user's responsibility to not call this method if another thread
  117. // may be calling it or StartCapturingLogs() at the same time.
  118. //
  119. // It is UB to add expectations, while capturing logs is enabled.
  120. void StopCapturingLogs();
  121. // ScopedMockLog::UseAsLocalSink()
  122. //
  123. // Each `ScopedMockLog` is implemented with an `y_absl::LogSink`; this method
  124. // returns a reference to that sink (e.g. for use with
  125. // `LOG(...).ToSinkOnly()`) and marks the `ScopedMockLog` as having been used
  126. // even if `StartCapturingLogs` is never called.
  127. y_absl::LogSink& UseAsLocalSink();
  128. // Implements the mock method:
  129. //
  130. // void Log(LogSeverity severity, y_absl::string_view file_path,
  131. // y_absl::string_view message);
  132. //
  133. // The second argument to Log() is the full path of the source file in
  134. // which the LOG() was issued.
  135. //
  136. // This is a shorthand form, which should be used by most users. Use the
  137. // `Send` mock only if you want to add expectations for other log message
  138. // attributes.
  139. MOCK_METHOD(void, Log,
  140. (y_absl::LogSeverity severity, const TString& file_path,
  141. const TString& message));
  142. // Implements the mock method:
  143. //
  144. // void Send(const y_absl::LogEntry& entry);
  145. //
  146. // This is the most generic form of mock that can be specified. Use this mock
  147. // only if you want to add expectations for log message attributes different
  148. // from the log message text, log message path and log message severity.
  149. //
  150. // If no expectations are specified for this mock, the default action is to
  151. // forward the call to the `Log` mock.
  152. MOCK_METHOD(void, Send, (const y_absl::LogEntry&));
  153. // Implements the mock method:
  154. //
  155. // void Flush();
  156. //
  157. // Use this mock only if you want to add expectations for log flush calls.
  158. MOCK_METHOD(void, Flush, ());
  159. private:
  160. class ForwardingSink final : public y_absl::LogSink {
  161. public:
  162. explicit ForwardingSink(ScopedMockLog* sml) : sml_(sml) {}
  163. ForwardingSink(const ForwardingSink&) = delete;
  164. ForwardingSink& operator=(const ForwardingSink&) = delete;
  165. void Send(const y_absl::LogEntry& entry) override { sml_->Send(entry); }
  166. void Flush() override { sml_->Flush(); }
  167. private:
  168. ScopedMockLog* sml_;
  169. };
  170. ForwardingSink sink_;
  171. bool is_capturing_logs_;
  172. // Until C++20, the default constructor leaves the underlying value wrapped in
  173. // std::atomic uninitialized, so all constructors should be sure to initialize
  174. // is_triggered_.
  175. std::atomic<bool> is_triggered_;
  176. };
  177. Y_ABSL_NAMESPACE_END
  178. } // namespace y_absl
  179. #endif // Y_ABSL_LOG_SCOPED_MOCK_LOG_H_