| // Copyright 2020 The Fuchsia 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 "src/local_aggregation/backfill_manager.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include <gmock/gmock.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/util/clock.h" |
| #include "src/lib/util/datetime_util.h" |
| #include "src/local_aggregation/aggregation_procedures/aggregation_procedure.h" |
| #include "src/local_aggregation/civil_time_manager.h" |
| #include "src/public/lib/statusor/status_macros.h" |
| |
| namespace cobalt::local_aggregation { |
| |
| using TimeInfo = util::TimeInfo; |
| |
| namespace { |
| MetricDefinition MetricWithTimeZonePolicy(MetricDefinition::TimeZonePolicy policy) { |
| MetricDefinition metric; |
| metric.set_time_zone_policy(policy); |
| return metric; |
| } |
| |
| std::vector<uint32_t> ToHourIds(std::vector<TimeInfo> times) { |
| std::vector<uint32_t> output; |
| std::transform(times.begin(), times.end(), std::back_inserter(output), |
| [](TimeInfo time) { return time.hour_id; }); |
| return output; |
| } |
| |
| std::vector<uint32_t> ToDayIndices(std::vector<TimeInfo> times) { |
| std::vector<uint32_t> output; |
| std::transform(times.begin(), times.end(), std::back_inserter(output), |
| [](TimeInfo time) { return time.day_index; }); |
| return output; |
| } |
| } // namespace |
| |
| TEST(BackfillManager, HourlyBackfill) { |
| const uint32_t kNumHoursToGenerate = 5; |
| BackfillManager manager(2); |
| util::UtcTimeConverter converter; |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::UTC); |
| |
| std::chrono::system_clock::time_point start_time(std::chrono::hours(10001) / |
| util::kHourIdsPerHour); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo last_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| ASSERT_EQ(last_time_info.hour_id, 10001); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(last_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({10003, 10005, 10007, 10009, 10011})); |
| } |
| |
| TEST(BackfillManager, HourlyBackfillEdgecase) { |
| BackfillManager manager(2); |
| util::UtcTimeConverter converter; |
| |
| // This edgecase was discovered in testing. |
| // When end_epoch is set to 1 more than the day index corresponding to `end_time`, this will fail. |
| // When end_epoch is set to 2 more than the day index corresponding to `end_time`, this passes. |
| TimeInfo start = TimeInfo::FromHourId(0); |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(896489 / util::kHourIdsPerHour + 1)); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start, &civil_time_mgr, |
| MetricWithTimeZonePolicy(MetricDefinition::UTC), |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| std::vector<uint32_t> hour_sequence = ToHourIds(backfill); |
| EXPECT_EQ(hour_sequence[hour_sequence.size() - 1], 896489); |
| } |
| |
| TEST(BackfillManager, HourlyBackstopDayAligned) { |
| BackfillManager manager(1); |
| util::UtcTimeConverter converter; |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::UTC); |
| |
| const uint32_t kStartDayIndex = 100; |
| const uint32_t kStartHourId = (kStartDayIndex * util::kHourIdsPerDay) + 1; |
| // Set the end time to the first hour of the next day. |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kStartDayIndex + 2))); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromHourId(kStartHourId), &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| std::vector<uint32_t> hour_sequence = ToHourIds(backfill); |
| ASSERT_EQ(hour_sequence.size(), 24); |
| EXPECT_EQ(*hour_sequence.rbegin(), util::TimePointToHourIdUtc(end_time) - 2); |
| } |
| |
| TEST(BackfillManager, HourlyBackstopDayUnaligned) { |
| BackfillManager manager(1); |
| util::UtcTimeConverter converter; |
| |
| const uint32_t kStartDayIndex = 100; |
| const uint32_t kStartHourId = (kStartDayIndex * util::kHourIdsPerDay) + 1; |
| // Set the end time to 3 hours before the end of the same day. |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kStartDayIndex + 1) - 3)); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromHourId(kStartHourId), &civil_time_mgr, |
| MetricWithTimeZonePolicy(MetricDefinition::UTC), /*is_daily=*/false, |
| /*is_expedited=*/false)); |
| |
| std::vector<uint32_t> hour_sequence = ToHourIds(backfill); |
| ASSERT_EQ(*hour_sequence.rbegin(), util::TimePointToHourIdUtc(end_time) - 2); |
| EXPECT_EQ(hour_sequence.size(), 20); |
| } |
| |
| TEST(BackfillManager, DailyBackfill) { |
| BackfillManager manager(5); |
| util::UtcTimeConverter converter; |
| |
| const uint32_t kStartDayIndex = 10001; |
| const uint32_t kNumDaysToGenerate = 5; |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kStartDayIndex + kNumDaysToGenerate + 1))); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromDayIndex(kStartDayIndex), &civil_time_mgr, |
| MetricWithTimeZonePolicy(MetricDefinition::UTC), /*is_daily=*/true, |
| /*is_expedited=*/false)); |
| |
| EXPECT_EQ(ToDayIndices(backfill), std::vector<uint32_t>({10002, 10003, 10004, 10005, 10006})); |
| } |
| |
| TEST(BackfillManager, DailyBackfillExpedited) { |
| BackfillManager manager(2); |
| util::UtcTimeConverter converter; |
| |
| const uint32_t kStartDayIndex = 10001; |
| const uint32_t kNumPastDays = 2; |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kStartDayIndex + kNumPastDays + 1))); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromDayIndex(kStartDayIndex), &civil_time_mgr, |
| MetricWithTimeZonePolicy(MetricDefinition::UTC), |
| /*is_daily=*/true, |
| /*is_expedited=*/true)); |
| |
| // Expect day indices for the 2 past days in addition to the day index of `end_time`. |
| ASSERT_EQ(util::TimePointToDayIndexUtc(end_time), 10004); |
| EXPECT_EQ(ToDayIndices(backfill), std::vector<uint32_t>({10002, 10003, 10004})); |
| } |
| |
| TEST(BackfillManager, DailyBackstop) { |
| BackfillManager manager(10); |
| util::UtcTimeConverter converter; |
| |
| const uint32_t kStartDayIndex = 100; |
| const uint32_t kLastDayIndex = 120; |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kLastDayIndex + 1))); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromDayIndex(kStartDayIndex), &civil_time_mgr, |
| MetricWithTimeZonePolicy(MetricDefinition::UTC), /*is_daily=*/true, |
| /*is_expedited=*/false)); |
| |
| std::vector<uint32_t> day_sequence = ToDayIndices(backfill); |
| ASSERT_EQ(day_sequence.size(), 10); |
| EXPECT_EQ(*day_sequence.rbegin(), kLastDayIndex); |
| } |
| |
| // Test backfill calculation for an OTHER_TIME_ZONE metric, with a time zone that agrees with UTC. |
| // The result should be the same as for a UTC metric. |
| TEST(BackfillManager, HourlyBackfillOtherTimeZone) { |
| const uint32_t kNumHoursToGenerate = 5; |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/0, /*start_isdst=*/false); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo start_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| ASSERT_EQ(start_time_info.hour_id, 10001); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({10003, 10005, 10007, 10009, 10011})); |
| } |
| |
| // Test backfill calculation for an OTHER_TIME_ZONE metric with a time zone at an offset from UTC. |
| TEST(BackfillManager, HourlyBackfillOtherTimeZoneUtcOffset) { |
| const uint32_t kNumHoursToGenerate = 5; |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/8, /*start_isdst=*/false); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo start_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| ASSERT_EQ(start_time_info.hour_id, 10017); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({10019, 10021, 10023, 10025, 10027})); |
| } |
| |
| // Test backfill calculation for an OTHER_TIME_ZONE metric, in a case where the backstop time falls |
| // multiple hours after the beginning of the backfill period in the metric's time zone. |
| TEST(BackfillManager, HourlyBackfillOtherTimeZoneEarlyBackstop) { |
| const uint32_t kNumHoursToGenerate = 24; |
| BackfillManager manager(1); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time(std::chrono::hours(util::kNumHoursPerDay * 100)); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| |
| // The end time has day index 101 with respect to UTC, and the backfill period is 2 days. |
| // The backfill manager should only return hour IDs that correspond to day indices 100 and 101. |
| ASSERT_EQ(util::TimePointToDayIndexUtc(end_time), 101); |
| |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/-23, /*start_isdst=*/false); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| // All times between `start_time` and `end_time`-3h have day index 99 with respect to `metric`'s |
| // time zone. Only the hours `end_time`-2h and `end_time`-1h (hour IDs 4801 and 4803) fall within |
| // the backfill period. |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo start_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| ASSERT_EQ(start_time_info.hour_id, 4755); |
| ASSERT_EQ(start_time_info.day_index, 99); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| TimeInfo boundary_info, |
| TimeInfo::FromTimePoint(end_time - std::chrono::hours(3), converter, metric)); |
| ASSERT_EQ(boundary_info.hour_id, 4799); |
| ASSERT_EQ(boundary_info.day_index, 99); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo end_time_info, |
| TimeInfo::FromTimePoint(end_time, converter, metric)); |
| ASSERT_EQ(end_time_info.hour_id, 4805); |
| ASSERT_EQ(end_time_info.day_index, 100); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({4801, 4803})); |
| } |
| |
| // Test backfill calculation for an OTHER_TIME_ZONE metric, with a time zone that agrees with UTC |
| // except that the DST flag is set. |
| TEST(BackfillManager, HourlyBackfillOtherTimeZoneDst) { |
| const uint32_t kNumHoursToGenerate = 5; |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/0, /*start_isdst=*/true); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo start_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| ASSERT_EQ(start_time_info.hour_id, 10000); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({10002, 10004, 10006, 10008, 10010})); |
| } |
| |
| // Test backfill calculation when DST starts during the backfill period. |
| TEST(BackfillManager, HourlyBackfillWithDstStart) { |
| const uint32_t kNumHoursToGenerate = 5; |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| |
| // Use a civil time converter that simulates a transition from GMT (UTC+0, not DST) to British |
| // Summer Time (UTC+1, DST), with the transition occurring 2 hours after the last observation was |
| // generated. |
| util::FakeCivilTimeConverter converter( |
| /*start_utc_offset=*/0, /*start_isdst=*/false, |
| /*end_utc_offset=*/1, /*end_isdst=*/true, |
| /*threshold=*/start_time + 2 * util::kOneHour); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo start_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| ASSERT_EQ(start_time_info.hour_id, 10001); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| // The first hour ID in the backfill period is odd (not DST) and the remainder are even (DST). |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({10003, 10006, 10008, 10010, 10012})); |
| } |
| |
| // Test backfill calculation when DST ends during the backfill period. |
| TEST(BackfillManager, HourlyBackfillWithDstEnd) { |
| const uint32_t kNumHoursToGenerate = 5; |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| |
| // Use a civil time converter that simulates a transition from British Summer Time (UTC+1, DST) to |
| // GMT (UTC+0, not DST) with the transition occurring 2 hours after the last observation was |
| // generated. |
| util::FakeCivilTimeConverter converter( |
| /*start_utc_offset=*/1, /*start_isdst=*/true, |
| /*end_utc_offset=*/0, /*end_isdst=*/false, |
| /*threshold=*/start_time + std::chrono::hours(2)); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo start_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| ASSERT_EQ(start_time_info.hour_id, 10002); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| // The first hour ID in the backfill period is even (DST) and the remainder are odd (not DST). |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({10004, 10005, 10007, 10009, 10011})); |
| } |
| |
| // Tests that the sequence of hour IDs returned by the BackfillManager agrees with the sequence of |
| // hour IDs assigned to logged events, when the time period includes the start of DST. |
| TEST(BackfillManager, HourlyBackfillSameAsLoggedHoursWithDstStart) { |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = start_time + util::kOneDay; |
| |
| // Use a civil time converter that simulates a transition from GMT (UTC+0, not DST) to British |
| // Summer Time (UTC+1, DST), with the transition occurring 2 hours after the last observation was |
| // generated. |
| util::FakeCivilTimeConverter converter( |
| /*start_utc_offset=*/0, /*start_isdst=*/false, |
| /*end_utc_offset=*/1, /*end_isdst=*/true, |
| /*threshold=*/start_time + 2 * util::kOneHour); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo last_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(last_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| // Generate all possible hour IDs from the start to end of the time window. |
| std::set<uint32_t> hour_ids_set; |
| std::chrono::system_clock::time_point time = start_time; |
| while (time < end_time) { |
| CB_ASSERT_OK_AND_ASSIGN(uint32_t hour_id, util::TimePointToHourId(time, converter, metric)); |
| hour_ids_set.insert(hour_id); |
| time += std::chrono::minutes(1); |
| } |
| // The hour ID of the last generated observations shouldn't show up in `backfill`. |
| hour_ids_set.erase(last_time_info.hour_id); |
| std::vector hour_ids(hour_ids_set.begin(), hour_ids_set.end()); |
| std::sort(hour_ids.begin(), hour_ids.end()); |
| EXPECT_THAT(ToHourIds(backfill), hour_ids); |
| } |
| |
| // Tests that the sequence of hour IDs returned by the BackfillManager agrees with the sequence of |
| // hour IDs assigned to logged events, when the time period includes the end of DST. |
| TEST(BackfillManager, HourlyBackfillSameAsLoggedHoursWithDstEnd) { |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = start_time + util::kOneDay; |
| |
| // Use a civil time converter that simulates a transition from British Summer Time (UTC+1, DST) to |
| // GMT (UTC+0, not DST) with the transition occurring 2 hours after the last observation was |
| // generated. |
| util::FakeCivilTimeConverter converter( |
| /*start_utc_offset=*/1, /*start_isdst=*/true, |
| /*end_utc_offset=*/0, /*end_isdst=*/false, |
| /*threshold=*/start_time + std::chrono::hours(2)); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo last_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(last_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| |
| // Generate all possible hour IDs from the start to end of the time window. |
| std::set<uint32_t> hour_ids_set; |
| std::chrono::system_clock::time_point time = start_time; |
| while (time < end_time) { |
| CB_ASSERT_OK_AND_ASSIGN(uint32_t hour_id, util::TimePointToHourId(time, converter, metric)); |
| hour_ids_set.insert(hour_id); |
| time += std::chrono::minutes(1); |
| } |
| // The hour ID of the last generated observations shouldn't show up in `backfill`. |
| hour_ids_set.erase(last_time_info.hour_id); |
| std::vector hour_ids(hour_ids_set.begin(), hour_ids_set.end()); |
| std::sort(hour_ids.begin(), hour_ids.end()); |
| EXPECT_THAT(ToHourIds(backfill), hour_ids); |
| } |
| |
| // Test a simple case of daily backfill for an OTHER_TIME_ZONE metric. |
| TEST(BackfillManager, DailyBackfillOtherTimeZone) { |
| BackfillManager manager(5); |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/8, /*start_isdst=*/false); |
| |
| const uint32_t kStartDayIndex = 10001; |
| const uint32_t kNumDaysToGenerate = 5; |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kStartDayIndex + kNumDaysToGenerate + 1))); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromDayIndex(kStartDayIndex), &civil_time_mgr, |
| MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE), |
| /*is_daily=*/true, /*is_expedited=*/false)); |
| |
| EXPECT_EQ(ToDayIndices(backfill), std::vector<uint32_t>({10002, 10003, 10004, 10005, 10006})); |
| } |
| |
| // Test daily backfill when the final day index in the metric's time zone is smaller than the final |
| // day index in UTC. |
| TEST(BackfillManager, DailyBackfillShiftedOtherTimeZone) { |
| BackfillManager manager(5); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/-23, /*start_isdst=*/false); |
| |
| const uint32_t kStartDayIndex = 10001; |
| const uint32_t kNumDaysToGenerate = 5; |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kStartDayIndex + kNumDaysToGenerate + 1))); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| // The day index of `end_time` in `metric`'s time zone is 10006 (rather than 10007 in UTC). |
| CB_ASSERT_OK_AND_ASSIGN(uint32_t end_day_index, |
| util::TimePointToDayIndex(end_time, converter, metric)); |
| ASSERT_EQ(end_day_index, 10006); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromDayIndex(kStartDayIndex), &civil_time_mgr, metric, |
| /*is_daily=*/true, /*is_expedited=*/false)); |
| |
| // Since the day index of `end_time` is 10006, only return 4 day indices (instead of 5 as would be |
| // expected for UTC). |
| EXPECT_EQ(ToDayIndices(backfill), std::vector<uint32_t>({10002, 10003, 10004, 10005})); |
| } |
| |
| // Test daily backfill when the backstop day is multiple days later than the last day for which |
| // observations were generated. |
| TEST(BackfillManager, DailyBackstopOtherTimeZone) { |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/-23, /*start_isdst=*/false); |
| |
| const uint32_t kStartDayIndex = 10001; |
| const uint32_t kNumDaysToGenerate = 5; |
| std::chrono::system_clock::time_point end_time( |
| std::chrono::hours(util::kNumHoursPerDay * (kStartDayIndex + kNumDaysToGenerate + 1))); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| // The day index of `end_time` in `metric`'s time zone is 10006. |
| CB_ASSERT_OK_AND_ASSIGN(uint32_t end_day_index, |
| util::TimePointToDayIndex(end_time, converter, metric)); |
| ASSERT_EQ(end_day_index, 10006); |
| |
| CB_ASSERT_OK_AND_ASSIGN( |
| std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(TimeInfo::FromDayIndex(kStartDayIndex), &civil_time_mgr, metric, |
| /*is_daily=*/true, /*is_expedited=*/false)); |
| |
| // Only the previous day index, 10005, is permitted by the 2-day backfill window. |
| EXPECT_EQ(ToDayIndices(backfill), std::vector<uint32_t>({10005})); |
| } |
| |
| // Test that the BackfillManager gives the expected result when TimeInfos for past hours are read |
| // from the CivilTimeManager's cache. |
| TEST(BackfillManager, HourlyBackfillWithCaching) { |
| const uint32_t kNumHoursToGenerate = 5; |
| BackfillManager manager(2); |
| MetricDefinition metric = MetricWithTimeZonePolicy(MetricDefinition::OTHER_TIME_ZONE); |
| metric.set_other_time_zone("other_tz"); |
| |
| std::chrono::system_clock::time_point start_time( |
| std::chrono::hours(10001 / util::kHourIdsPerHour)); |
| std::chrono::system_clock::time_point end_time = |
| start_time + std::chrono::hours(kNumHoursToGenerate + 1); |
| |
| util::FakeCivilTimeConverter converter(/*start_utc_offset=*/0, /*start_isdst=*/true); |
| CivilTimeManager civil_time_mgr(end_time, converter); |
| |
| CB_ASSERT_OK_AND_ASSIGN(TimeInfo start_time_info, |
| TimeInfo::FromTimePoint(start_time, converter, metric)); |
| |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| EXPECT_EQ(ToHourIds(backfill), std::vector<uint32_t>({10002, 10004, 10006, 10008, 10010})); |
| |
| // When we compute backfill for an hourly metric with the same time zone policy, the |
| // CivilTimeManager fetches the TimeInfo for each past hour from its cache. |
| CB_ASSERT_OK_AND_ASSIGN(std::vector<TimeInfo> backfill_2, |
| manager.CalculateBackfill(start_time_info, &civil_time_mgr, metric, |
| /*is_daily=*/false, /*is_expedited=*/false)); |
| EXPECT_EQ(ToHourIds(backfill_2), ToHourIds(backfill)); |
| } |
| |
| } // namespace cobalt::local_aggregation |