diff options
Diffstat (limited to 'third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc new file mode 100644 index 0000000000..1e8799bdd6 --- /dev/null +++ b/third_party/libwebrtc/logging/rtc_event_log/rtc_event_log_impl_unittest.cc @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2023 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "logging/rtc_event_log/rtc_event_log_impl.h" + +#include <utility> +#include <vector> + +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/time_controller/simulated_time_controller.h" + +namespace webrtc { +namespace { + +using ::testing::_; +using ::testing::InSequence; +using ::testing::Invoke; +using ::testing::Mock; +using ::testing::Property; +using ::testing::Ref; +using ::testing::Return; +using ::testing::StrEq; + +class MockEventEncoder : public RtcEventLogEncoder { + public: + MOCK_METHOD(std::string, + EncodeLogStart, + (int64_t /*timestamp_us*/, int64_t /*utc_time_us*/), + (override)); + MOCK_METHOD(std::string, + EncodeLogEnd, + (int64_t /*timestamp_us*/), + (override)); + MOCK_METHOD(std::string, OnEncode, (const RtcEvent&), ()); + std::string EncodeBatch( + std::deque<std::unique_ptr<RtcEvent>>::const_iterator a, + std::deque<std::unique_ptr<RtcEvent>>::const_iterator b) override { + std::string result; + while (a != b) { + result += OnEncode(**a); + ++a; + } + return result; + } +}; + +class FakeOutput : public RtcEventLogOutput { + public: + explicit FakeOutput(std::string& written_data) + : written_data_(written_data) {} + bool IsActive() const { return is_active_; } + bool Write(absl::string_view data) override { + RTC_DCHECK(is_active_); + if (fails_write_) { + is_active_ = false; + fails_write_ = false; + return false; + } else { + written_data_.append(std::string(data)); + return true; + } + } + void Flush() override {} + + void FailsNextWrite() { fails_write_ = true; } + + private: + std::string& written_data_; + bool is_active_ = true; + bool fails_write_ = false; +}; + +class FakeEvent : public RtcEvent { + public: + Type GetType() const override { return RtcEvent::Type::FakeEvent; } + bool IsConfigEvent() const override { return false; } +}; + +class FakeConfigEvent : public RtcEvent { + public: + Type GetType() const override { return RtcEvent::Type::FakeEvent; } + bool IsConfigEvent() const override { return true; } +}; + +class RtcEventLogImplTest : public ::testing::Test { + public: + static constexpr size_t kMaxEventsInHistory = 2; + static constexpr size_t kMaxEventsInConfigHistory = 5; + static constexpr TimeDelta kOutputPeriod = TimeDelta::Seconds(2); + static constexpr Timestamp kStartTime = Timestamp::Seconds(1); + + GlobalSimulatedTimeController time_controller_{kStartTime}; + std::string written_data_; // This must be destroyed after the event_log_. + std::unique_ptr<MockEventEncoder> encoder_ = + std::make_unique<MockEventEncoder>(); + MockEventEncoder* encoder_ptr_ = encoder_.get(); + std::unique_ptr<FakeOutput> output_ = + std::make_unique<FakeOutput>(written_data_); + FakeOutput* output_ptr_ = output_.get(); + RtcEventLogImpl event_log_{std::move(encoder_), + time_controller_.GetTaskQueueFactory(), + kMaxEventsInHistory, kMaxEventsInConfigHistory}; +}; + +TEST_F(RtcEventLogImplTest, WritesHeaderAndEventsAndTrailer) { + EXPECT_CALL(*encoder_ptr_, EncodeLogStart(kStartTime.us(), kStartTime.us())) + .WillOnce(Return("start")); + EXPECT_CALL(*encoder_ptr_, EncodeLogEnd(kStartTime.us())) + .WillOnce(Return("stop")); + EXPECT_CALL(*encoder_ptr_, OnEncode(Property(&RtcEvent::IsConfigEvent, true))) + .WillOnce(Return("config")); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))) + .WillOnce(Return("event")); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + event_log_.Log(std::make_unique<FakeEvent>()); + event_log_.Log(std::make_unique<FakeConfigEvent>()); + event_log_.StopLogging(); + Mock::VerifyAndClearExpectations(encoder_ptr_); + EXPECT_TRUE(written_data_ == "startconfigeventstop" || + written_data_ == "starteventconfigstop"); +} + +TEST_F(RtcEventLogImplTest, KeepsMostRecentEventsOnStart) { + auto e2 = std::make_unique<FakeEvent>(); + RtcEvent* e2_ptr = e2.get(); + auto e3 = std::make_unique<FakeEvent>(); + RtcEvent* e3_ptr = e3.get(); + event_log_.Log(std::make_unique<FakeEvent>()); + event_log_.Log(std::move(e2)); + event_log_.Log(std::move(e3)); + InSequence s; + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e2_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e3_ptr))); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, KeepsConfigEventsOnStart) { + // The config-history is supposed to be unbounded and never overflow. + auto c1 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c1_ptr = c1.get(); + auto c2 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c2_ptr = c2.get(); + auto c3 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c3_ptr = c3.get(); + event_log_.Log(std::move(c1)); + event_log_.Log(std::move(c2)); + event_log_.Log(std::move(c3)); + InSequence s; + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c1_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c2_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c3_ptr))); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, EncodesEventsOnHistoryFullPostponesLastEncode) { + auto e1 = std::make_unique<FakeEvent>(); + RtcEvent* e1_ptr = e1.get(); + auto e2 = std::make_unique<FakeEvent>(); + RtcEvent* e2_ptr = e2.get(); + auto e3 = std::make_unique<FakeEvent>(); + RtcEvent* e3_ptr = e3.get(); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + event_log_.Log(std::move(e1)); + event_log_.Log(std::move(e2)); + // Overflows and causes immediate output, and eventual output after the output + // period has passed. + event_log_.Log(std::move(e3)); + InSequence s; + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e1_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e2_ptr))); + time_controller_.AdvanceTime(TimeDelta::Zero()); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*e3_ptr))); + time_controller_.AdvanceTime(kOutputPeriod); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, RewritesAllConfigEventsOnlyOnRestart) { + auto c1 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c1_ptr = c1.get(); + auto c2 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c2_ptr = c2.get(); + auto c3 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c3_ptr = c3.get(); + auto c4 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c4_ptr = c4.get(); + auto c5 = std::make_unique<FakeConfigEvent>(); + RtcEvent* c5_ptr = c5.get(); + // Keep the config event before start. + event_log_.Log(std::move(c1)); + // Start logging. + event_log_.StartLogging(std::make_unique<FakeOutput>(written_data_), + kOutputPeriod.ms()); + event_log_.Log(std::move(c2)); + event_log_.Log(std::move(c3)); + event_log_.StopLogging(); + time_controller_.AdvanceTime(TimeDelta::Zero()); + // Restart logging. + event_log_.StartLogging(std::make_unique<FakeOutput>(written_data_), + kOutputPeriod.ms()); + event_log_.Log(std::move(c4)); + event_log_.Log(std::move(c5)); + event_log_.Log(std::make_unique<FakeEvent>()); + event_log_.StopLogging(); + time_controller_.AdvanceTime(TimeDelta::Zero()); + InSequence s; + // Rewrite all config events in sequence on next restart. + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c1_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c2_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c3_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c4_ptr))); + EXPECT_CALL(*encoder_ptr_, OnEncode(Ref(*c5_ptr))); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))) + .Times(0); + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + time_controller_.AdvanceTime(TimeDelta::Zero()); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, SchedulesWriteAfterOutputDurationPassed) { + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + event_log_.Log(std::make_unique<FakeConfigEvent>()); + event_log_.Log(std::make_unique<FakeEvent>()); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, true))); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))); + time_controller_.AdvanceTime(kOutputPeriod); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, DoNotDropEventsIfHistoryFullAfterStarted) { + constexpr size_t kNumberOfEvents = 10 * kMaxEventsInHistory; + + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + event_log_.Log(std::make_unique<FakeConfigEvent>()); + for (size_t i = 0; i < kNumberOfEvents; i++) { + event_log_.Log(std::make_unique<FakeEvent>()); + } + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, true))); + EXPECT_CALL(*encoder_ptr_, + OnEncode(Property(&RtcEvent::IsConfigEvent, false))) + .Times(kNumberOfEvents); + time_controller_.AdvanceTime(kOutputPeriod); + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +TEST_F(RtcEventLogImplTest, StopOutputOnWriteFailure) { + constexpr size_t kNumberOfEvents = 10; + constexpr size_t kFailsWriteOnEventsCount = 5; + + size_t number_of_encoded_events = 0; + EXPECT_CALL(*encoder_ptr_, OnEncode(_)) + .WillRepeatedly(Invoke([this, &number_of_encoded_events]() { + ++number_of_encoded_events; + if (number_of_encoded_events == kFailsWriteOnEventsCount) { + output_ptr_->FailsNextWrite(); + } + return std::string(); + })); + + event_log_.StartLogging(std::move(output_), kOutputPeriod.ms()); + + // Fails `RtcEventLogOutput` on the last event. + for (size_t i = 0; i < kFailsWriteOnEventsCount; i++) { + event_log_.Log(std::make_unique<FakeEvent>()); + } + time_controller_.AdvanceTime(kOutputPeriod); + // Expect that the remainder events are not encoded. + for (size_t i = kFailsWriteOnEventsCount; i < kNumberOfEvents; i++) { + event_log_.Log(std::make_unique<FakeEvent>()); + } + time_controller_.AdvanceTime(kOutputPeriod); + event_log_.StopLogging(); + + EXPECT_EQ(number_of_encoded_events, kFailsWriteOnEventsCount); + + Mock::VerifyAndClearExpectations(encoder_ptr_); +} + +} // namespace +} // namespace webrtc |