| // Copyright 2018 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "shill/net/event_history.h" |
| |
| #include <memory> |
| #include <string> |
| #include <vector> |
| |
| #include <base/stl_util.h> |
| #include <gtest/gtest.h> |
| |
| #include "shill/net/mock_time.h" |
| #include "shill/net/shill_time.h" |
| |
| using std::deque; |
| using std::string; |
| using std::vector; |
| using ::testing::Return; |
| |
| namespace shill { |
| |
| namespace { |
| // consts here |
| } // namespace |
| |
| class EventHistoryTest : public ::testing::Test { |
| public: |
| EventHistoryTest() : event_history_(new EventHistory()) { |
| event_history_->time_ = &time_; |
| } |
| |
| ~EventHistoryTest() override = default; |
| |
| void SetMaxEventsSaved(int num_events) { |
| event_history_->max_events_saved_ = num_events; |
| event_history_->max_events_specified_ = true; |
| } |
| |
| void SetNoMaxEvents() { |
| event_history_->max_events_saved_ = 0; |
| event_history_->max_events_specified_ = false; |
| } |
| |
| int GetMaxEventsSaved() { return event_history_->max_events_saved_; } |
| |
| bool GetMaxEventsSpecified() { return event_history_->max_events_specified_; } |
| |
| deque<Timestamp>* GetEvents() { return &event_history_->events_; } |
| |
| void RecordEvent(Timestamp now) { |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(now)); |
| event_history_->RecordEvent(); |
| } |
| |
| void ExpireEventsBefore(int seconds_ago, |
| Timestamp now, |
| EventHistory::ClockType clock_type) { |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(now)); |
| event_history_->ExpireEventsBefore(seconds_ago, clock_type); |
| } |
| |
| void RecordEventAndExpireEventsBefore(int seconds_ago, |
| Timestamp now, |
| EventHistory::ClockType clock_type) { |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(now)); |
| event_history_->RecordEventAndExpireEventsBefore(seconds_ago, clock_type); |
| } |
| |
| vector<string> ExtractWallClockToStrings() { |
| return event_history_->ExtractWallClockToStrings(); |
| } |
| |
| Timestamp GetTimestamp(int monotonic_seconds, |
| int boottime_seconds, |
| const string& wall_clock) { |
| struct timeval monotonic = {.tv_sec = monotonic_seconds, .tv_usec = 0}; |
| struct timeval boottime = {.tv_sec = boottime_seconds, .tv_usec = 0}; |
| return Timestamp(monotonic, boottime, wall_clock); |
| } |
| |
| int CountEventsWithinInterval(int seconds_ago, |
| EventHistory::ClockType clock_type, |
| Timestamp now) { |
| EXPECT_CALL(time_, GetNow()).WillOnce(Return(now)); |
| return event_history_->CountEventsWithinInterval(seconds_ago, clock_type); |
| } |
| |
| protected: |
| MockTime time_; |
| std::unique_ptr<EventHistory> event_history_; |
| }; |
| |
| TEST_F(EventHistoryTest, RecordEvent) { |
| const int kTime1 = 5; |
| const int kTime2 = 8; |
| EXPECT_TRUE(GetEvents()->empty()); |
| RecordEvent(GetTimestamp(kTime1, kTime1, "")); |
| EXPECT_EQ(1, GetEvents()->size()); |
| EXPECT_EQ(kTime1, GetEvents()->back().monotonic.tv_sec); |
| EXPECT_EQ(kTime1, GetEvents()->back().boottime.tv_sec); |
| |
| // Latest events pushed to the back of the list. |
| RecordEvent(GetTimestamp(kTime2, kTime2, "")); |
| EXPECT_EQ(2, GetEvents()->size()); |
| EXPECT_EQ(kTime2, GetEvents()->back().monotonic.tv_sec); |
| EXPECT_EQ(kTime2, GetEvents()->back().boottime.tv_sec); |
| } |
| |
| TEST_F(EventHistoryTest, EventThresholdReached) { |
| const int kMaxEventsThreshold = 10; |
| const int kTime1 = 5; |
| const int kTime2 = 8; |
| SetMaxEventsSaved(kMaxEventsThreshold); |
| EXPECT_TRUE(GetEvents()->empty()); |
| for (int i = 0; i < kMaxEventsThreshold; ++i) { |
| RecordEvent(GetTimestamp(kTime1, kTime1, "")); |
| } |
| // All kMaxEventsThreshold events successfully saved. |
| EXPECT_EQ(kMaxEventsThreshold, GetEvents()->size()); |
| EXPECT_EQ(kTime1, GetEvents()->back().monotonic.tv_sec); |
| EXPECT_EQ(kTime1, GetEvents()->back().boottime.tv_sec); |
| |
| // One timestamp will be evicted to make way for the latest event timestamp, |
| // which will be pushed to the back of the list. |
| RecordEvent(GetTimestamp(kTime2, kTime2, "")); |
| EXPECT_EQ(kMaxEventsThreshold, GetEvents()->size()); |
| EXPECT_EQ(kTime2, GetEvents()->back().monotonic.tv_sec); |
| EXPECT_EQ(kTime2, GetEvents()->back().boottime.tv_sec); |
| } |
| |
| TEST_F(EventHistoryTest, ExpireEventsBefore_EvictExpiredEvents) { |
| const int kExpiryThresholdSeconds = 10; |
| const int kTimeEarly = 5; |
| const int kTimeLate = kTimeEarly + kExpiryThresholdSeconds + 1; |
| const int kNumEarlierEvents = 20; |
| |
| EXPECT_TRUE(GetEvents()->empty()); |
| for (int i = 0; i < kNumEarlierEvents; ++i) { |
| RecordEvent(GetTimestamp(kTimeEarly, kTimeEarly, "")); |
| } |
| EXPECT_EQ(kNumEarlierEvents, GetEvents()->size()); |
| EXPECT_EQ(kTimeEarly, GetEvents()->front().monotonic.tv_sec); |
| EXPECT_EQ(kTimeEarly, GetEvents()->front().boottime.tv_sec); |
| |
| RecordEvent(GetTimestamp(kTimeLate, kTimeLate, "")); |
| EXPECT_EQ(kNumEarlierEvents + 1, GetEvents()->size()); |
| EXPECT_EQ(kTimeEarly, GetEvents()->front().monotonic.tv_sec); |
| EXPECT_EQ(kTimeEarly, GetEvents()->front().boottime.tv_sec); |
| EXPECT_EQ(kTimeLate, GetEvents()->back().monotonic.tv_sec); |
| EXPECT_EQ(kTimeLate, GetEvents()->back().boottime.tv_sec); |
| |
| // Expect that all the kTimeEarly event timestamps will be evicted since |
| // they took place more than kExpiryThresholdSeconds ago. |
| ExpireEventsBefore(kExpiryThresholdSeconds, |
| GetTimestamp(kTimeLate, kTimeLate, ""), |
| EventHistory::kClockTypeBoottime); |
| EXPECT_EQ(1, GetEvents()->size()); |
| EXPECT_EQ(kTimeLate, GetEvents()->front().monotonic.tv_sec); |
| EXPECT_EQ(kTimeLate, GetEvents()->front().boottime.tv_sec); |
| } |
| |
| TEST_F(EventHistoryTest, ExpireEventsBefore_UseSuspendTime) { |
| const int kExpiryThresholdSeconds = 10; |
| const int kTime1 = 5; |
| |
| EventHistory::ClockType clock_type; |
| |
| EXPECT_TRUE(GetEvents()->empty()); |
| RecordEvent(GetTimestamp(kTime1, kTime1, "")); |
| EXPECT_EQ(1, GetEvents()->size()); |
| EXPECT_EQ(kTime1, GetEvents()->front().monotonic.tv_sec); |
| EXPECT_EQ(kTime1, GetEvents()->front().boottime.tv_sec); |
| |
| const int kTime2Monotonic = kTime1 + kExpiryThresholdSeconds - 1; |
| const int kTime2Boot = kTime1 + kExpiryThresholdSeconds + 1; |
| // If we don't count suspend time (i.e. use the monotonic clock), we will not |
| // expire the event because it took place less than kExpiryThresholdSeconds |
| // ago. |
| clock_type = EventHistory::kClockTypeMonotonic; |
| ExpireEventsBefore(kExpiryThresholdSeconds, |
| GetTimestamp(kTime2Monotonic, kTime2Boot, ""), clock_type); |
| EXPECT_EQ(1, GetEvents()->size()); |
| |
| // If we count suspend time (i.e. use the boottime clock), we will expire the |
| // event because it took place more than kExpiryThresholdSeconds ago. |
| clock_type = EventHistory::kClockTypeBoottime; |
| ExpireEventsBefore(kExpiryThresholdSeconds, |
| GetTimestamp(kTime2Monotonic, kTime2Boot, ""), clock_type); |
| EXPECT_TRUE(GetEvents()->empty()); |
| } |
| |
| TEST_F(EventHistoryTest, RecordEventAndExpireEventsBefore) { |
| const int kExpiryThresholdSeconds = 10; |
| const int kTimeEarly = 5; |
| const int kTimeLate = kTimeEarly + kExpiryThresholdSeconds + 1; |
| const int kNumEarlierEvents = 20; |
| const int kMaxEventsThreshold = kNumEarlierEvents / 2; |
| |
| SetMaxEventsSaved(kMaxEventsThreshold); |
| EXPECT_TRUE(GetEvents()->empty()); |
| for (int i = 0; i < kNumEarlierEvents; ++i) { |
| RecordEventAndExpireEventsBefore(kExpiryThresholdSeconds, |
| GetTimestamp(kTimeEarly, kTimeEarly, ""), |
| EventHistory::kClockTypeBoottime); |
| } |
| // kNumEarlierEvents is greater than kMaxEventsThreshold, so only |
| // kMaxEventsThreshold events should be saved. |
| EXPECT_EQ(kMaxEventsThreshold, GetEvents()->size()); |
| EXPECT_EQ(kTimeEarly, GetEvents()->front().monotonic.tv_sec); |
| EXPECT_EQ(kTimeEarly, GetEvents()->front().boottime.tv_sec); |
| |
| // Expect that the kTimeLate timestamp should be added and all the kTimeEarly |
| // event timestamps will be evicted since the the former took place less than |
| // kExpiryThresholdSeconds ago and the latter took place more than |
| // kExpiryThresholdSeconds ago. |
| RecordEventAndExpireEventsBefore(kExpiryThresholdSeconds, |
| GetTimestamp(kTimeLate, kTimeLate, ""), |
| EventHistory::kClockTypeBoottime); |
| EXPECT_EQ(1, GetEvents()->size()); |
| EXPECT_EQ(kTimeLate, GetEvents()->front().monotonic.tv_sec); |
| EXPECT_EQ(kTimeLate, GetEvents()->front().boottime.tv_sec); |
| } |
| |
| TEST_F(EventHistoryTest, ConvertTimestampsToStrings) { |
| EXPECT_TRUE(ExtractWallClockToStrings().empty()); |
| |
| const Timestamp kValues[] = { |
| GetTimestamp(123, 123, "2012-12-09T12:41:22.123456+0100"), |
| GetTimestamp(234, 234, "2012-12-31T23:59:59.012345+0100")}; |
| for (size_t i = 0; i < base::size(kValues); ++i) { |
| RecordEvent(kValues[i]); |
| } |
| |
| vector<string> strings = ExtractWallClockToStrings(); |
| EXPECT_GT(base::size(kValues), 0); |
| ASSERT_EQ(base::size(kValues), strings.size()); |
| for (size_t i = 0; i < base::size(kValues); i++) { |
| EXPECT_EQ(kValues[i].wall_clock, strings[i]); |
| } |
| } |
| |
| TEST_F(EventHistoryTest, CountEventsWithinInterval) { |
| const int kExpiryThresholdSeconds = 10; |
| const int kTimeEarly = 5; |
| const int kTimeLate = kTimeEarly + kExpiryThresholdSeconds + 1; |
| const int kNumEarlierEvents = 20; |
| const int kNumLaterEvents = 10; |
| const int kMaxEventsThreshold = kNumEarlierEvents + kNumLaterEvents; |
| |
| SetMaxEventsSaved(kMaxEventsThreshold); |
| EXPECT_TRUE(GetEvents()->empty()); |
| for (int i = 0; i < kNumEarlierEvents; ++i) { |
| RecordEvent(GetTimestamp(kTimeEarly, kTimeEarly, "")); |
| } |
| for (int i = 0; i < kNumLaterEvents; ++i) { |
| RecordEvent(GetTimestamp(kTimeLate, kTimeLate, "")); |
| } |
| EXPECT_EQ(kMaxEventsThreshold, GetEvents()->size()); |
| |
| // Only count later events. |
| EXPECT_EQ(kNumLaterEvents, |
| CountEventsWithinInterval(kExpiryThresholdSeconds, |
| EventHistory::kClockTypeBoottime, |
| GetTimestamp(kTimeLate, kTimeLate, ""))); |
| |
| // Count all events. |
| EXPECT_EQ( |
| kMaxEventsThreshold, |
| CountEventsWithinInterval(kTimeLate, EventHistory::kClockTypeBoottime, |
| GetTimestamp(kTimeLate, kTimeLate, ""))); |
| } |
| |
| } // namespace shill |