| // Copyright 2019 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/aggregate_store.h" |
| |
| #include <algorithm> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include <google/protobuf/io/coded_stream.h> |
| #include <google/protobuf/io/zero_copy_stream_impl_lite.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/util/clock.h" |
| #include "src/lib/util/datetime_util.h" |
| #include "src/lib/util/proto_util.h" |
| #include "src/lib/util/testing/test_with_files.h" |
| #include "src/local_aggregation/aggregation_utils.h" |
| #include "src/local_aggregation/event_aggregator_mgr.h" |
| #include "src/local_aggregation/test_utils/test_event_aggregator_mgr.h" |
| #include "src/logger/logger_test_utils.h" |
| #include "src/logger/testing_constants.h" |
| #include "src/pb/event.pb.h" |
| #include "src/pb/metadata_builder.h" |
| #include "src/registry/packed_event_codes.h" |
| #include "src/registry/project_configs.h" |
| |
| namespace cobalt { |
| |
| using config::PackEventCodes; |
| using encoder::ClientSecret; |
| using encoder::SystemDataInterface; |
| using logger::Encoder; |
| using logger::EventRecord; |
| using logger::kInvalidArguments; |
| using logger::kOK; |
| using logger::MetricReportId; |
| using logger::ObservationWriter; |
| using logger::ProjectContext; |
| using logger::Status; |
| using logger::testing::CheckPerDeviceNumericObservations; |
| using logger::testing::CheckUniqueActivesObservations; |
| using logger::testing::ExpectedAggregationParams; |
| using logger::testing::ExpectedPerDeviceNumericObservations; |
| using logger::testing::ExpectedReportParticipationObservations; |
| using logger::testing::ExpectedUniqueActivesObservations; |
| using logger::testing::FakeObservationStore; |
| using logger::testing::FetchAggregatedObservations; |
| using logger::testing::GetTestProject; |
| using logger::testing::MakeAggregationKey; |
| using logger::testing::MakeExpectedReportParticipationObservations; |
| using logger::testing::MakeNullExpectedUniqueActivesObservations; |
| using logger::testing::TestUpdateRecipient; |
| using util::EncryptedMessageMaker; |
| using util::IncrementingSteadyClock; |
| using util::IncrementingSystemClock; |
| using util::SerializeToBase64; |
| using util::TimeToDayIndex; |
| |
| namespace local_aggregation { |
| |
| namespace { |
| // Number of seconds in a day |
| constexpr int kDay = 60 * 60 * 24; |
| // Number of seconds in an ideal year |
| constexpr int kYear = kDay * 365; |
| |
| template <typename T> |
| std::string SerializeAsStringDeterministic(const T& message) { |
| std::string s; |
| { |
| google::protobuf::io::StringOutputStream output(&s); |
| google::protobuf::io::CodedOutputStream out(&output); |
| out.SetSerializationDeterministic(true); |
| message.SerializePartialToCodedStream(&out); |
| } |
| return s; |
| } |
| |
| constexpr uint32_t kTestCustomerId = 34; |
| constexpr uint32_t kTestProjectId = 123; |
| constexpr uint32_t kTestMetricId = 1; |
| constexpr uint32_t kTestReportId = 3; |
| constexpr uint64_t kTestEventCode = 1; |
| constexpr uint32_t kTestDayIndex = 56; |
| |
| // A map keyed by base64-encoded, serialized ReportAggregationKeys. The value at |
| // a key is a map of event codes to sets of day indices. Used in tests as |
| // a record, external to the LocalAggregateStore, of the activity logged for |
| // UNIQUE_N_DAY_ACTIVES reports. |
| using LoggedActivity = std::map<std::string, std::map<uint32_t, std::set<uint32_t>>>; |
| |
| // A map used in tests as a record, external to the LocalAggregateStore, of the |
| // activity logged for PER_DEVICE_NUMERIC_STATS reports. The keys are, in |
| // descending order, serialized ReportAggregationKeys, components, event codes, |
| // and day indices. Each day index maps to a vector of numeric values that were |
| // logged for that day index.. |
| using LoggedValues = |
| std::map<std::string, |
| std::map<std::string, std::map<uint32_t, std::map<uint32_t, std::vector<int64_t>>>>>; |
| |
| std::unique_ptr<ProjectContext> GetProjectContextFor(const MetricDefinition& metric) { |
| auto project_config = std::make_unique<ProjectConfig>(); |
| project_config->set_project_name("test_project"); |
| project_config->set_project_id(metric.project_id()); |
| *project_config->add_metrics() = metric; |
| return std::make_unique<ProjectContext>(metric.customer_id(), "test_customer", |
| std::move(project_config)); |
| } |
| |
| MetricDefinition GetMetricWithId(uint32_t customer_id, uint32_t project_id, uint32_t metric_id) { |
| MetricDefinition metric_definition; |
| metric_definition.set_metric_name("test_metric"); |
| metric_definition.set_id(metric_id); |
| metric_definition.set_customer_id(customer_id); |
| metric_definition.set_project_id(project_id); |
| return metric_definition; |
| } |
| |
| MetricDefinition GetEventOccurredMetric(uint32_t customer_id, uint32_t project_id, |
| uint32_t metric_id) { |
| MetricDefinition metric_definition = GetMetricWithId(customer_id, project_id, metric_id); |
| metric_definition.set_metric_type(MetricDefinition::EVENT_OCCURRED); |
| return metric_definition; |
| } |
| |
| MetricDefinition GetEventCountMetric(uint32_t customer_id, uint32_t project_id, |
| uint32_t metric_id) { |
| MetricDefinition metric_definition = GetMetricWithId(customer_id, project_id, metric_id); |
| metric_definition.set_metric_type(MetricDefinition::EVENT_COUNT); |
| return metric_definition; |
| } |
| |
| std::tuple<MetricDefinition, ReportDefinition> GetUniqueActivesMetricAndReport(uint32_t customer_id, |
| uint32_t project_id, |
| uint32_t metric_id, |
| uint32_t report_id) { |
| ReportDefinition report_definition; |
| report_definition.set_report_name("test_unique_actives_report"); |
| report_definition.set_id(report_id); |
| report_definition.set_report_type(ReportDefinition::UNIQUE_N_DAY_ACTIVES); |
| *report_definition.add_aggregation_window() = MakeDayWindow(1); |
| |
| MetricDefinition metric_definition = GetEventOccurredMetric(customer_id, project_id, metric_id); |
| *metric_definition.add_reports() = report_definition; |
| |
| return std::make_tuple(metric_definition, report_definition); |
| } |
| |
| std::tuple<MetricDefinition, ReportDefinition> GetPerDeviceNumericStatsMetricAndReport( |
| uint32_t customer_id, uint32_t project_id, uint32_t metric_id, uint32_t report_id, |
| ReportDefinition::OnDeviceAggregationType aggregation_type) { |
| ReportDefinition report_definition; |
| report_definition.set_report_name("test_per_device_numeric_stats_report"); |
| report_definition.set_id(report_id); |
| report_definition.set_report_type(ReportDefinition::PER_DEVICE_NUMERIC_STATS); |
| report_definition.set_aggregation_type(aggregation_type); |
| *report_definition.add_aggregation_window() = MakeDayWindow(1); |
| |
| MetricDefinition metric_definition = GetEventCountMetric(customer_id, project_id, metric_id); |
| *metric_definition.add_reports() = report_definition; |
| |
| return std::make_tuple(metric_definition, report_definition); |
| } |
| |
| std::tuple<MetricDefinition, ReportDefinition> GetNotLocallyAggregatedMetricAndReport( |
| uint32_t customer_id, uint32_t project_id, uint32_t metric_id, uint32_t report_id) { |
| ReportDefinition report_definition; |
| report_definition.set_report_name("test_report"); |
| report_definition.set_id(report_id); |
| report_definition.set_report_type(ReportDefinition::SIMPLE_OCCURRENCE_COUNT); |
| *report_definition.add_aggregation_window() = MakeDayWindow(1); |
| |
| MetricDefinition metric_definition = GetEventOccurredMetric(customer_id, project_id, metric_id); |
| *metric_definition.add_reports() = report_definition; |
| |
| return std::make_tuple(metric_definition, report_definition); |
| } |
| |
| } // namespace |
| |
| // AggregateStoreTest creates an EventAggregator which sends its Observations |
| // to a FakeObservationStore. The EventAggregator is not pre-populated with |
| // aggregation configurations. |
| class AggregateStoreTest : public util::testing::TestWithFiles { |
| protected: |
| void SetUp() override { |
| MakeTestFolder(); |
| observation_store_ = std::make_unique<FakeObservationStore>(); |
| update_recipient_ = std::make_unique<TestUpdateRecipient>(); |
| observation_encrypter_ = EncryptedMessageMaker::MakeUnencrypted(); |
| observation_writer_ = std::make_unique<ObservationWriter>( |
| observation_store_.get(), update_recipient_.get(), observation_encrypter_.get()); |
| metadata_builder_ = |
| std::make_unique<MetadataBuilder>(system_data_.get(), system_data_cache_path(), fs()); |
| encoder_ = |
| std::make_unique<Encoder>(ClientSecret::GenerateNewSecret(), metadata_builder_.get()); |
| ResetEventAggregator(); |
| } |
| |
| void ResetEventAggregator() { |
| CobaltConfig cfg = {.client_secret = system_data::ClientSecret::GenerateNewSecret()}; |
| |
| cfg.local_aggregation_backfill_days = 0; |
| cfg.local_aggregate_proto_store_path = aggregate_store_path(); |
| cfg.obs_history_proto_store_path = obs_history_path(); |
| |
| event_aggregator_mgr_ = std::make_unique<TestEventAggregatorManager>(cfg, fs(), encoder_.get(), |
| observation_writer_.get()); |
| // Pass this clock to the EventAggregator::Start method, if it is called. |
| test_clock_ = std::make_unique<IncrementingSystemClock>(std::chrono::system_clock::duration(0)); |
| // Initilize it to 10 years after the beginning of time. |
| test_clock_->set_time(std::chrono::system_clock::time_point(std::chrono::seconds(10 * kYear))); |
| // Use this to advance the clock in the tests. |
| unowned_test_clock_ = test_clock_.get(); |
| day_store_created_ = CurrentDayIndex(); |
| test_steady_clock_ = new IncrementingSteadyClock(std::chrono::system_clock::duration(0)); |
| event_aggregator_mgr_->SetSteadyClock(test_steady_clock_); |
| } |
| |
| // Destruct the EventAggregator (thus calling EventAggregator::ShutDown()) |
| // before destructing the objects which the EventAggregator points to but does |
| // not own. |
| void TearDown() override { event_aggregator_mgr_.reset(); } |
| |
| // Advances |test_clock_| by |num_seconds| seconds. |
| void AdvanceClock(int num_seconds) { |
| unowned_test_clock_->increment_by(std::chrono::seconds(num_seconds)); |
| test_steady_clock_->increment_by(std::chrono::seconds(num_seconds)); |
| } |
| |
| // Returns the day index of the current day according to |test_clock_|, in |
| // |time_zone|, without incrementing the clock. |
| uint32_t CurrentDayIndex(MetricDefinition::TimeZonePolicy time_zone = MetricDefinition::UTC) { |
| return TimeToDayIndex(std::chrono::system_clock::to_time_t(unowned_test_clock_->peek_now()), |
| time_zone); |
| } |
| |
| size_t GetBackfillDays() { return event_aggregator_mgr_->aggregate_store_->backfill_days_; } |
| |
| void SetBackfillDays(size_t num_days) { |
| event_aggregator_mgr_->aggregate_store_->backfill_days_ = num_days; |
| } |
| |
| Status BackUpLocalAggregateStore() { |
| return event_aggregator_mgr_->aggregate_store_->BackUpLocalAggregateStore(); |
| } |
| |
| Status BackUpObservationHistory() { |
| return event_aggregator_mgr_->aggregate_store_->BackUpObservationHistory(); |
| } |
| |
| LocalAggregateStore MakeNewLocalAggregateStore( |
| uint32_t version = kCurrentLocalAggregateStoreVersion) { |
| return event_aggregator_mgr_->aggregate_store_->MakeNewLocalAggregateStore(version); |
| } |
| |
| AggregatedObservationHistoryStore MakeNewObservationHistoryStore( |
| uint32_t version = kCurrentObservationHistoryStoreVersion) { |
| return event_aggregator_mgr_->aggregate_store_->MakeNewObservationHistoryStore(version); |
| } |
| |
| Status MaybeUpgradeLocalAggregateStore(LocalAggregateStore* store) { |
| return event_aggregator_mgr_->aggregate_store_->MaybeUpgradeLocalAggregateStore(store); |
| } |
| |
| Status MaybeUpgradeObservationHistoryStore(AggregatedObservationHistoryStore* store) { |
| return event_aggregator_mgr_->aggregate_store_->MaybeUpgradeObservationHistoryStore(store); |
| } |
| |
| LocalAggregateStore CopyLocalAggregateStore() { |
| return event_aggregator_mgr_->aggregate_store_->CopyLocalAggregateStore(); |
| } |
| |
| Status GenerateObservations(uint32_t final_day_index_utc, uint32_t final_day_index_local = 0u) { |
| return event_aggregator_mgr_->aggregate_store_->GenerateObservations(final_day_index_utc, |
| final_day_index_local); |
| } |
| |
| bool IsReportInStore(uint32_t customer_id, uint32_t project_id, uint32_t metric_id, |
| uint32_t report_id) { |
| std::string key; |
| ReportAggregationKey key_data; |
| key_data.set_customer_id(customer_id); |
| key_data.set_project_id(project_id); |
| key_data.set_metric_id(metric_id); |
| key_data.set_report_id(report_id); |
| SerializeToBase64(key_data, &key); |
| |
| auto locked = event_aggregator_mgr_->aggregate_store_->protected_aggregate_store_.lock(); |
| return locked->local_aggregate_store.by_report_key().count(key) > 0; |
| } |
| |
| bool IsActive(uint32_t customer_id, uint32_t project_id, uint32_t metric_id, uint32_t report_id, |
| uint64_t event_code, uint32_t day_index) { |
| std::string key; |
| ReportAggregationKey key_data; |
| key_data.set_customer_id(customer_id); |
| key_data.set_project_id(project_id); |
| key_data.set_metric_id(metric_id); |
| key_data.set_report_id(report_id); |
| SerializeToBase64(key_data, &key); |
| |
| auto locked = event_aggregator_mgr_->aggregate_store_->protected_aggregate_store_.lock(); |
| |
| auto aggregates = locked->local_aggregate_store.by_report_key().find(key); |
| if (aggregates == locked->local_aggregate_store.by_report_key().end()) { |
| return false; |
| } |
| |
| auto by_event_code = |
| aggregates->second.unique_actives_aggregates().by_event_code().find(event_code); |
| if (by_event_code == aggregates->second.unique_actives_aggregates().by_event_code().end()) { |
| return false; |
| } |
| |
| auto by_day_index = by_event_code->second.by_day_index().find(day_index); |
| if (by_day_index == by_event_code->second.by_day_index().end()) { |
| return false; |
| } |
| |
| return by_day_index->second.activity_daily_aggregate().activity_indicator(); |
| } |
| |
| std::optional<int64_t> GetValue(uint32_t customer_id, uint32_t project_id, uint32_t metric_id, |
| uint32_t report_id, const std::string& component, |
| uint64_t event_code, uint32_t day_index) { |
| std::string key; |
| ReportAggregationKey key_data; |
| key_data.set_customer_id(customer_id); |
| key_data.set_project_id(project_id); |
| key_data.set_metric_id(metric_id); |
| key_data.set_report_id(report_id); |
| SerializeToBase64(key_data, &key); |
| |
| auto locked = event_aggregator_mgr_->aggregate_store_->protected_aggregate_store_.lock(); |
| |
| auto aggregates = locked->local_aggregate_store.by_report_key().find(key); |
| if (aggregates == locked->local_aggregate_store.by_report_key().end()) { |
| return std::nullopt; |
| } |
| |
| auto by_component = aggregates->second.numeric_aggregates().by_component().find(component); |
| if (by_component == aggregates->second.numeric_aggregates().by_component().end()) { |
| return std::nullopt; |
| } |
| |
| auto by_event_code = by_component->second.by_event_code().find(event_code); |
| if (by_event_code == by_component->second.by_event_code().end()) { |
| return std::nullopt; |
| } |
| |
| auto by_day_index = by_event_code->second.by_day_index().find(day_index); |
| if (by_day_index == by_event_code->second.by_day_index().end()) { |
| return std::nullopt; |
| } |
| |
| return by_day_index->second.numeric_daily_aggregate().value(); |
| } |
| |
| AggregateStore* GetAggregateStore() { return event_aggregator_mgr_->aggregate_store_.get(); } |
| |
| Status GarbageCollect(uint32_t day_index_utc, uint32_t day_index_local = 0u) { |
| return event_aggregator_mgr_->aggregate_store_->GarbageCollect(day_index_utc, day_index_local); |
| } |
| |
| void DoScheduledTasksNow() { |
| // Steady values don't matter, just tell DoScheduledTasks to run everything. |
| auto steady_time = std::chrono::steady_clock::now(); |
| event_aggregator_mgr_->next_generate_obs_ = steady_time; |
| event_aggregator_mgr_->next_gc_ = steady_time; |
| event_aggregator_mgr_->DoScheduledTasks(unowned_test_clock_->now(), steady_time); |
| } |
| |
| // Clears the FakeObservationStore and resets the counts of Observations |
| // received by the FakeObservationStore and the TestUpdateRecipient. |
| void ResetObservationStore() { |
| observation_store_->messages_received.clear(); |
| observation_store_->metadata_received.clear(); |
| observation_store_->ResetObservationCounter(); |
| update_recipient_->invocation_count = 0; |
| } |
| |
| // Given a ProjectContext |project_context| and the MetricReportId of a |
| // UNIQUE_N_DAY_ACTIVES report in |project_context|, as well as a day index |
| // and an event code, adds an EventOccurredEvent to the EventAggregator for |
| // that report, day index, and event code. If a non-null LoggedActivity map is |
| // provided, updates the map with information about the logged Event. |
| Status AddUniqueActivesEvent(std::shared_ptr<const ProjectContext> project_context, |
| const MetricReportId& metric_report_id, uint32_t day_index, |
| uint32_t event_code, LoggedActivity* logged_activity = nullptr) { |
| EventRecord event_record(std::move(project_context), metric_report_id.first); |
| event_record.event()->set_day_index(day_index); |
| event_record.event()->mutable_event_occurred_event()->set_event_code(event_code); |
| auto status = event_aggregator_mgr_->GetEventAggregator()->AddUniqueActivesEvent( |
| metric_report_id.second, event_record); |
| if (logged_activity == nullptr) { |
| return status; |
| } |
| std::string key; |
| if (!SerializeToBase64(MakeAggregationKey(*event_record.project_context(), metric_report_id), |
| &key)) { |
| return kInvalidArguments; |
| } |
| (*logged_activity)[key][event_code].insert(day_index); |
| return status; |
| } |
| |
| // Given a ProjectContext |project_context| and the MetricReportId of an |
| // EVENT_COUNT metric with a PER_DEVICE_NUMERIC_STATS report in |
| // |project_context|, as well as a day index, a component string, and an event |
| // code, adds a EventCountEvent to the EventAggregator for that report, day |
| // index, component, and event code. If a non-null LoggedValues map is |
| // provided, updates the map with information about the logged Event. |
| Status AddPerDeviceEventCountEvent(std::shared_ptr<const ProjectContext> project_context, |
| const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, uint32_t event_code, |
| int64_t count, LoggedValues* logged_values = nullptr) { |
| EventRecord event_record(std::move(project_context), metric_report_id.first); |
| event_record.event()->set_day_index(day_index); |
| auto event_count_event = event_record.event()->mutable_event_count_event(); |
| event_count_event->set_component(component); |
| event_count_event->add_event_code(event_code); |
| event_count_event->set_count(count); |
| auto status = event_aggregator_mgr_->GetEventAggregator()->AddEventCountEvent( |
| metric_report_id.second, event_record); |
| if (logged_values == nullptr) { |
| return status; |
| } |
| std::string key; |
| if (!SerializeToBase64(MakeAggregationKey(*event_record.project_context(), metric_report_id), |
| &key)) { |
| return kInvalidArguments; |
| } |
| (*logged_values)[key][component][event_code][day_index].push_back(count); |
| return status; |
| } |
| |
| // Given a ProjectContext |project_context| and the MetricReportId of an |
| // ELAPSED_TIME metric with a PER_DEVICE_NUMERIC_STATS report in |
| // |project_context|, as well as a day index, a component string, and an event |
| // code, logs an ElapsedTimeEvent to the EventAggregator for that report, day |
| // index, component, and event code. If a non-null LoggedValues map is |
| // provided, updates the map with information about the logged Event. |
| Status AddPerDeviceElapsedTimeEvent(std::shared_ptr<const ProjectContext> project_context, |
| const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, uint32_t event_code, |
| int64_t micros, LoggedValues* logged_values = nullptr) { |
| EventRecord event_record(std::move(project_context), metric_report_id.first); |
| event_record.event()->set_day_index(day_index); |
| auto elapsed_time_event = event_record.event()->mutable_elapsed_time_event(); |
| elapsed_time_event->set_component(component); |
| elapsed_time_event->add_event_code(event_code); |
| elapsed_time_event->set_elapsed_micros(micros); |
| auto status = event_aggregator_mgr_->GetEventAggregator()->AddElapsedTimeEvent( |
| metric_report_id.second, event_record); |
| if (logged_values == nullptr) { |
| return status; |
| } |
| std::string key; |
| if (!SerializeToBase64(MakeAggregationKey(*event_record.project_context(), metric_report_id), |
| &key)) { |
| return kInvalidArguments; |
| } |
| (*logged_values)[key][component][event_code][day_index].push_back(micros); |
| return status; |
| } |
| |
| // Given a ProjectContext |project_context| and the MetricReportId of a |
| // FRAME_RATE metric with a PER_DEVICE_NUMERIC_STATS report in |
| // |project_context|, as well as a day index, a component string, and an event |
| // code, logs a FrameRateEvent to the EventAggregator for that report, day |
| // index, component, and event code. If a non-null LoggedValues map is |
| // provided, updates the map with information about the logged Event. |
| Status AddPerDeviceFrameRateEvent(std::shared_ptr<const ProjectContext> project_context, |
| const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, uint32_t event_code, float fps, |
| LoggedValues* logged_values = nullptr) { |
| EventRecord event_record(std::move(project_context), metric_report_id.first); |
| event_record.event()->set_day_index(day_index); |
| auto frame_rate_event = event_record.event()->mutable_frame_rate_event(); |
| frame_rate_event->set_component(component); |
| frame_rate_event->add_event_code(event_code); |
| int64_t frames_per_1000_seconds = std::round(fps * 1000.0); |
| frame_rate_event->set_frames_per_1000_seconds(frames_per_1000_seconds); |
| auto status = event_aggregator_mgr_->GetEventAggregator()->AddFrameRateEvent( |
| metric_report_id.second, event_record); |
| if (logged_values == nullptr) { |
| return status; |
| } |
| std::string key; |
| if (!SerializeToBase64(MakeAggregationKey(*event_record.project_context(), metric_report_id), |
| &key)) { |
| return kInvalidArguments; |
| } |
| (*logged_values)[key][component][event_code][day_index].push_back(frames_per_1000_seconds); |
| return status; |
| } |
| |
| // Given a ProjectContext |project_context| and the MetricReportId of a |
| // MEMORY_USAGE metric with a PER_DEVICE_NUMERIC_STATS report in |
| // |project_context|, as well as a day index, a component string, and an event |
| // code, logs a MemoryUsageEvent to the EventAggregator for that report, day |
| // index, component, and event code. If a non-null LoggedValues map is |
| // provided, updates the map with information about the logged Event. |
| Status AddPerDeviceMemoryUsageEvent(std::shared_ptr<const ProjectContext> project_context, |
| const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, |
| const std::vector<uint32_t>& event_codes, int64_t bytes, |
| LoggedValues* logged_values = nullptr) { |
| EventRecord event_record(std::move(project_context), metric_report_id.first); |
| event_record.event()->set_day_index(day_index); |
| auto memory_usage_event = event_record.event()->mutable_memory_usage_event(); |
| memory_usage_event->set_component(component); |
| for (auto event_code : event_codes) { |
| memory_usage_event->add_event_code(event_code); |
| } |
| memory_usage_event->set_bytes(bytes); |
| auto status = event_aggregator_mgr_->GetEventAggregator()->AddMemoryUsageEvent( |
| metric_report_id.second, event_record); |
| if (logged_values == nullptr) { |
| return status; |
| } |
| std::string key; |
| if (!SerializeToBase64(MakeAggregationKey(*event_record.project_context(), metric_report_id), |
| &key)) { |
| return kInvalidArguments; |
| } |
| (*logged_values)[key][component][PackEventCodes(event_codes)][day_index].push_back(bytes); |
| return status; |
| } |
| |
| // Given a LoggedActivity map describing the events that have been logged |
| // to the EventAggregator, checks whether the contents of the |
| // LocalAggregateStore are as expected, accounting for any garbage |
| // collection. |
| // |
| // logged_activity: a LoggedActivity representing event occurrences |
| // since the LocalAggregateStore was created. All day indices should be |
| // greater than or equal to |day_store_created_| and less than or equal to |
| // |current_day_index|. |
| // |
| // current_day_index: The day index of the current day in the test's frame |
| // of reference. |
| bool CheckUniqueActivesAggregates(const LoggedActivity& logged_activity, |
| uint32_t /*current_day_index*/) { |
| auto local_aggregate_store = event_aggregator_mgr_->aggregate_store_->CopyLocalAggregateStore(); |
| // Check that the LocalAggregateStore contains no more UniqueActives |
| // aggregates than |logged_activity| and |day_last_garbage_collected_| |
| // should imply. |
| for (const auto& report_pair : local_aggregate_store.by_report_key()) { |
| const auto& aggregates = report_pair.second; |
| if (aggregates.type_case() != ReportAggregates::kUniqueActivesAggregates) { |
| continue; |
| } |
| const auto& report_key = report_pair.first; |
| // Check whether this ReportAggregationKey is in |logged_activity|. If |
| // not, expect that its by_event_code map is empty. |
| auto report_activity = logged_activity.find(report_key); |
| if (report_activity == logged_activity.end()) { |
| EXPECT_TRUE(aggregates.unique_actives_aggregates().by_event_code().empty()); |
| if (!aggregates.unique_actives_aggregates().by_event_code().empty()) { |
| return false; |
| } |
| break; |
| } |
| auto expected_events = report_activity->second; |
| for (const auto& event_pair : aggregates.unique_actives_aggregates().by_event_code()) { |
| // Check that this event code is in |logged_activity| under this |
| // ReportAggregationKey. |
| auto event_code = event_pair.first; |
| auto event_activity = expected_events.find(event_code); |
| EXPECT_NE(event_activity, expected_events.end()); |
| if (event_activity == expected_events.end()) { |
| return false; |
| } |
| const auto& expected_days = event_activity->second; |
| for (const auto& day_pair : event_pair.second.by_day_index()) { |
| // Check that this day index is in |logged_activity| under this |
| // ReportAggregationKey and event code. |
| const auto& day_index = day_pair.first; |
| auto day_activity = expected_days.find(day_index); |
| EXPECT_NE(day_activity, expected_days.end()); |
| if (day_activity == expected_days.end()) { |
| return false; |
| } |
| // Check that the day index is no earlier than is implied by the |
| // dates of store creation and garbage collection. |
| EXPECT_GE(day_index, EarliestAllowedDayIndex(aggregates.aggregation_config())); |
| if (day_index < EarliestAllowedDayIndex(aggregates.aggregation_config())) { |
| return false; |
| } |
| } |
| } |
| } |
| |
| // Check that the LocalAggregateStore contains aggregates for all events in |
| // |logged_activity|, as long as they are recent enough to have survived any |
| // garbage collection. |
| for (const auto& logged_pair : logged_activity) { |
| const auto& logged_key = logged_pair.first; |
| const auto& logged_event_map = logged_pair.second; |
| // Check that this ReportAggregationKey is in the LocalAggregateStore, and |
| // that the aggregates are of the expected type. |
| auto report_aggregates = local_aggregate_store.by_report_key().find(logged_key); |
| EXPECT_NE(report_aggregates, local_aggregate_store.by_report_key().end()); |
| if (report_aggregates == local_aggregate_store.by_report_key().end()) { |
| return false; |
| } |
| if (report_aggregates->second.type_case() != ReportAggregates::kUniqueActivesAggregates) { |
| return false; |
| } |
| // Compute the earliest day index that should appear among the aggregates |
| // for this report. |
| auto earliest_allowed = |
| EarliestAllowedDayIndex(report_aggregates->second.aggregation_config()); |
| for (const auto& logged_event_pair : logged_event_map) { |
| const auto& logged_event_code = logged_event_pair.first; |
| const auto& logged_days = logged_event_pair.second; |
| // Check whether this event code is in the LocalAggregateStore |
| // under this ReportAggregationKey. If not, check that all day indices |
| // for this event code are smaller than the day index of the earliest |
| // allowed aggregate. |
| auto event_code_aggregates = |
| report_aggregates->second.unique_actives_aggregates().by_event_code().find( |
| logged_event_code); |
| if (event_code_aggregates == |
| report_aggregates->second.unique_actives_aggregates().by_event_code().end()) { |
| for (auto day_index : logged_days) { |
| EXPECT_LT(day_index, earliest_allowed); |
| if (day_index >= earliest_allowed) { |
| return false; |
| } |
| } |
| break; |
| } |
| // Check that all of the day indices in |logged_activity| under this |
| // ReportAggregationKey and event code are in the |
| // LocalAggregateStore, as long as they are recent enough to have |
| // survived any garbage collection. Check that each aggregate has its |
| // activity field set to true. |
| for (const auto& logged_day_index : logged_days) { |
| auto day_aggregate = event_code_aggregates->second.by_day_index().find(logged_day_index); |
| if (logged_day_index >= earliest_allowed) { |
| EXPECT_NE(day_aggregate, event_code_aggregates->second.by_day_index().end()); |
| if (day_aggregate == event_code_aggregates->second.by_day_index().end()) { |
| return false; |
| } |
| EXPECT_TRUE(day_aggregate->second.activity_daily_aggregate().activity_indicator()); |
| if (!day_aggregate->second.activity_daily_aggregate().activity_indicator()) { |
| return false; |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| bool CheckPerDeviceNumericAggregates(const LoggedValues& logged_values, |
| uint32_t /*current_day_index*/) { |
| auto local_aggregate_store = event_aggregator_mgr_->aggregate_store_->CopyLocalAggregateStore(); |
| // Check that the LocalAggregateStore contains no more PerDeviceNumeric |
| // aggregates than |logged_values| and |day_last_garbage_collected_| should |
| // imply. |
| for (const auto& report_pair : local_aggregate_store.by_report_key()) { |
| const auto& aggregates = report_pair.second; |
| if (aggregates.type_case() != ReportAggregates::kNumericAggregates) { |
| continue; |
| } |
| const auto& report_key = report_pair.first; |
| // Check whether this ReportAggregationKey is in |logged_values|. If not, |
| // expect that its by_component map is empty. |
| auto report_values = logged_values.find(report_key); |
| if (report_values == logged_values.end()) { |
| EXPECT_TRUE(aggregates.numeric_aggregates().by_component().empty()); |
| if (!aggregates.numeric_aggregates().by_component().empty()) { |
| return false; |
| } |
| break; |
| } |
| auto expected_components = report_values->second; |
| for (const auto& component_pair : aggregates.numeric_aggregates().by_component()) { |
| // Check that this component is in |logged_values| under this |
| // ReportAggregationKey. |
| auto component = component_pair.first; |
| auto component_values = expected_components.find(component); |
| EXPECT_NE(component_values, expected_components.end()); |
| if (component_values == expected_components.end()) { |
| return false; |
| } |
| const auto& expected_events = component_values->second; |
| for (const auto& event_pair : component_pair.second.by_event_code()) { |
| // Check that this event code is in |logged_values| under this |
| // ReportAggregationKey and component. |
| const auto& event_code = event_pair.first; |
| auto event_values = expected_events.find(event_code); |
| EXPECT_NE(event_values, expected_events.end()); |
| if (event_values == expected_events.end()) { |
| return false; |
| } |
| const auto& expected_days = event_values->second; |
| for (const auto& day_pair : event_pair.second.by_day_index()) { |
| // Check that this day index is in |logged_values| under this |
| // ReportAggregationKey, component, and event code. |
| const auto& day_index = day_pair.first; |
| auto day_value = expected_days.find(day_index); |
| EXPECT_NE(day_value, expected_days.end()); |
| if (day_value == expected_days.end()) { |
| return false; |
| } |
| // Check that the day index is no earlier than is implied by the |
| // dates of store creation and garbage collection. |
| EXPECT_GE(day_index, EarliestAllowedDayIndex(aggregates.aggregation_config())); |
| if (day_index < EarliestAllowedDayIndex(aggregates.aggregation_config())) { |
| return false; |
| } |
| } |
| } |
| } |
| } |
| |
| // Check that the LocalAggregateStore contains aggregates for all values in |
| // |logged_values|, as long as they are recent enough to have survived any |
| // garbage collection. |
| for (const auto& logged_pair : logged_values) { |
| const auto& logged_key = logged_pair.first; |
| const auto& logged_component_map = logged_pair.second; |
| // Check that this ReportAggregationKey is in the LocalAggregateStore, and |
| // that the aggregates are of the expected type. |
| auto report_aggregates = local_aggregate_store.by_report_key().find(logged_key); |
| EXPECT_NE(report_aggregates, local_aggregate_store.by_report_key().end()); |
| if (report_aggregates == local_aggregate_store.by_report_key().end()) { |
| return false; |
| } |
| if (report_aggregates->second.type_case() != ReportAggregates::kNumericAggregates) { |
| return false; |
| } |
| const auto& aggregation_type = |
| report_aggregates->second.aggregation_config().report().aggregation_type(); |
| // Compute the earliest day index that should appear among the aggregates |
| // for this report. |
| auto earliest_allowed = |
| EarliestAllowedDayIndex(report_aggregates->second.aggregation_config()); |
| for (const auto& logged_component_pair : logged_component_map) { |
| const auto& logged_component = logged_component_pair.first; |
| const auto& logged_event_code_map = logged_component_pair.second; |
| // Check whether this component is in the LocalAggregateStore under this |
| // ReportAggregationKey. If not, check that all day indices for all |
| // entries in |logged_values| under this component are smaller than the |
| // day index of the earliest allowed aggregate. |
| bool component_found = false; |
| auto component_aggregates = |
| report_aggregates->second.numeric_aggregates().by_component().find(logged_component); |
| if (component_aggregates != |
| report_aggregates->second.numeric_aggregates().by_component().end()) { |
| component_found = true; |
| } |
| for (const auto& logged_event_pair : logged_event_code_map) { |
| const auto& logged_event_code = logged_event_pair.first; |
| const auto& logged_day_map = logged_event_pair.second; |
| // Check whether this event code is in the LocalAggregateStore under |
| // this ReportAggregationKey. If not, check that all day indices in |
| // |logged_values| under this component are smaller than the day index |
| // of the earliest allowed aggregate. |
| bool event_code_found = false; |
| if (component_found) { |
| auto event_code_aggregates = |
| component_aggregates->second.by_event_code().find(logged_event_code); |
| if (event_code_aggregates != component_aggregates->second.by_event_code().end()) { |
| event_code_found = true; |
| } |
| if (event_code_found) { |
| // Check that all of the day indices in |logged_values| under this |
| // ReportAggregationKey, component, and event code are in the |
| // LocalAggregateStore, as long as they are recent enough to have |
| // survived any garbage collection. Check that each aggregate has |
| // the expected value. |
| for (const auto& logged_day_pair : logged_day_map) { |
| auto logged_day_index = logged_day_pair.first; |
| auto logged_values = logged_day_pair.second; |
| auto day_aggregate = |
| event_code_aggregates->second.by_day_index().find(logged_day_index); |
| if (logged_day_index >= earliest_allowed) { |
| EXPECT_NE(day_aggregate, event_code_aggregates->second.by_day_index().end()); |
| if (day_aggregate == event_code_aggregates->second.by_day_index().end()) { |
| return false; |
| } |
| int64_t aggregate_from_logged_values = 0; |
| for (size_t index = 0; index < logged_values.size(); index++) { |
| switch (aggregation_type) { |
| case ReportDefinition::SUM: |
| aggregate_from_logged_values += logged_values[index]; |
| break; |
| case ReportDefinition::MAX: |
| aggregate_from_logged_values = |
| std::max(aggregate_from_logged_values, logged_values[index]); |
| break; |
| case ReportDefinition::MIN: |
| if (index == 0) { |
| aggregate_from_logged_values = logged_values[0]; |
| } |
| aggregate_from_logged_values = |
| std::min(aggregate_from_logged_values, logged_values[index]); |
| break; |
| default: |
| return false; |
| } |
| } |
| EXPECT_EQ(day_aggregate->second.numeric_daily_aggregate().value(), |
| aggregate_from_logged_values); |
| if (day_aggregate->second.numeric_daily_aggregate().value() != |
| aggregate_from_logged_values) { |
| return false; |
| } |
| } |
| } |
| } |
| } |
| if (!component_found | !event_code_found) { |
| for (const auto& logged_day_pair : logged_day_map) { |
| auto logged_day_index = logged_day_pair.first; |
| EXPECT_LT(logged_day_index, earliest_allowed); |
| if (logged_day_index >= earliest_allowed) { |
| return false; |
| } |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| // Given the AggregationConfig of a locally aggregated report, returns the |
| // earliest (smallest) day index for which an aggregate may exist in the |
| // LocalAggregateStore for that report, accounting for garbage |
| // collection and the number of backfill days. |
| uint32_t EarliestAllowedDayIndex(const AggregationConfig& config) { |
| // If the LocalAggregateStore has never been garbage-collected, then the |
| // earliest allowed day index is just the day when the store was created, |
| // minus the number of backfill days. |
| auto backfill_days = GetBackfillDays(); |
| EXPECT_GE(day_store_created_, backfill_days) |
| << "The day index of store creation must be larger than the number " |
| "of backfill days."; |
| if (day_last_garbage_collected_ == 0u) { |
| return day_store_created_ - backfill_days; |
| } |
| uint32_t max_aggregation_days = 1; |
| for (const auto& window : config.aggregation_window()) { |
| if (window.units_case() == OnDeviceAggregationWindow::kDays && |
| window.days() > max_aggregation_days) { |
| max_aggregation_days = window.days(); |
| } |
| } |
| // Otherwise, it is the later of: |
| // (a) The day index on which the store was created minus the number |
| // of backfill days. |
| // (b) The day index for which the store was last garbage-collected |
| // minus the number of backfill days, minus the largest window size in |
| // the report associated to |config|, plus 1. |
| EXPECT_GE(day_last_garbage_collected_, backfill_days) |
| << "The day index of last garbage collection must be larger than " |
| "the number of backfill days."; |
| |
| if (day_last_garbage_collected_ - backfill_days < (max_aggregation_days + 1)) { |
| return day_store_created_ - backfill_days; |
| } |
| return (day_store_created_ < (day_last_garbage_collected_ - max_aggregation_days + 1)) |
| ? (day_last_garbage_collected_ - backfill_days - max_aggregation_days + 1) |
| : day_store_created_ - backfill_days; |
| } |
| |
| std::unique_ptr<TestEventAggregatorManager> event_aggregator_mgr_; |
| std::unique_ptr<ObservationWriter> observation_writer_; |
| std::unique_ptr<MetadataBuilder> metadata_builder_; |
| std::unique_ptr<Encoder> encoder_; |
| std::unique_ptr<EncryptedMessageMaker> observation_encrypter_; |
| std::unique_ptr<TestUpdateRecipient> update_recipient_; |
| std::unique_ptr<FakeObservationStore> observation_store_; |
| std::unique_ptr<IncrementingSystemClock> test_clock_; |
| IncrementingSystemClock* unowned_test_clock_; |
| IncrementingSteadyClock* test_steady_clock_; |
| // The day index on which the LocalAggregateStore was last |
| // garbage-collected. A value of 0 indicates that the store has never been |
| // garbage-collected. |
| uint32_t day_last_garbage_collected_ = 0u; |
| // The day index on which the LocalAggregateStore was created. |
| uint32_t day_store_created_ = 0u; |
| |
| private: |
| std::unique_ptr<SystemDataInterface> system_data_; |
| }; |
| |
| // Creates an EventAggregator and provides it with a ProjectContext generated |
| // from a registry. |
| class AggregateStoreTestWithProjectContext : public AggregateStoreTest { |
| protected: |
| explicit AggregateStoreTestWithProjectContext(const std::string& registry_var_name) { |
| project_context_ = GetTestProject(registry_var_name); |
| } |
| |
| void SetUp() override { |
| AggregateStoreTest::SetUp(); |
| event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context_); |
| } |
| |
| // Adds an EventOccurredEvent to the local aggregations for the MetricReportId of a locally |
| // aggregated report of the ProjectContext. Overrides the method |
| // AggregateStoreTest::AddUniqueActivesEvent. |
| Status AddUniqueActivesEvent(const MetricReportId& metric_report_id, uint32_t day_index, |
| uint32_t event_code, LoggedActivity* logged_activity = nullptr) { |
| return AggregateStoreTest::AddUniqueActivesEvent(project_context_, metric_report_id, day_index, |
| event_code, logged_activity); |
| } |
| |
| // Logs a EventCountEvent for the MetricReportId of a locally |
| // aggregated report of the ProjectContext. Overrides the method |
| // AggregateStoreTest::AddPerDeviceEventCountEvent. |
| Status AddPerDeviceEventCountEvent(const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, uint32_t event_code, |
| int64_t count, LoggedValues* logged_values = nullptr) { |
| return AggregateStoreTest::AddPerDeviceEventCountEvent( |
| project_context_, metric_report_id, day_index, component, event_code, count, logged_values); |
| } |
| |
| // Adds an ElapsedTimeEvent to the local aggregations for the MetricReportId of a locally |
| // aggregated report of the ProjectContext. Overrides the method |
| // AggregateStoreTest::AddPerDeviceElapsedTimeEvent. |
| Status AddPerDeviceElapsedTimeEvent(const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, uint32_t event_code, |
| int64_t micros, LoggedValues* logged_values = nullptr) { |
| return AggregateStoreTest::AddPerDeviceElapsedTimeEvent(project_context_, metric_report_id, |
| day_index, component, event_code, |
| micros, logged_values); |
| } |
| |
| // Logs a FrameRateEvent for the MetricReportId of a locally |
| // aggregated report of the ProjectContext. Overrides the method |
| // AggregateStoreTest::AddPerDeviceFrameRateEvent. |
| Status AddPerDeviceFrameRateEvent(const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, uint32_t event_code, float fps, |
| LoggedValues* logged_values = nullptr) { |
| return AggregateStoreTest::AddPerDeviceFrameRateEvent( |
| project_context_, metric_report_id, day_index, component, event_code, fps, logged_values); |
| } |
| |
| // Adds a MemoryUsageEvent to the local aggregations for the MetricReportId of a locally |
| // aggregated report of the ProjectContext. Overrides the method |
| // AggregateStoreTest::AddPerDeviceMemoryUsageEvent. |
| Status AddPerDeviceMemoryUsageEvent(const MetricReportId& metric_report_id, uint32_t day_index, |
| const std::string& component, |
| const std::vector<uint32_t>& event_codes, int64_t bytes, |
| LoggedValues* logged_values = nullptr) { |
| return AggregateStoreTest::AddPerDeviceMemoryUsageEvent(project_context_, metric_report_id, |
| day_index, component, event_codes, |
| bytes, logged_values); |
| } |
| |
| private: |
| // A ProjectContext wrapping the MetricDefinitions passed to the |
| // constructor in |metric_string|. |
| std::shared_ptr<ProjectContext> project_context_; |
| }; |
| |
| // Creates an EventAggregator and provides it with a ProjectContext generated |
| // from test_registries/unique_actives_test_registry.yaml. All metrics in this |
| // registry are of type EVENT_OCCURRED and have a UNIQUE_N_DAY_ACTIVES report. |
| class UniqueActivesAggregateStoreTest : public AggregateStoreTestWithProjectContext { |
| protected: |
| UniqueActivesAggregateStoreTest() |
| : AggregateStoreTestWithProjectContext( |
| logger::testing::unique_actives::kCobaltRegistryBase64) {} |
| }; |
| |
| // Creates an EventAggregator and provides it with a ProjectContext generated |
| // from test_registries/unique_actives_noise_free_test_registry.yaml. All |
| // metrics in this registry are of type EVENT_OCCURRED and have a |
| // UNIQUE_N_DAY_ACTIVES report with local_privacy_noise_level NONE. |
| class UniqueActivesNoiseFreeAggregateStoreTest : public AggregateStoreTestWithProjectContext { |
| protected: |
| UniqueActivesNoiseFreeAggregateStoreTest() |
| : AggregateStoreTestWithProjectContext( |
| logger::testing::unique_actives_noise_free::kCobaltRegistryBase64) {} |
| }; |
| |
| // Creates an EventAggregator and provides it with a ProjectContext generated |
| // from test_registries/per_device_numeric_stats_test_registry.yaml. All metrics |
| // in this registry are of type EVENT_COUNT and have a PER_DEVICE_NUMERIC_STATS |
| // report. |
| class PerDeviceNumericAggregateStoreTest : public AggregateStoreTestWithProjectContext { |
| protected: |
| PerDeviceNumericAggregateStoreTest() |
| : AggregateStoreTestWithProjectContext( |
| logger::testing::per_device_numeric_stats::kCobaltRegistryBase64) {} |
| }; |
| |
| // Creates an EventAggregator and provides it with a ProjectContext generated |
| // from test_registries/mixed_time_zone_test_registry.yaml. This registry |
| // contains multiple MetricDefinitions with different time zone policies. |
| class NoiseFreeMixedTimeZoneAggregateStoreTest : public AggregateStoreTestWithProjectContext { |
| protected: |
| NoiseFreeMixedTimeZoneAggregateStoreTest() |
| : AggregateStoreTestWithProjectContext( |
| logger::testing::mixed_time_zone::kCobaltRegistryBase64) {} |
| }; |
| |
| class PerDeviceHistogramAggregateStoreTest : public AggregateStoreTestWithProjectContext { |
| protected: |
| PerDeviceHistogramAggregateStoreTest() |
| : AggregateStoreTestWithProjectContext( |
| logger::testing::per_device_histogram::kCobaltRegistryBase64) {} |
| }; |
| |
| class AggregateStoreWorkerTest : public AggregateStoreTest { |
| protected: |
| void SetUp() override { AggregateStoreTest::SetUp(); } |
| |
| void ShutDownWorkerThread() { event_aggregator_mgr_->ShutDown(); } |
| |
| bool in_shutdown_state() { return (shutdown_flag_set() && !worker_joinable()); } |
| |
| bool in_run_state() { return (!shutdown_flag_set() && worker_joinable()); } |
| |
| bool shutdown_flag_set() { |
| return event_aggregator_mgr_->protected_worker_thread_controller_.const_lock()->shut_down; |
| } |
| |
| bool worker_joinable() { return event_aggregator_mgr_->worker_thread_.joinable(); } |
| }; |
| |
| // Tests that the Read() method of each ConsistentProtoStore is called once |
| // during construction of the EventAggregator. |
| TEST_F(AggregateStoreTest, ReadProtosFromFiles) { |
| EXPECT_EQ(1, fs()->TimesRead(aggregate_store_path())); |
| EXPECT_EQ(1, fs()->TimesRead(obs_history_path())); |
| } |
| |
| // Tests that the BackUp*() methods return a positive status, and checks that |
| // the Write() method of a ConsistentProtoStore is called once when its |
| // respective BackUp*() method is called. |
| TEST_F(AggregateStoreTest, BackUpProtos) { |
| EXPECT_EQ(kOK, BackUpLocalAggregateStore()); |
| EXPECT_EQ(kOK, BackUpObservationHistory()); |
| EXPECT_EQ(1, fs()->TimesWritten(aggregate_store_path())); |
| EXPECT_EQ(1, fs()->TimesWritten(obs_history_path())); |
| } |
| |
| // MaybeUpgradeLocalAggregateStore should return an OK status if the version is current. The store |
| // should not change. |
| TEST_F(AggregateStoreTest, MaybeUpgradeLocalAggregateStoreCurrent) { |
| auto store = MakeNewLocalAggregateStore(); |
| std::string store_before = SerializeAsStringDeterministic(store); |
| ASSERT_EQ(kCurrentLocalAggregateStoreVersion, store.version()); |
| EXPECT_EQ(kOK, MaybeUpgradeLocalAggregateStore(&store)); |
| EXPECT_EQ(store_before, SerializeAsStringDeterministic(store)); |
| } |
| |
| // MaybeUpgradeLocalAggregateStore should return kInvalidArguments if it is not possible to upgrade |
| // to the current version. |
| TEST_F(AggregateStoreTest, MaybeUpgradeLocalAggregateStoreUnsupported) { |
| const uint32_t kFutureVersion = kCurrentLocalAggregateStoreVersion + 1; |
| auto store = MakeNewLocalAggregateStore(kFutureVersion); |
| ASSERT_EQ(kFutureVersion, store.version()); |
| EXPECT_EQ(kInvalidArguments, MaybeUpgradeLocalAggregateStore(&store)); |
| } |
| |
| // It should be possible to upgrade the LocalAggregateStore from v0 to the current version. The |
| // version number should be updated and the contents of window_size in each AggregationConfigs |
| // should be moved to aggregation_window, preserving their order. |
| TEST_F(AggregateStoreTest, MaybeUpgradeLocalAggregateStoreFromV0) { |
| const uint32_t kVersionZero = 0; |
| const std::vector<uint32_t> kWindowSizes = {1, 7, 30}; |
| const std::string kKey = "some_report_key"; |
| |
| // Make a v0 LocalAggregateStore with one report. |
| auto store = MakeNewLocalAggregateStore(kVersionZero); |
| ReportAggregates report_aggregates; |
| for (auto window_size : kWindowSizes) { |
| report_aggregates.mutable_aggregation_config()->add_window_size(window_size); |
| } |
| (*store.mutable_by_report_key())[kKey] = report_aggregates; |
| |
| // Make the expected upgraded store. |
| auto expected_store = MakeNewLocalAggregateStore(kCurrentLocalAggregateStoreVersion); |
| ReportAggregates expected_report_aggregates; |
| for (auto window_size : kWindowSizes) { |
| *expected_report_aggregates.mutable_aggregation_config()->add_aggregation_window() = |
| MakeDayWindow(window_size); |
| } |
| (*expected_store.mutable_by_report_key())[kKey] = expected_report_aggregates; |
| |
| // Upgrade and check that the upgraded store is as expected. |
| EXPECT_EQ(kOK, MaybeUpgradeLocalAggregateStore(&store)); |
| EXPECT_EQ(SerializeAsStringDeterministic(expected_store), SerializeAsStringDeterministic(store)); |
| } |
| |
| // MaybeUpgradeObservationHistoryStore should return an OK status if the version is current. The |
| // store should not change. |
| TEST_F(AggregateStoreTest, MaybeUpgradeObservationHistoryStoreCurrent) { |
| auto store = MakeNewObservationHistoryStore(); |
| std::string store_before = SerializeAsStringDeterministic(store); |
| ASSERT_EQ(kCurrentObservationHistoryStoreVersion, store.version()); |
| EXPECT_EQ(kOK, MaybeUpgradeObservationHistoryStore(&store)); |
| EXPECT_EQ(store_before, SerializeAsStringDeterministic(store)); |
| } |
| |
| // MaybeUpgradeObservationHistoryStore should return kInvalidArguments if it is not possible to |
| // upgrade to the current version. |
| TEST_F(AggregateStoreTest, MaybeUpgradeObservationHistoryStoreUnsupported) { |
| const uint32_t kFutureVersion = kCurrentObservationHistoryStoreVersion + 1; |
| auto store = MakeNewObservationHistoryStore(kFutureVersion); |
| ASSERT_EQ(kFutureVersion, store.version()); |
| EXPECT_EQ(kInvalidArguments, MaybeUpgradeObservationHistoryStore(&store)); |
| } |
| |
| // MaybeInsertReportConfig successfully updates the store. |
| TEST_F(AggregateStoreTest, MaybeInsertReportConfig) { |
| auto [metric, report] = GetUniqueActivesMetricAndReport(kTestCustomerId, kTestProjectId, |
| kTestMetricId, kTestReportId); |
| auto project_context = GetProjectContextFor(metric); |
| |
| EXPECT_FALSE(IsReportInStore(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId)); |
| ASSERT_EQ(kOK, GetAggregateStore()->MaybeInsertReportConfig(*project_context, metric, report)); |
| EXPECT_TRUE(IsReportInStore(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId)); |
| } |
| |
| // MaybeInsertReportConfig returns kOK when the same config is inserted twice. |
| TEST_F(AggregateStoreTest, MaybeInsertReportConfigTwice) { |
| auto [metric, report] = GetUniqueActivesMetricAndReport(kTestCustomerId, kTestProjectId, |
| kTestMetricId, kTestReportId); |
| auto project_context = GetProjectContextFor(metric); |
| |
| EXPECT_FALSE(IsReportInStore(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId)); |
| ASSERT_EQ(kOK, GetAggregateStore()->MaybeInsertReportConfig(*project_context, metric, report)); |
| ASSERT_EQ(kOK, GetAggregateStore()->MaybeInsertReportConfig(*project_context, metric, report)); |
| EXPECT_TRUE(IsReportInStore(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId)); |
| } |
| |
| // MaybeInsertReportConfig fails due to the fact that the given report does not have an aggregation |
| // window. |
| TEST_F(AggregateStoreTest, MaybeInsertReportConfigFail) { |
| auto [metric, report] = GetNotLocallyAggregatedMetricAndReport(kTestCustomerId, kTestProjectId, |
| kTestMetricId, kTestReportId); |
| auto project_context = GetProjectContextFor(metric); |
| |
| EXPECT_FALSE(IsReportInStore(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId)); |
| ASSERT_EQ(kInvalidArguments, |
| GetAggregateStore()->MaybeInsertReportConfig(*project_context, metric, report)); |
| } |
| |
| // The Aggregate store sets the record for an event to active using SetActive. |
| TEST_F(AggregateStoreTest, SetActive) { |
| auto [metric, report] = GetUniqueActivesMetricAndReport(kTestCustomerId, kTestProjectId, |
| kTestMetricId, kTestReportId); |
| auto project_context = GetProjectContextFor(metric); |
| |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| |
| EXPECT_FALSE(IsActive(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, |
| kTestEventCode, kTestDayIndex)); |
| ASSERT_EQ(kOK, GetAggregateStore()->SetActive(kTestCustomerId, kTestProjectId, kTestMetricId, |
| kTestReportId, kTestEventCode, kTestDayIndex)); |
| EXPECT_TRUE(IsActive(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, |
| kTestEventCode, kTestDayIndex)); |
| } |
| |
| // SetActive returns kOK when the same config is inserted twice. |
| TEST_F(AggregateStoreTest, SetActiveTwice) { |
| auto [metric, report] = GetUniqueActivesMetricAndReport(kTestCustomerId, kTestProjectId, |
| kTestMetricId, kTestReportId); |
| auto project_context = GetProjectContextFor(metric); |
| |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| EXPECT_FALSE(IsActive(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, |
| kTestEventCode, kTestDayIndex)); |
| |
| ASSERT_EQ(kOK, GetAggregateStore()->SetActive(kTestCustomerId, kTestProjectId, kTestMetricId, |
| kTestReportId, kTestEventCode, kTestDayIndex)); |
| ASSERT_EQ(kOK, GetAggregateStore()->SetActive(kTestCustomerId, kTestProjectId, kTestMetricId, |
| kTestReportId, kTestEventCode, kTestDayIndex)); |
| EXPECT_TRUE(IsActive(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, |
| kTestEventCode, kTestDayIndex)); |
| } |
| |
| // SetActive fails due to the fact that UpdateAggregationConfigs was not called for the given report |
| // does not have an aggregation. |
| TEST_F(AggregateStoreTest, SetActiveFail) { |
| auto [metric, report] = GetNotLocallyAggregatedMetricAndReport(kTestCustomerId, kTestProjectId, |
| kTestMetricId, kTestReportId); |
| auto project_context = GetProjectContextFor(metric); |
| |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| ASSERT_EQ(kInvalidArguments, |
| GetAggregateStore()->SetActive(kTestCustomerId, kTestProjectId, kTestMetricId, |
| kTestReportId, kTestEventCode, kTestDayIndex)); |
| } |
| |
| // The Aggregate store updates the local store with the sum of two values when the aggregation is of |
| // type SUM. |
| TEST_F(AggregateStoreTest, UpdateAggregationSUM) { |
| auto [metric, report] = GetPerDeviceNumericStatsMetricAndReport( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, ReportDefinition::SUM); |
| auto project_context = GetProjectContextFor(metric); |
| |
| const int64_t kFirstValue = 3; |
| const int64_t kSecondValue = 7; |
| |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| |
| EXPECT_EQ(kOK, GetAggregateStore()->UpdateNumericAggregate( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex, kFirstValue)); |
| EXPECT_EQ(kFirstValue, GetValue(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex)); |
| EXPECT_EQ(kOK, GetAggregateStore()->UpdateNumericAggregate( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex, kSecondValue)); |
| EXPECT_EQ(kSecondValue + kFirstValue, GetValue(kTestCustomerId, kTestProjectId, kTestMetricId, |
| kTestReportId, "", kTestEventCode, kTestDayIndex)); |
| } |
| |
| // The Aggregate store updates the local store with the sum of two values when the aggregation is of |
| // type MAX. |
| TEST_F(AggregateStoreTest, UpdateAggregationMAX) { |
| auto [metric, report] = GetPerDeviceNumericStatsMetricAndReport( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, ReportDefinition::MAX); |
| auto project_context = GetProjectContextFor(metric); |
| |
| const int64_t kFirstValue = 3; |
| const int64_t kSecondValue = 7; |
| |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| |
| EXPECT_EQ(kOK, GetAggregateStore()->UpdateNumericAggregate( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex, kFirstValue)); |
| EXPECT_EQ(kFirstValue, GetValue(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex)); |
| EXPECT_EQ(kOK, GetAggregateStore()->UpdateNumericAggregate( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex, kSecondValue)); |
| EXPECT_EQ(kSecondValue, GetValue(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, |
| "", kTestEventCode, kTestDayIndex)); |
| } |
| // The Aggregate store updates the local store with the sum of two values when the aggregation is of |
| // type MIN. |
| TEST_F(AggregateStoreTest, UpdateAggregationMIN) { |
| auto [metric, report] = GetPerDeviceNumericStatsMetricAndReport( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, ReportDefinition::MIN); |
| auto project_context = GetProjectContextFor(metric); |
| |
| const int64_t kFirstValue = 3; |
| const int64_t kSecondValue = 7; |
| |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| |
| EXPECT_EQ(kOK, GetAggregateStore()->UpdateNumericAggregate( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex, kFirstValue)); |
| EXPECT_EQ(kFirstValue, GetValue(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex)); |
| EXPECT_EQ(kOK, GetAggregateStore()->UpdateNumericAggregate( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex, kSecondValue)); |
| EXPECT_EQ(kFirstValue, GetValue(kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, "", |
| kTestEventCode, kTestDayIndex)); |
| } |
| |
| // UpdateAggregationConfigs fails due to the fact that UpdateAggregationConfigs was not called for |
| // the given report. |
| TEST_F(AggregateStoreTest, UpdateAggregationFail) { |
| auto [metric, report] = GetPerDeviceNumericStatsMetricAndReport( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, ReportDefinition::SUM); |
| auto project_context = GetProjectContextFor(metric); |
| |
| ASSERT_EQ(kInvalidArguments, GetAggregateStore()->UpdateNumericAggregate( |
| kTestCustomerId, kTestProjectId, kTestMetricId, kTestReportId, |
| "", kTestEventCode, kTestDayIndex, /*value*/ 4)); |
| } |
| |
| TEST_F(AggregateStoreTest, SetUniqueActivesLastGeneratedDayIndex) { |
| const std::string kReportKey = "test_key"; |
| const int64_t kFirstValue = 3; |
| |
| EXPECT_EQ(0u, |
| GetAggregateStore()->GetUniqueActivesLastGeneratedDayIndex(kReportKey, kTestEventCode, |
| /*aggregation_days*/ 1)); |
| GetAggregateStore()->SetUniqueActivesLastGeneratedDayIndex(kReportKey, kTestEventCode, |
| /*aggregation_days*/ 1, kFirstValue); |
| EXPECT_EQ(kFirstValue, |
| GetAggregateStore()->GetUniqueActivesLastGeneratedDayIndex(kReportKey, kTestEventCode, |
| /*aggregation_days*/ 1)); |
| } |
| TEST_F(AggregateStoreTest, SetPerDeviceNumericLastGeneratedDayIndex) { |
| const std::string kReportKey = "test_key"; |
| const int64_t kFirstValue = 3; |
| |
| EXPECT_EQ(0u, GetAggregateStore()->GetPerDeviceNumericLastGeneratedDayIndex( |
| kReportKey, "", kTestEventCode, |
| /*aggregation_days*/ 1)); |
| GetAggregateStore()->SetPerDeviceNumericLastGeneratedDayIndex(kReportKey, "", kTestEventCode, |
| /*aggregation_days*/ 1, |
| kFirstValue); |
| EXPECT_EQ(kFirstValue, GetAggregateStore()->GetPerDeviceNumericLastGeneratedDayIndex( |
| kReportKey, "", kTestEventCode, |
| /*aggregation_days*/ 1)); |
| } |
| TEST_F(AggregateStoreTest, SetReportParticipationLastGeneratedDayIndex) { |
| const std::string kReportKey = "test_key"; |
| const int64_t kFirstValue = 3; |
| |
| EXPECT_EQ(0u, GetAggregateStore()->GetReportParticipationLastGeneratedDayIndex(kReportKey)); |
| GetAggregateStore()->SetReportParticipationLastGeneratedDayIndex(kReportKey, kFirstValue); |
| EXPECT_EQ(kFirstValue, |
| GetAggregateStore()->GetReportParticipationLastGeneratedDayIndex(kReportKey)); |
| } |
| |
| // Tests that EventAggregator::GenerateObservations() returns a positive |
| // status and that the expected number of Observations is generated when no |
| // Events have been logged to the EventAggregator. |
| TEST_F(AggregateStoreTest, GenerateObservationsNoEvents) { |
| // Provide the all_report_types test registry to the EventAggregator. |
| auto project_context = GetTestProject(logger::testing::all_report_types::kCobaltRegistryBase64); |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| // Generate locally aggregated Observations for the current day index. |
| EXPECT_EQ(kOK, GenerateObservations(CurrentDayIndex())); |
| std::vector<Observation> observations(0); |
| EXPECT_TRUE(FetchAggregatedObservations( |
| &observations, logger::testing::all_report_types::kExpectedAggregationParams, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| |
| // Tests that EventAggregator::GenerateObservations() only generates |
| // Observations the first time it is called for a given day index. |
| TEST_F(AggregateStoreTest, GenerateObservationsTwice) { |
| // Provide the all_report_types test registry to the EventAggregator. |
| auto project_context = GetTestProject(logger::testing::all_report_types::kCobaltRegistryBase64); |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| // Check that Observations are generated when GenerateObservations is called |
| // for the current day index for the first time. |
| auto current_day_index = CurrentDayIndex(); |
| EXPECT_EQ(kOK, GenerateObservations(current_day_index)); |
| std::vector<Observation> observations(0); |
| EXPECT_TRUE(FetchAggregatedObservations( |
| &observations, logger::testing::all_report_types::kExpectedAggregationParams, |
| observation_store_.get(), update_recipient_.get())); |
| // Check that no Observations are generated when GenerateObservations is |
| // called for the currentday index for the second time. |
| ResetObservationStore(); |
| EXPECT_EQ(kOK, GenerateObservations(current_day_index)); |
| EXPECT_EQ(0u, observation_store_->messages_received.size()); |
| } |
| |
| // When the LocalAggregateStore contains one ReportAggregates proto and that |
| // proto is empty, GenerateObservations should return success but generate no |
| // observations. |
| TEST_F(AggregateStoreTest, GenerateObservationsFromBadStore) { |
| auto bad_store = std::make_unique<LocalAggregateStore>(); |
| (*bad_store->mutable_by_report_key())["some_key"] = ReportAggregates(); |
| auto stream = fs()->NewProtoOutputStream(aggregate_store_path()).ConsumeValueOrDie(); |
| EXPECT_TRUE(bad_store->SerializeToZeroCopyStream(stream.get())); |
| // Read the bad store in to the EventAggregator. |
| ResetEventAggregator(); |
| EXPECT_EQ(kOK, GenerateObservations(CurrentDayIndex())); |
| EXPECT_EQ(0u, observation_store_->messages_received.size()); |
| } |
| |
| // When the LocalAggregateStore contains one empty ReportAggregates proto and |
| // some valid ReportAggregates, GenerateObservations should produce observations |
| // for the valid ReportAggregates. |
| TEST_F(AggregateStoreTest, GenerateObservationsFromBadStoreMultiReport) { |
| auto bad_store = std::make_unique<LocalAggregateStore>(); |
| (*bad_store->mutable_by_report_key())["some_key"] = ReportAggregates(); |
| auto stream = fs()->NewProtoOutputStream(aggregate_store_path()).ConsumeValueOrDie(); |
| EXPECT_TRUE(bad_store->SerializeToZeroCopyStream(stream.get())); |
| // Read the bad store in to the EventAggregator. |
| ResetEventAggregator(); |
| // Provide the all_report_types test registry to the EventAggregator. |
| auto project_context = GetTestProject(logger::testing::all_report_types::kCobaltRegistryBase64); |
| EXPECT_EQ( |
| kOK, event_aggregator_mgr_->GetEventAggregator()->UpdateAggregationConfigs(*project_context)); |
| EXPECT_EQ(kOK, GenerateObservations(CurrentDayIndex())); |
| std::vector<Observation> observations(0); |
| EXPECT_TRUE(FetchAggregatedObservations( |
| &observations, logger::testing::all_report_types::kExpectedAggregationParams, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| |
| // When the LocalAggregateStore contains one ReportAggregates proto and that |
| // proto is empty, GarbageCollect should return success. |
| TEST_F(AggregateStoreTest, GarbageCollectBadStore) { |
| auto bad_store = std::make_unique<LocalAggregateStore>(); |
| (*bad_store->mutable_by_report_key())["some_key"] = ReportAggregates(); |
| auto stream = fs()->NewProtoOutputStream(aggregate_store_path()).ConsumeValueOrDie(); |
| EXPECT_TRUE(bad_store->SerializeToZeroCopyStream(stream.get())); |
| // Read the bad store in to the EventAggregator. |
| ResetEventAggregator(); |
| EXPECT_EQ(kOK, GarbageCollect(CurrentDayIndex())); |
| } |
| |
| // Tests GarbageCollect() for UniqueActivesReportAggregates. |
| // |
| // For each value of N in the range [0, 34], logs some UniqueActivesEvents |
| // each day for N consecutive days and then garbage-collects the |
| // LocalAggregateStore. After garbage collection, verifies the contents of |
| // the LocalAggregateStore. |
| TEST_F(UniqueActivesAggregateStoreTest, GarbageCollect) { |
| uint32_t max_days_before_gc = 35; |
| for (uint32_t days_before_gc = 0; days_before_gc < max_days_before_gc; days_before_gc++) { |
| SetUp(); |
| day_last_garbage_collected_ = 0u; |
| LoggedActivity logged_activity; |
| for (uint32_t offset = 0; offset < days_before_gc; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (const auto& metric_report_id : |
| logger::testing::unique_actives::kExpectedAggregationParams.metric_report_ids) { |
| // Add 2 events to the local aggregations with event code 0. |
| EXPECT_EQ(kOK, AddUniqueActivesEvent(metric_report_id, day_index, 0u, &logged_activity)); |
| EXPECT_EQ(kOK, AddUniqueActivesEvent(metric_report_id, day_index, 0u, &logged_activity)); |
| if (offset < 3) { |
| // Add 1 event to the local aggregations with event code 1. |
| EXPECT_EQ(kOK, AddUniqueActivesEvent(metric_report_id, day_index, 1u, &logged_activity)); |
| } |
| } |
| AdvanceClock(kDay); |
| } |
| auto end_day_index = CurrentDayIndex(); |
| EXPECT_EQ(kOK, GarbageCollect(end_day_index)); |
| day_last_garbage_collected_ = end_day_index; |
| EXPECT_TRUE(CheckUniqueActivesAggregates(logged_activity, end_day_index)); |
| TearDown(); |
| } |
| } |
| |
| // Tests that EventAggregator::GenerateObservations() returns a positive |
| // status and that the expected number of Observations is generated after |
| // some UniqueActivesEvents have been logged, without any garbage |
| // collection. |
| // |
| // For 35 days, logs 2 events each day for the NetworkActivity_UniqueDevices |
| // reports and 2 events for the FeaturesActive_UniqueDevices report, all |
| // with event code 0. |
| // |
| // Each day, calls GenerateObservations() with the day index of the previous |
| // day. Checks that a positive status is returned and that the |
| // FakeObservationStore has received the expected number of new observations |
| // for each locally aggregated report ID in the unique_actives registry. |
| TEST_F(UniqueActivesAggregateStoreTest, GenerateObservations) { |
| int num_days = 35; |
| std::vector<Observation> observations(0); |
| for (int offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| observations.clear(); |
| ResetObservationStore(); |
| EXPECT_EQ(kOK, GenerateObservations(day_index - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations( |
| &observations, logger::testing::unique_actives::kExpectedAggregationParams, |
| observation_store_.get(), update_recipient_.get())); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityWindowSizeMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityAggregationWindowMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent(logger::testing::unique_actives::kFeaturesActiveMetricReportId, |
| day_index, 0u)); |
| } |
| AdvanceClock(kDay); |
| } |
| observations.clear(); |
| ResetObservationStore(); |
| EXPECT_EQ(kOK, GenerateObservations(CurrentDayIndex() - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations( |
| &observations, logger::testing::unique_actives::kExpectedAggregationParams, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| |
| // Tests that GenerateObservations() returns a positive status and that the |
| // expected number of Observations is generated each day when Events are |
| // logged for UNIQUE_N_DAY_ACTIVES reports over multiple days, and when the |
| // LocalAggregateStore is garbage-collected each day. |
| // |
| // For 35 days, logs 2 events each day for the NetworkActivity_UniqueDevices |
| // reports and 2 events for the FeaturesActive_UniqueDevices report, all |
| // with event code 0. |
| // |
| // Each day following the first day, calls GenerateObservations() and then |
| // GarbageCollect() with the day index of the current day. Checks that |
| // positive statuses are returned and that the FakeObservationStore has |
| // received the expected number of new observations for each locally |
| // aggregated report ID in the unique_actives registry. |
| TEST_F(UniqueActivesAggregateStoreTest, GenerateObservationsWithGc) { |
| int num_days = 35; |
| std::vector<Observation> observations(0); |
| for (int offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| observations.clear(); |
| ResetObservationStore(); |
| EXPECT_EQ(kOK, GenerateObservations(day_index - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations( |
| &observations, logger::testing::unique_actives::kExpectedAggregationParams, |
| observation_store_.get(), update_recipient_.get())); |
| EXPECT_EQ(kOK, GarbageCollect(day_index)); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityWindowSizeMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityAggregationWindowMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent(logger::testing::unique_actives::kFeaturesActiveMetricReportId, |
| day_index, 0u)); |
| } |
| AdvanceClock(kDay); |
| } |
| observations.clear(); |
| ResetObservationStore(); |
| auto day_index = CurrentDayIndex(); |
| EXPECT_EQ(kOK, GenerateObservations(day_index - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations( |
| &observations, logger::testing::unique_actives::kExpectedAggregationParams, |
| observation_store_.get(), update_recipient_.get())); |
| EXPECT_EQ(kOK, GarbageCollect(day_index)); |
| } |
| |
| // Tests that GenerateObservations() returns a positive status and that the |
| // expected number of Observations is generated when events are logged over |
| // multiple days and some of those days' Observations are backfilled, without |
| // any garbage collection of the LocalAggregateStore. |
| // |
| // Sets the |backfill_days_| field of the EventAggregator to 3. |
| // |
| // Logging pattern: |
| // For 35 days, logs 2 events each day for the |
| // NetworkActivity_UniqueDevices reports and 2 events for the |
| // FeaturesActive_UniqueDevices report, all with event code 0. |
| // |
| // Observation generation pattern: |
| // Calls GenerateObservations() on the 1st through 5th and the 7th out of |
| // every 10 days, for 35 days. |
| // |
| // Expected numbers of Observations: |
| // It is expected that 4 days' worth of Observations are generated on |
| // the first day of every 10 (the day index for which GenerateObservations() |
| // was called, plus 3 days of backfill), that 1 day's worth of Observations |
| // are generated on the 2nd through 5th day of every 10, that 2 days' |
| // worth of Observations are generated on the 7th day of every 10 (the |
| // day index for which GenerateObservations() was called, plus 1 day of |
| // backfill), and that no Observations are generated on the remaining days. |
| TEST_F(UniqueActivesAggregateStoreTest, GenerateObservationsWithBackfill) { |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| // Adds 2 events to the local aggregations each day for 35 days. Call GenerateObservations() on |
| // the first 5 day indices, and the 7th, out of every 10. |
| for (int offset = 0; offset < 35; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityWindowSizeMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityAggregationWindowMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent(logger::testing::unique_actives::kFeaturesActiveMetricReportId, |
| day_index, 0u)); |
| } |
| observation_store_->ResetObservationCounter(); |
| if (offset % 10 < 5 || offset % 10 == 6) { |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| } |
| auto num_new_obs = observation_store_->num_observations_added(); |
| EXPECT_GE(num_new_obs, 0u); |
| // Check that the expected daily number of Observations was generated. |
| switch (offset % 10) { |
| case 0: |
| EXPECT_EQ(logger::testing::unique_actives::kExpectedAggregationParams.daily_num_obs * |
| (backfill_days + 1), |
| num_new_obs); |
| break; |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| EXPECT_EQ(logger::testing::unique_actives::kExpectedAggregationParams.daily_num_obs, |
| num_new_obs); |
| break; |
| case 6: |
| EXPECT_EQ(logger::testing::unique_actives::kExpectedAggregationParams.daily_num_obs * 2, |
| num_new_obs); |
| break; |
| default: |
| EXPECT_EQ(0u, num_new_obs); |
| } |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Tests that GenerateObservations() returns a positive status and that the |
| // expected number of Observations is generated when events are logged over |
| // multiple days and some of those days' Observations are backfilled, and when |
| // the LocalAggregateStore is garbage-collected after each call to |
| // GenerateObservations(). |
| // |
| // Sets the |backfill_days_| field of the EventAggregator to 3. |
| // |
| // Logging pattern: |
| // For 35 days, logs 2 events each day for the |
| // NetworkActivity_UniqueDevices reports and 2 events for the |
| // FeaturesActive_Unique_Devices report, all with event code 0. |
| // |
| // Observation generation pattern: |
| // Calls GenerateObservations() on the 1st through 5th and the 7th out of |
| // every 10 days, for 35 days. Garbage-collects the LocalAggregateStore after |
| // each call. |
| // |
| // Expected numbers of Observations: |
| // It is expected that 4 days' worth of Observations are generated on |
| // the first day of every 10 (the day index for which GenerateObservations() |
| // was called, plus 3 days of backfill), that 1 day's worth of Observations |
| // are generated on the 2nd through 5th day of every 10, that 2 days' |
| // worth of Observations are generated on the 7th day of every 10 (the |
| // day index for which GenerateObservations() was called, plus 1 day of |
| // backfill), and that no Observations are generated on the remaining days. |
| TEST_F(UniqueActivesAggregateStoreTest, GenerateObservationsWithBackfillAndGc) { |
| int num_days = 35; |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| // Adds 2 events to the local aggregations each day for 35 days. Call GenerateObservations() on |
| // the first 5 day indices, and the 7th, out of every 10. |
| for (int offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityWindowSizeMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives::kNetworkActivityAggregationWindowMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ( |
| kOK, AddUniqueActivesEvent(logger::testing::unique_actives::kFeaturesActiveMetricReportId, |
| day_index, 0u)); |
| } |
| observation_store_->ResetObservationCounter(); |
| if (offset % 10 < 5 || offset % 10 == 6) { |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| EXPECT_EQ(kOK, GarbageCollect(day_index)); |
| } |
| auto num_new_obs = observation_store_->num_observations_added(); |
| EXPECT_GE(num_new_obs, 0u); |
| // Check that the expected daily number of Observations was generated. |
| // This expected number is some multiple of the daily_num_obs field of |
| // |kUniqueActivesExpectedParams|, depending on the number of days which |
| // should have been backfilled when GenerateObservations() was called. |
| switch (offset % 10) { |
| case 0: |
| EXPECT_EQ(logger::testing::unique_actives::kExpectedAggregationParams.daily_num_obs * |
| (backfill_days + 1), |
| num_new_obs); |
| break; |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| EXPECT_EQ(logger::testing::unique_actives::kExpectedAggregationParams.daily_num_obs, |
| num_new_obs); |
| break; |
| case 6: |
| EXPECT_EQ(logger::testing::unique_actives::kExpectedAggregationParams.daily_num_obs * 2, |
| num_new_obs); |
| break; |
| default: |
| EXPECT_EQ(0u, num_new_obs); |
| } |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Checks that UniqueActivesObservations with the expected values (i.e., |
| // non-active for all UNIQUE_N_DAY_ACTIVES reports, for all window sizes and |
| // event codes) are generated when no Events have been logged to the |
| // EventAggregator. |
| TEST_F(UniqueActivesNoiseFreeAggregateStoreTest, CheckObservationValuesNoEvents) { |
| auto current_day_index = CurrentDayIndex(); |
| EXPECT_EQ(kOK, GenerateObservations(current_day_index)); |
| auto expected_obs = MakeNullExpectedUniqueActivesObservations( |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams, current_day_index); |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| } |
| |
| // Checks that UniqueActivesObservations with the expected values are |
| // generated when GenerateObservations() is called for a single day index |
| // after logging some events for UNIQUE_N_DAY_ACTIVES reports for that day |
| // index, without any garbage collection or backfill. |
| // |
| // Logging pattern: |
| // Logs 2 occurrences of event code 0 for the FeaturesActives_UniqueDevices |
| // report, and 1 occurrence of event code 1 for the |
| // EventsOccurred_UniqueDevices report, all on the same day. |
| // |
| // Observation generation pattern: |
| // Calls GenerateObservations() after logging all events. |
| // |
| // Expected numbers of Observations: |
| // The expected number of Observations is the daily_num_obs field of |
| // |logger::testing::unique_actives_noise_free::kExpectedAggregationParams|. |
| // |
| // Expected Observation values: |
| // All Observations should be labeled with the day index on which the events |
| // were logged. |
| // |
| // For the FeaturesActive_UniqueDevices report, expect activity indicators: |
| // |
| // window size active for event codes |
| // ------------------------------------------ |
| // 1 0 |
| // 7 0 |
| // 30 0 |
| // |
| // For the EventsOccurred_UniqueDevices report, expected activity indicators: |
| // window size active for event codes |
| // ------------------------------------------ |
| // 1 1 |
| // 7 1 |
| // |
| // All other Observations should be of inactivity. |
| TEST_F(UniqueActivesNoiseFreeAggregateStoreTest, CheckObservationValuesSingleDay) { |
| auto day_index = CurrentDayIndex(); |
| // Adds several events to the local aggregations on |day_index|. |
| EXPECT_EQ(kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives_noise_free::kFeaturesActiveMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ(kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives_noise_free::kFeaturesActiveMetricReportId, |
| day_index, 0u)); |
| EXPECT_EQ(kOK, AddUniqueActivesEvent( |
| logger::testing::unique_actives_noise_free::kEventsOccurredMetricReportId, |
| day_index, 1u)); |
| // Generate locally aggregated Observations for |day_index|. |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| |
| // Form the expected observations. |
| auto expected_obs = MakeNullExpectedUniqueActivesObservations( |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams, day_index); |
| expected_obs[{logger::testing::unique_actives_noise_free::kFeaturesActiveMetricReportId, |
| day_index}] = {{1, {true, false, false, false, false}}, |
| {7, {true, false, false, false, false}}, |
| {28, {true, false, false, false, false}}, |
| {30, {true, false, false, false, false}}}; |
| expected_obs[{logger::testing::unique_actives_noise_free::kEventsOccurredMetricReportId, |
| day_index}] = {{1, {false, true, false, false, false}}, |
| {7, {false, true, false, false, false}}}; |
| |
| // Check the contents of the FakeObservationStore. |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| } |
| |
| // Checks that UniqueActivesObservations with the expected values are |
| // generated when some events have been logged for a UNIQUE_N_DAY_ACTIVES |
| // report over multiple days and GenerateObservations() is called each |
| // day, without garbage collection or backfill. |
| // |
| // Logging pattern: |
| // Logs events for the EventsOccurred_UniqueDevices report (whose parent |
| // metric has max_event_code = 4) for 10 days, according to the following |
| // pattern: |
| // |
| // * Never log event code 0. |
| // * On the i-th day (0-indexed) of logging, log an event for event code k, |
| // 1 <= k < 5, if 3*k divides i. |
| // |
| // Observation generation pattern: |
| // Each day following the first day, generates Observations for the previous |
| // day index. |
| // |
| // Expected number of Observations: |
| // Each call to GenerateObservations should generate a number of Observations |
| // equal to the daily_num_obs field of |
| // |testing::unique_actives_noise_free::kExpectedAggregationParams|. |
| // |
| // Expected Observation values: |
| // The EventsOccurred_UniqueDevices report has window sizes 1 and 7, and |
| // the expected activity indicators of Observations for that report on the |
| // i-th day are: |
| // |
| // (i, window size) active for event codes |
| // ------------------------------------------------------ |
| // (0, 1) 1, 2, 3, 4 |
| // (0, 7) 1, 2, 3, 4 |
| // (1, 1) --- |
| // (1, 7) 1, 2, 3, 4 |
| // (2, 1) --- |
| // (2, 7) 1, 2, 3, 4 |
| // (3, 1) 1 |
| // (3, 7) 1, 2, 3, 4 |
| // (4, 1) --- |
| // (4, 7) 1, 2, 3, 4 |
| // (5, 1) --- |
| // (5, 7) 1, 2, 3, 4 |
| // (6, 1) 1, 2 |
| // (6, 7) 1, 2, 3, 4 |
| // (7, 1) --- |
| // (7, 7) 1, 2 |
| // (8, 1) --- |
| // (8, 7) 1, 2 |
| // (9, 1) 1, 3 |
| // (9, 7) 1, 2, 3 |
| // |
| // All Observations for all other locally aggregated reports should be |
| // observations of non-occurrence. |
| TEST_F(UniqueActivesNoiseFreeAggregateStoreTest, CheckObservationValuesMultiDay) { |
| auto start_day_index = CurrentDayIndex(); |
| // Form expected Obsevations for the 10 days of logging. |
| uint32_t num_days = 10; |
| std::vector<ExpectedUniqueActivesObservations> expected_obs(num_days); |
| const auto& expected_id = |
| logger::testing::unique_actives_noise_free::kEventsOccurredMetricReportId; |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| expected_obs[offset] = MakeNullExpectedUniqueActivesObservations( |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams, |
| start_day_index + offset); |
| } |
| expected_obs[0][{expected_id, start_day_index}] = {{1, {false, true, true, true, true}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[1][{expected_id, start_day_index + 1}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[2][{expected_id, start_day_index + 2}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[3][{expected_id, start_day_index + 3}] = {{1, {false, true, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[4][{expected_id, start_day_index + 4}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[5][{expected_id, start_day_index + 5}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[6][{expected_id, start_day_index + 6}] = {{1, {false, true, true, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[7][{expected_id, start_day_index + 7}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, false, false}}}; |
| expected_obs[8][{expected_id, start_day_index + 8}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, false, false}}}; |
| expected_obs[9][{expected_id, start_day_index + 9}] = {{1, {false, true, false, true, false}}, |
| {7, {false, true, true, true, false}}}; |
| |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (uint32_t event_code = 1; |
| event_code < |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams.num_event_codes.at( |
| expected_id); |
| event_code++) { |
| if (offset % (3 * event_code) == 0) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent(expected_id, day_index, event_code)); |
| } |
| } |
| // Clear the FakeObservationStore. |
| ResetObservationStore(); |
| // Generate locally aggregated Observations. |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| // Check the generated Observations against the expectation. |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[offset], observation_store_.get(), |
| update_recipient_.get())); |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Checks that UniqueActivesObservations with the expected values are |
| // generated when some events have been logged for a UNIQUE_N_DAY_ACTIVES |
| // report over multiple days and GenerateObservations() is called each |
| // day, and when the LocalAggregateStore is garbage-collected after each call |
| // to GenerateObservations(). |
| // |
| // Logging pattern: |
| // Logs events for the EventsOccurred_UniqueDevices report (whose parent |
| // metric has max_event_code = 4) for 10 days, according to the following |
| // pattern: |
| // |
| // * Never log event code 0. |
| // * On the i-th day (0-indexed) of logging, log an event for event code k, |
| // 1 <= k < 5, if 3*k divides i. |
| // |
| // Observation generation pattern: |
| // Each day following the first day, generates Observations for the previous |
| // day index. |
| // |
| // Expected number of Observations: |
| // Each call to GenerateObservations should generate a number of Observations |
| // equal to the daily_num_obs field of |
| // |logger::testing::unique_actives_noise_free::kExpectedAggregationParams|. |
| // |
| // Expected Observation values: |
| // The EventsOccurred_UniqueDevices report has window sizes 1 and 7, and |
| // the expected activity indicators of Observations for that report on the |
| // i-th day are: |
| // |
| // (i, window size) active for event codes |
| // ------------------------------------------------------ |
| // (0, 1) 1, 2, 3, 4 |
| // (0, 7) 1, 2, 3, 4 |
| // (1, 1) --- |
| // (1, 7) 1, 2, 3, 4 |
| // (2, 1) --- |
| // (2, 7) 1, 2, 3, 4 |
| // (3, 1) 1 |
| // (3, 7) 1, 2, 3, 4 |
| // (4, 1) --- |
| // (4, 7) 1, 2, 3, 4 |
| // (5, 1) --- |
| // (5, 7) 1, 2, 3, 4 |
| // (6, 1) 1, 2 |
| // (6, 7) 1, 2, 3, 4 |
| // (7, 1) --- |
| // (7, 7) 1, 2 |
| // (8, 1) --- |
| // (8, 7) 1, 2 |
| // (9, 1) 1, 3 |
| // (9, 7) 1, 2, 3 |
| // |
| // All Observations for all other locally aggregated reports should be |
| // observations of non-occurrence. |
| TEST_F(UniqueActivesNoiseFreeAggregateStoreTest, |
| CheckObservationValuesMultiDayWithGarbageCollection) { |
| auto start_day_index = CurrentDayIndex(); |
| // Form expected Observations for the 10 days of logging. |
| uint32_t num_days = 10; |
| std::vector<ExpectedUniqueActivesObservations> expected_obs(num_days); |
| const auto& expected_id = |
| logger::testing::unique_actives_noise_free::kEventsOccurredMetricReportId; |
| |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| expected_obs[offset] = MakeNullExpectedUniqueActivesObservations( |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams, |
| start_day_index + offset); |
| } |
| expected_obs[0][{expected_id, start_day_index}] = {{1, {false, true, true, true, true}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[1][{expected_id, start_day_index + 1}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[2][{expected_id, start_day_index + 2}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[3][{expected_id, start_day_index + 3}] = {{1, {false, true, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[4][{expected_id, start_day_index + 4}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[5][{expected_id, start_day_index + 5}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[6][{expected_id, start_day_index + 6}] = {{1, {false, true, true, false, false}}, |
| {7, {false, true, true, true, true}}}; |
| expected_obs[7][{expected_id, start_day_index + 7}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, false, false}}}; |
| expected_obs[8][{expected_id, start_day_index + 8}] = {{1, {false, false, false, false, false}}, |
| {7, {false, true, true, false, false}}}; |
| expected_obs[9][{expected_id, start_day_index + 9}] = {{1, {false, true, false, true, false}}, |
| {7, {false, true, true, true, false}}}; |
| |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (uint32_t event_code = 1; |
| event_code < |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams.num_event_codes.at( |
| expected_id); |
| event_code++) { |
| if (offset % (3 * event_code) == 0) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent(expected_id, day_index, event_code)); |
| } |
| } |
| // Advance |test_clock_| by 1 day. |
| AdvanceClock(kDay); |
| // Clear the FakeObservationStore. |
| ResetObservationStore(); |
| // Generate locally aggregated Observations and garbage-collect the |
| // LocalAggregateStore, both for the previous day as measured by |
| // |test_clock_|. Back up the LocalAggregateStore and |
| // AggregatedObservationHistoryStore. |
| DoScheduledTasksNow(); |
| // Check the generated Observations against the expectation. |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[offset], observation_store_.get(), |
| update_recipient_.get())); |
| } |
| } |
| |
| // Tests that the expected UniqueActivesObservations are generated when events |
| // are logged over multiple days and when Observations are backfilled for some |
| // days during that period, without any garbage-collection of the |
| // LocalAggregateStore. |
| // |
| // The test sets the number of backfill days to 3. |
| // |
| // Logging pattern: |
| // Events for the EventsOccurred_UniqueDevices report are logged over the days |
| // |start_day_index| to |start_day_index + 8| according to the following |
| // pattern: |
| // |
| // * For i = 0 to i = 4, log an event with event code i on day |
| // |start_day_index + i| and |start_day_index + 2*i|. |
| // |
| // Observation generation pattern: |
| // The test calls GenerateObservations() on day |start_day_index + i| for i = |
| // 0 through i = 5 and for i = 8, skipping the days |start_day_index + 6| and |
| // |start_day_index + 7|. |
| // |
| // Expected numbers of Observations: |
| // It is expected that 4 days' worth of Observations are generated on the |
| // first day (the day index for which GenerateObservations() was called, plus |
| // 3 days of backfill), that 1 day's worth of Observations is generated on the |
| // 2nd through 6th day, that 3 days' worth of Observations are generated on |
| // the 9th day (the day index for which GenerateObservations() was called, |
| // plus 2 days of backfill), and that no Observations are generated on the |
| // remaining days. |
| // |
| // Expected Observation values: |
| // The expected activity indicators of Observations for the |
| // EventsOccurred_UniqueDevices report for the i-th day of logging are: |
| // |
| // (i, window size) active for event codes |
| // ------------------------------------------------------------------------- |
| // (0, 1) 0 |
| // (0, 7) 0 |
| // (1, 1) 1 |
| // (1, 7) 0, 1 |
| // (2, 1) 1, 2 |
| // (2, 7) 0, 1, 2 |
| // (3, 1) 3 |
| // (3, 7) 0, 1, 2, 3 |
| // (4, 1) 2, 4 |
| // (4, 7) 0, 1, 2, 3, 4 |
| // (5, 1) --- |
| // (5, 7) 0, 1, 2, 3, 4 |
| // (6, 1) 3 |
| // (6, 7) 0, 1, 2, 3, 4 |
| // (7, 1) --- |
| // (7, 7) 1, 2, 3, 4 |
| // (8, 1) 4 |
| // (8, 7) 1, 2, 3, 4 |
| // |
| // All other Observations should be of non-activity. |
| TEST_F(UniqueActivesNoiseFreeAggregateStoreTest, CheckObservationValuesWithBackfill) { |
| auto start_day_index = CurrentDayIndex(); |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| const auto& expected_id = |
| logger::testing::unique_actives_noise_free::kEventsOccurredMetricReportId; |
| const auto& expected_params = |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams; |
| // Add eventsto the local aggregations for 9 days. Call GenerateObservations() on the first 6 |
| // day indices, and the 9th. |
| for (uint32_t offset = 0; offset < 9; offset++) { |
| auto day_index = CurrentDayIndex(); |
| ResetObservationStore(); |
| for (uint32_t event_code = 0; event_code < expected_params.num_event_codes.at(expected_id); |
| event_code++) { |
| if (event_code == offset || (2 * event_code) == offset) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent(expected_id, day_index, event_code)); |
| } |
| } |
| if (offset < 6 || offset == 8) { |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| } |
| // Make the set of Observations which are expected to be generated on |
| // |start_day_index + offset| and check it against the contents of the |
| // FakeObservationStore. |
| ExpectedUniqueActivesObservations expected_obs; |
| switch (offset) { |
| case 0: { |
| for (uint32_t day_index = start_day_index - backfill_days; day_index <= start_day_index; |
| day_index++) { |
| for (const auto& pair : |
| MakeNullExpectedUniqueActivesObservations(expected_params, day_index)) { |
| expected_obs.insert(pair); |
| } |
| } |
| expected_obs[{expected_id, start_day_index}] = {{1, {true, false, false, false, false}}, |
| {7, {true, false, false, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 1: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 1); |
| expected_obs[{expected_id, start_day_index + 1}] = {{1, {false, true, false, false, false}}, |
| {7, {true, true, false, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 2: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 2); |
| expected_obs[{expected_id, start_day_index + 2}] = {{1, {false, true, true, false, false}}, |
| {7, {true, true, true, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 3: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 3); |
| expected_obs[{expected_id, start_day_index + 3}] = {{1, {false, false, false, true, false}}, |
| {7, {true, true, true, true, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 4: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 4); |
| expected_obs[{expected_id, start_day_index + 4}] = {{1, {false, false, true, false, true}}, |
| {7, {true, true, true, true, true}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 5: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 5); |
| expected_obs[{expected_id, start_day_index + 5}] = { |
| {1, {false, false, false, false, false}}, {7, {true, true, true, true, true}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 8: { |
| for (uint32_t day_index = start_day_index + 6; day_index <= start_day_index + 8; |
| day_index++) { |
| for (const auto& pair : |
| MakeNullExpectedUniqueActivesObservations(expected_params, day_index)) { |
| expected_obs.insert(pair); |
| } |
| } |
| expected_obs[{expected_id, start_day_index + 6}] = {{1, {false, false, false, true, false}}, |
| {7, {true, true, true, true, true}}}; |
| expected_obs[{expected_id, start_day_index + 7}] = { |
| {1, {false, false, false, false, false}}, {7, {false, true, true, true, true}}}; |
| expected_obs[{expected_id, start_day_index + 8}] = {{1, {false, false, false, false, true}}, |
| {7, {false, true, true, true, true}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| default: |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| } |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Tests that the expected UniqueActivesObservations are generated when events |
| // are logged over multiple days and when Observations are backfilled for some |
| // days during that period, and when the LocalAggregateStore is |
| // garbage-collected after each call to GenerateObservations(). |
| // |
| // The test sets the number of backfill days to 3. |
| // |
| // Logging pattern: |
| // Events for the EventsOccurred_UniqueDevices report are logged over the days |
| // |start_day_index| to |start_day_index + 8| according to the following |
| // pattern: |
| // |
| // * For i = 0 to i = 4, log an event with event code i on day |
| // |start_day_index + i| and |start_day_index + 2*i|. |
| // |
| // Observation generation pattern: |
| // The test calls GenerateObservations() on day |start_day_index + i| for i = |
| // 0 through i = 5 and for i = 8, skipping the days |start_day_index + 6| and |
| // |start_day_index + 7|. |
| // |
| // Expected numbers of Observations: |
| // It is expected that 4 days' worth of Observations are generated on the |
| // first day (the day index for which GenerateObservations() was called, plus |
| // 3 days of backfill), that 1 day's worth of Observations is generated on the |
| // 2nd through 6th day, that 3 days' worth of Observations are generated on |
| // the 9th day (the day index for which GenerateObservations() was called, |
| // plus 2 days of backfill), and that no Observations are generated on the |
| // remaining days. |
| // |
| // Expected Observation values: |
| // The expected activity indicators of Observations for the |
| // EventsOccurred_UniqueDevices report for the i-th day of logging are: |
| // |
| // (i, window size) active for event codes |
| // ------------------------------------------------------------------------- |
| // (0, 1) 0 |
| // (0, 7) 0 |
| // (1, 1) 1 |
| // (1, 7) 0, 1 |
| // (2, 1) 1, 2 |
| // (2, 7) 0, 1, 2 |
| // (3, 1) 3 |
| // (3, 7) 0, 1, 2, 3 |
| // (4, 1) 2, 4 |
| // (4, 7) 0, 1, 2, 3, 4 |
| // (5, 1) --- |
| // (5, 7) 0, 1, 2, 3, 4 |
| // (6, 1) 3 |
| // (6, 7) 0, 1, 2, 3, 4 |
| // (7, 1) --- |
| // (7, 7) 1, 2, 3, 4 |
| // (8, 1) 4 |
| // (8, 7) 1, 2, 3, 4 |
| // |
| // All other Observations should be of non-activity. |
| TEST_F(UniqueActivesNoiseFreeAggregateStoreTest, CheckObservationValuesWithBackfillAndGc) { |
| auto start_day_index = CurrentDayIndex(); |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| |
| const auto& expected_id = |
| logger::testing::unique_actives_noise_free::kEventsOccurredMetricReportId; |
| const auto& expected_params = |
| logger::testing::unique_actives_noise_free::kExpectedAggregationParams; |
| |
| // Add events to the local aggregations for 9 days. Call GenerateObservations() on the first 6 |
| // day indices, and the 9th. |
| for (uint32_t offset = 0; offset < 8; offset++) { |
| auto day_index = CurrentDayIndex(); |
| ResetObservationStore(); |
| for (uint32_t event_code = 0; event_code < expected_params.num_event_codes.at(expected_id); |
| event_code++) { |
| if (event_code == offset || (2 * event_code) == offset) { |
| EXPECT_EQ(kOK, AddUniqueActivesEvent(expected_id, day_index, event_code)); |
| } |
| } |
| // Advance |test_clock_| by 1 day. |
| AdvanceClock(kDay); |
| if (offset < 6 || offset == 9) { |
| // Generate Observations and garbage-collect, both for the previous day |
| // index according to |test_clock_|. Back up the LocalAggregateStore and |
| // the AggregatedObservationHistoryStore. |
| DoScheduledTasksNow(); |
| } |
| // Make the set of Observations which are expected to be generated on |
| // |start_day_index + offset| and check it against the contents of the |
| // FakeObservationStore. |
| ExpectedUniqueActivesObservations expected_obs; |
| switch (offset) { |
| case 0: { |
| for (uint32_t day_index = start_day_index - backfill_days; day_index <= start_day_index; |
| day_index++) { |
| for (const auto& pair : |
| MakeNullExpectedUniqueActivesObservations(expected_params, day_index)) { |
| expected_obs.insert(pair); |
| } |
| } |
| expected_obs[{expected_id, start_day_index}] = {{1, {true, false, false, false, false}}, |
| {7, {true, false, false, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 1: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 1); |
| expected_obs[{expected_id, start_day_index + 1}] = {{1, {false, true, false, false, false}}, |
| {7, {true, true, false, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 2: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 2); |
| expected_obs[{expected_id, start_day_index + 2}] = {{1, {false, true, true, false, false}}, |
| {7, {true, true, true, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 3: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 3); |
| expected_obs[{expected_id, start_day_index + 3}] = {{1, {false, false, false, true, false}}, |
| {7, {true, true, true, true, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 4: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 4); |
| expected_obs[{expected_id, start_day_index + 4}] = {{1, {false, false, true, false, true}}, |
| {7, {true, true, true, true, true}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 5: { |
| expected_obs = |
| MakeNullExpectedUniqueActivesObservations(expected_params, start_day_index + 5); |
| expected_obs[{expected_id, start_day_index + 5}] = { |
| {1, {false, false, false, false, false}}, {7, {true, true, true, true, true}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| case 8: { |
| for (uint32_t day_index = start_day_index + 6; day_index <= start_day_index + 8; |
| day_index++) { |
| for (const auto& pair : |
| MakeNullExpectedUniqueActivesObservations(expected_params, day_index)) { |
| expected_obs.insert(pair); |
| } |
| } |
| expected_obs[{expected_id, start_day_index + 6}] = {{1, {false, false, false, true, false}}, |
| {7, {true, true, true, true, true}}}; |
| expected_obs[{expected_id, start_day_index + 7}] = { |
| {1, {false, false, false, false, false}}, {7, {false, true, true, true, true}}}; |
| expected_obs[{expected_id, start_day_index + 8}] = {{1, {false, false, false, false, true}}, |
| {7, {false, true, true, true, true}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| break; |
| } |
| default: |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs, observation_store_.get(), |
| update_recipient_.get())); |
| } |
| } |
| } |
| |
| // Tests GarbageCollect() for PerDeviceNumericReportAggregates. |
| // |
| // For each value of N in the range [0, 34], logs some events for |
| // PerDeviceNumeric reports each day for N consecutive days, and then |
| // garbage-collects the LocalAggregateStore. After garbage collection, verifies |
| // the contents of the LocalAggregateStore. |
| TEST_F(PerDeviceNumericAggregateStoreTest, GarbageCollect) { |
| uint32_t max_days_before_gc = 35; |
| for (uint32_t days_before_gc = 0; days_before_gc < max_days_before_gc; days_before_gc++) { |
| SetUp(); |
| day_last_garbage_collected_ = 0u; |
| LoggedValues logged_values; |
| std::vector<MetricReportId> count_metric_report_ids = { |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| logger::testing::per_device_numeric_stats::kSettingsChangedAggregationWindowMetricReportId, |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId}; |
| std::vector<MetricReportId> elapsed_time_metric_report_ids = { |
| logger::testing::per_device_numeric_stats::kStreamingTimeTotalMetricReportId, |
| logger::testing::per_device_numeric_stats::kStreamingTimeMinMetricReportId, |
| logger::testing::per_device_numeric_stats::kStreamingTimeMaxMetricReportId}; |
| MetricReportId frame_rate_metric_report_id = |
| logger::testing::per_device_numeric_stats::kLoginModuleFrameRateMinMetricReportId; |
| MetricReportId memory_usage_metric_report_id = |
| logger::testing::per_device_numeric_stats::kLedgerMemoryUsageMaxMetricReportId; |
| for (uint32_t offset = 0; offset < days_before_gc; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (const auto& id : count_metric_report_ids) { |
| for (const auto& component : {"component_A", "component_B", "component_C"}) { |
| // Adds 2 events to the local aggregations with event code 0, for each component A, B, |
| // C. |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent(id, day_index, component, 0u, 2, &logged_values)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent(id, day_index, component, 0u, 3, &logged_values)); |
| } |
| if (offset < 3) { |
| // Adds 1 event to the local aggregations for component D and event code 1. |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(id, day_index, "component_D", 1u, 4, |
| &logged_values)); |
| } |
| } |
| for (const auto& id : elapsed_time_metric_report_ids) { |
| for (const auto& component : {"component_A", "component_B", "component_C"}) { |
| // Add 2 events to the local aggregations with event code 0, for each component A, B, C. |
| EXPECT_EQ(kOK, |
| AddPerDeviceElapsedTimeEvent(id, day_index, component, 0u, 2, &logged_values)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceElapsedTimeEvent(id, day_index, component, 0u, 3, &logged_values)); |
| } |
| if (offset < 3) { |
| // Add 1 event to the local aggregations for component D and event code 1. |
| EXPECT_EQ(kOK, AddPerDeviceElapsedTimeEvent(id, day_index, "component_D", 1u, 4, |
| &logged_values)); |
| } |
| } |
| for (const auto& component : {"component_A", "component_B"}) { |
| EXPECT_EQ(kOK, AddPerDeviceFrameRateEvent(frame_rate_metric_report_id, day_index, component, |
| 0u, 2.25, &logged_values)); |
| EXPECT_EQ(kOK, AddPerDeviceFrameRateEvent(frame_rate_metric_report_id, day_index, component, |
| 0u, 1.75, &logged_values)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceMemoryUsageEvent(memory_usage_metric_report_id, day_index, component, |
| std::vector<uint32_t>{0u, 0u}, 300, &logged_values)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceMemoryUsageEvent(memory_usage_metric_report_id, day_index, component, |
| std::vector<uint32_t>{1u, 0u}, 300, &logged_values)); |
| } |
| AdvanceClock(kDay); |
| } |
| auto end_day_index = CurrentDayIndex(); |
| EXPECT_EQ(kOK, GarbageCollect(end_day_index)); |
| day_last_garbage_collected_ = end_day_index; |
| EXPECT_TRUE(CheckPerDeviceNumericAggregates(logged_values, end_day_index)); |
| TearDown(); |
| } |
| } |
| |
| // Tests that EventAggregator::GenerateObservations() returns a positive |
| // status and that the expected number of Observations is generated after |
| // some EventCountEvents have been logged for PerDeviceNumericStats reports, without |
| // any garbage collection. |
| // |
| // For 35 days, logs a positive number of events each day for the |
| // ConnectionFailures_PerDeviceNumericStats report with "component_A" and for |
| // the SettingsChanged_PerDeviceNumericStats reports with "component_B", all with |
| // event code 0. |
| // |
| // Each day, calls GenerateObservations() with the day index of the previous |
| // day. Checks that a positive status is returned and that the |
| // FakeObservationStore has received the expected number of new observations |
| // for each locally aggregated report ID in the per_device_numeric_stats test |
| // registry. |
| TEST_F(PerDeviceNumericAggregateStoreTest, GenerateObservations) { |
| int num_days = 1; |
| std::vector<Observation> observations(0); |
| ExpectedAggregationParams expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| for (int offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| observations.clear(); |
| ResetObservationStore(); |
| EXPECT_EQ(kOK, GenerateObservations(day_index - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations(&observations, expected_params, |
| observation_store_.get(), update_recipient_.get())); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 1)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 1)); |
| EXPECT_EQ( |
| kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent(logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| } |
| // If this is the first time we're logging events, update the expected |
| // numbers of generated Observations to account for the logged events. |
| // For each report, for each aggregation window, expect 1 Observation more than if |
| // no events had been logged. |
| if (offset == 0) { |
| expected_params.daily_num_obs += 5; |
| expected_params.num_obs_per_report |
| [logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId] += 1; |
| expected_params.num_obs_per_report |
| [logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId] += |
| 2; |
| expected_params.num_obs_per_report[logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId] += 2; |
| } |
| AdvanceClock(kDay); |
| } |
| observations.clear(); |
| ResetObservationStore(); |
| EXPECT_EQ(kOK, GenerateObservations(CurrentDayIndex() - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations(&observations, expected_params, observation_store_.get(), |
| update_recipient_.get())); |
| } |
| |
| // Tests that EventAggregator::GenerateObservations() returns a positive |
| // status and that the expected number of Observations is generated after |
| // some EventCountEvents have been logged for PerDeviceNumeric reports over multiple |
| // days, and when the LocalAggregateStore is garbage-collected each day. |
| // |
| // For 35 days, logs a positive number of events each day for the |
| // ConnectionFailures_PerDeviceNumeric report with "component_A" and for |
| // the SettingsChanged_PerDeviceNumeric report with "component_B", all with |
| // event code 0. |
| // |
| // Each day, calls GenerateObservations() with the day index of the previous |
| // day. Checks that a positive status is returned and that the |
| // FakeObservationStore has received the expected number of new observations |
| // for each locally aggregated report ID in the per_device_numeric_stats test |
| // registry. |
| TEST_F(PerDeviceNumericAggregateStoreTest, GenerateObservationsWithGc) { |
| int num_days = 35; |
| std::vector<Observation> observations(0); |
| ExpectedAggregationParams expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| for (int offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| observations.clear(); |
| ResetObservationStore(); |
| EXPECT_EQ(kOK, GenerateObservations(day_index - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations(&observations, expected_params, |
| observation_store_.get(), update_recipient_.get())); |
| EXPECT_EQ(kOK, GarbageCollect(day_index)); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 1)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 1)); |
| EXPECT_EQ( |
| kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent(logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| } |
| // If this is the first time we're logging events, update the expected |
| // numbers of generated Observations to account for the logged events. |
| // For each report, for each window size, expect 1 Observation more than if |
| // no events had been logged. |
| if (offset == 0) { |
| expected_params.daily_num_obs += 5; |
| expected_params.num_obs_per_report |
| [logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId] += 1; |
| expected_params.num_obs_per_report |
| [logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId] += |
| 2; |
| expected_params.num_obs_per_report[logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId] += 2; |
| } |
| AdvanceClock(kDay); |
| } |
| observations.clear(); |
| ResetObservationStore(); |
| auto day_index = CurrentDayIndex(); |
| EXPECT_EQ(kOK, GenerateObservations(day_index - 1)); |
| EXPECT_TRUE(FetchAggregatedObservations(&observations, expected_params, observation_store_.get(), |
| update_recipient_.get())); |
| EXPECT_EQ(kOK, GarbageCollect(day_index)); |
| } |
| |
| // Tests that GenerateObservations() returns a positive status and that the |
| // expected number of Observations is generated when events are logged over |
| // multiple days and some of those days' Observations are backfilled, without |
| // any garbage collection of the LocalAggregateStore. |
| // |
| // Sets the |backfill_days_| field of the EventAggregator to 3. |
| // |
| // Logging pattern: |
| // For 35 days, logs 2 events each day for the |
| // ConnectionFailures_PerDeviceCount report and 2 events for the |
| // SettingsChanged_PerDeviceCount report, all with event code 0. |
| // |
| // Observation generation pattern: |
| // Calls GenerateObservations() on the 1st through 5th and the 7th out of |
| // every 10 days, for 35 days. |
| // |
| // Expected numbers of Observations: |
| // It is expected that 4 days' worth of Observations are generated on |
| // the first day of every 10 (the day index for which GenerateObservations() |
| // was called, plus 3 days of backfill), that 1 day's worth of Observations |
| // are generated on the 2nd through 5th day of every 10, that 2 days' |
| // worth of Observations are generated on the 7th day of every 10 (the |
| // day index for which GenerateObservations() was called, plus 1 day of |
| // backfill), and that no Observations are generated on the remaining days. |
| TEST_F(PerDeviceNumericAggregateStoreTest, GenerateObservationsWithBackfill) { |
| const auto& expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| // Add 2 events to the local aggregations each day for 35 days. Call GenerateObservations() on |
| // the first 5 day indices, and the 7th, out of every 10. |
| for (int offset = 0; offset < 35; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 1)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 1)); |
| EXPECT_EQ( |
| kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent(logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| } |
| auto num_obs_before = observation_store_->messages_received.size(); |
| if (offset % 10 < 5 || offset % 10 == 6) { |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| } |
| auto num_obs_after = observation_store_->messages_received.size(); |
| EXPECT_GE(num_obs_after, num_obs_before); |
| // Check that the expected daily number of Observations was generated. |
| switch (offset % 10) { |
| case 0: |
| // If this is the first day of logging, expect 3 Observations for each |
| // day in the backfill period and 8 Observations for the current day. |
| if (offset == 0) { |
| EXPECT_EQ( |
| (expected_params.daily_num_obs * backfill_days) + expected_params.daily_num_obs + 5, |
| num_obs_after - num_obs_before); |
| } else { |
| // If this is another day whose offset is a multiple of 10, expect 8 |
| // Observations for each day in the backfill period as well as the |
| // current day. |
| EXPECT_EQ((expected_params.daily_num_obs + 5) * (backfill_days + 1), |
| num_obs_after - num_obs_before); |
| } |
| break; |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| // Expect 8 Observations for this day. |
| EXPECT_EQ(expected_params.daily_num_obs + 5, num_obs_after - num_obs_before); |
| break; |
| case 6: |
| // Expect 8 Observations for each of today and yesterday. |
| EXPECT_EQ((expected_params.daily_num_obs + 5) * 2, num_obs_after - num_obs_before); |
| break; |
| default: |
| EXPECT_EQ(num_obs_after, num_obs_before); |
| } |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Tests that GenerateObservations() returns a positive status and that the |
| // expected number of Observations is generated when events are logged over |
| // multiple days and some of those days' Observations are backfilled, and when |
| // the LocalAggregateStore is garbage-collected after each call to |
| // GenerateObservations(). |
| // |
| // Sets the |backfill_days_| field of the EventAggregator to 3. |
| // |
| // Logging pattern: |
| // For 35 days, logs 2 events each day for the |
| // ConnectionFailures_PerDeviceNumeric report with "component_A" and 2 events |
| // for the SettingsChanged_PerDeviceNumeric reports with "component_B", all with |
| // event code 0. |
| // |
| // Observation generation pattern: |
| // Calls GenerateObservations() on the 1st through 5th and the 7th out of |
| // every 10 days, for 35 days. Garbage-collects the LocalAggregateStore after |
| // each call. |
| // |
| // Expected numbers of Observations: |
| // It is expected that 4 days' worth of Observations are generated on |
| // the first day of every 10 (the day index for which GenerateObservations() |
| // was called, plus 3 days of backfill), that 1 day's worth of Observations |
| // are generated on the 2nd through 5th day of every 10, that 2 days' |
| // worth of Observations are generated on the 7th day of every 10 (the |
| // day index for which GenerateObservations() was called, plus 1 day of |
| // backfill), and that no Observations are generated on the remaining days. |
| TEST_F(PerDeviceNumericAggregateStoreTest, GenerateObservationsWithBackfillAndGc) { |
| int num_days = 35; |
| const auto& expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| // Add 2 events to the local aggregations each day for 35 days. Call GenerateObservations() on |
| // the first 5 day indices, and the 7th, out of every 10. |
| for (int offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (int i = 0; i < 2; i++) { |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 1)); |
| EXPECT_EQ( |
| kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent(logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| } |
| auto num_obs_before = observation_store_->messages_received.size(); |
| if (offset % 10 < 5 || offset % 10 == 6) { |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| EXPECT_EQ(kOK, GarbageCollect(day_index)); |
| } |
| auto num_obs_after = observation_store_->messages_received.size(); |
| EXPECT_GE(num_obs_after, num_obs_before); |
| // Check that the expected daily number of Observations was generated. |
| switch (offset % 10) { |
| case 0: |
| // If this is the first day of logging, expect 3 Observations for each |
| // day in the backfill period and 8 Observations for the current day. |
| if (offset == 0) { |
| EXPECT_EQ( |
| (expected_params.daily_num_obs * backfill_days) + expected_params.daily_num_obs + 5, |
| num_obs_after - num_obs_before); |
| } else { |
| // If this is another day whose offset is a multiple of 10, expect 8 |
| // Observations for each day in the backfill period as well as the |
| // current day. |
| EXPECT_EQ((expected_params.daily_num_obs + 5) * (backfill_days + 1), |
| num_obs_after - num_obs_before); |
| } |
| break; |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| // Expect 8 Observations for this day. |
| EXPECT_EQ(expected_params.daily_num_obs + 5, num_obs_after - num_obs_before); |
| break; |
| case 6: |
| // Expect 6 Observations for each of today and yesterday. |
| EXPECT_EQ((expected_params.daily_num_obs + 5) * 2, num_obs_after - num_obs_before); |
| break; |
| default: |
| EXPECT_EQ(num_obs_after, num_obs_before); |
| } |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Generate Observations without logging any events, and check that the |
| // resulting Observations are as expected: 1 ReportParticipationObservation for |
| // each PER_DEVICE_NUMERIC_STATS report in the config, and no |
| // PerDeviceNumericObservations. |
| TEST_F(PerDeviceNumericAggregateStoreTest, CheckObservationValuesNoEvents) { |
| const auto current_day_index = CurrentDayIndex(); |
| EXPECT_EQ(kOK, GenerateObservations(current_day_index)); |
| const auto& expected_report_participation_obs = MakeExpectedReportParticipationObservations( |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams, current_day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations({}, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| |
| // Check that the expected PerDeviceNumericObservations and |
| // ReportParticipationObservations are generated when GenerateObservations() is |
| // called after logging some EventCountEvents and ElapsedTimeEvents for |
| // PER_DEVICE_NUMERIC_STATS reports over a single day index. |
| TEST_F(PerDeviceNumericAggregateStoreTest, CheckObservationValuesSingleDay) { |
| const auto day_index = CurrentDayIndex(); |
| // Add several events to the local aggregations on |day_index|. |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 5)); |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_B", 0u, 5)); |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 0u, 5)); |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, |
| day_index, "component_A", 1u, 5)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index, "component_C", 0u, 5)); |
| EXPECT_EQ(kOK, |
| AddPerDeviceEventCountEvent( |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index, "component_C", 0u, 5)); |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId, |
| day_index, "component_C", 0u, 5)); |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(logger::testing::per_device_numeric_stats:: |
| kSettingsChangedAggregationWindowMetricReportId, |
| day_index, "component_C", 0u, 5)); |
| |
| std::vector<MetricReportId> streaming_time_ids = { |
| logger::testing::per_device_numeric_stats::kStreamingTimeTotalMetricReportId, |
| logger::testing::per_device_numeric_stats::kStreamingTimeMinMetricReportId, |
| logger::testing::per_device_numeric_stats::kStreamingTimeMaxMetricReportId}; |
| for (const auto& id : streaming_time_ids) { |
| EXPECT_EQ(kOK, AddPerDeviceElapsedTimeEvent(id, day_index, "component_D", 0u, 15)); |
| EXPECT_EQ(kOK, AddPerDeviceElapsedTimeEvent(id, day_index, "component_D", 1u, 5)); |
| EXPECT_EQ(kOK, AddPerDeviceElapsedTimeEvent(id, day_index, "component_D", 0u, 10)); |
| } |
| // Generate locally aggregated Observations for |day_index|. |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| |
| // Form the expected Observations. |
| auto expected_report_participation_obs = MakeExpectedReportParticipationObservations( |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams, day_index); |
| ExpectedPerDeviceNumericObservations expected_per_device_numeric_obs; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kConnectionFailuresMetricReportId, day_index}][1] = |
| {{"component_A", 0u, 10}, {"component_A", 1u, 5}, {"component_B", 0u, 5}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index}][7] = {{"component_C", 0u, 10}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kSettingsChangedWindowSizeMetricReportId, |
| day_index}][30] = {{"component_C", 0u, 10}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kSettingsChangedAggregationWindowMetricReportId, |
| day_index}][7] = {{"component_C", 0u, 10}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kSettingsChangedAggregationWindowMetricReportId, |
| day_index}][30] = {{"component_C", 0u, 10}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kStreamingTimeTotalMetricReportId, day_index}][1] = |
| {{"component_D", 0u, 25}, {"component_D", 1u, 5}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kStreamingTimeTotalMetricReportId, day_index}][7] = |
| {{"component_D", 0u, 25}, {"component_D", 1u, 5}}; |
| // The 7-day minimum value for the StreamingTime metric is 0 for all event |
| // codes and components, so we don't expect a PerDeviceNumericObservation with |
| // a 7-day window for the StreamingTime_PerDeviceMin report. |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kStreamingTimeMinMetricReportId, day_index}][1] = { |
| {"component_D", 0u, 10}, {"component_D", 1u, 5}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kStreamingTimeMinMetricReportId, day_index}][7] = { |
| {"component_D", 0u, 10}, {"component_D", 1u, 5}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kStreamingTimeMaxMetricReportId, day_index}][1] = { |
| {"component_D", 0u, 15}, {"component_D", 1u, 5}}; |
| expected_per_device_numeric_obs[{ |
| logger::testing::per_device_numeric_stats::kStreamingTimeMaxMetricReportId, day_index}][7] = { |
| {"component_D", 0u, 15}, {"component_D", 1u, 5}}; |
| |
| EXPECT_TRUE(CheckPerDeviceNumericObservations(expected_per_device_numeric_obs, |
| expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| |
| // Checks that PerDeviceNumericObservations with the expected values are |
| // generated when some events have been logged for an EVENT_COUNT metric with |
| // a PER_DEVICE_NUMERIC_STATS report over multiple days and |
| // GenerateObservations() is called each day, without garbage collection or |
| // backfill. |
| // |
| // Logged events for the SettingsChanged_PerDeviceCount report on the i-th |
| // day: |
| // |
| // i (component, event code, count) |
| // ----------------------------------------------------------------------- |
| // 0 |
| // 1 ("A", 1, 3) |
| // 2 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // 3 ("A", 1, 3) |
| // 4 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // 5 ("A", 1, 3) |
| // 6 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // 7 ("A", 1, 3) |
| // 8 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // 9 ("A", 1, 3) |
| // |
| // Expected PerDeviceNumericObservations for the |
| // SettingsChanged_PerDeviceNumeric report on the i-th day: |
| // |
| // (i, window size) (component, event code, count) |
| // ----------------------------------------------------------------------- |
| // (0, 7) |
| // (0, 30) |
| // (1, 7) ("A", 1, 3) |
| // (1, 30) ("A", 1, 3) |
| // (2, 7) ("A", 1, 6), ("A", 2, 3), ("B", 1, 2) |
| // (2, 30) ("A", 1, 6), ("A", 2, 3), ("B", 1, 2) |
| // (3, 7) ("A", 1, 9), ("A", 2, 3), ("B", 1, 2) |
| // (3, 30) ("A", 1, 9), ("A", 2, 3), ("B", 1, 2) |
| // (4, 7) ("A", 1, 12), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2) |
| // (4, 30) ("A", 1, 12), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2) |
| // (5, 7) ("A", 1, 15), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2) |
| // (5, 30) ("A", 1, 15), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2) |
| // (6, 7) ("A", 1, 18), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2) |
| // (6, 30) ("A", 1, 18), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2) |
| // (7, 7) ("A", 1, 21), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2) |
| // (7, 30) ("A", 1, 21), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2) |
| // (8, 7) ("A", 1, 21), ("A", 2, 12), ("B", 1, 8), ("B", 2, 4) |
| // (8, 30) ("A", 1, 24), ("A", 2, 12), ("B", 1, 8), ("B", 2, 4) |
| // (9, 7) ("A", 1, 21), ("A", 2, 9), ("B", 1, 6), ("B", 2, 4) |
| // (9, 30) ("A", 1, 27), ("A", 2, 12), ("B", 1, 8), ("B", 2, 4) |
| // |
| // In addition, expect 1 ReportParticipationObservation each day for each of |
| // the reports in the registry. |
| TEST_F(PerDeviceNumericAggregateStoreTest, CheckObservationValuesMultiDay) { |
| auto start_day_index = CurrentDayIndex(); |
| const auto& expected_id = |
| logger::testing::per_device_numeric_stats::kSettingsChangedAggregationWindowMetricReportId; |
| const auto& expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| // Form expected Observations for the 10 days of logging. |
| uint32_t num_days = 10; |
| std::vector<ExpectedPerDeviceNumericObservations> expected_per_device_numeric_obs(num_days); |
| std::vector<ExpectedReportParticipationObservations> expected_report_participation_obs(num_days); |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| expected_report_participation_obs[offset] = |
| MakeExpectedReportParticipationObservations(expected_params, start_day_index + offset); |
| } |
| expected_per_device_numeric_obs[0] = {}; |
| expected_per_device_numeric_obs[1][{expected_id, start_day_index + 1}] = {{7, {{"A", 1u, 3}}}, |
| {30, {{"A", 1u, 3}}}}; |
| expected_per_device_numeric_obs[2][{expected_id, start_day_index + 2}] = { |
| {7, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[3][{expected_id, start_day_index + 3}] = { |
| {7, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[4][{expected_id, start_day_index + 4}] = { |
| {7, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[5][{expected_id, start_day_index + 5}] = { |
| {7, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[6][{expected_id, start_day_index + 6}] = { |
| {7, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[7][{expected_id, start_day_index + 7}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[8][{expected_id, start_day_index + 8}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}, |
| {30, {{"A", 1u, 24}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}}; |
| expected_per_device_numeric_obs[9][{expected_id, start_day_index + 9}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 4}}}, |
| {30, {{"A", 1u, 27}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}}; |
| |
| for (uint32_t offset = 0; offset < 1; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (uint32_t event_code = 1; event_code < 3; event_code++) { |
| if (offset > 0 && offset % event_code == 0) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "A", event_code, 3)); |
| } |
| if (offset > 0 && offset % (2 * event_code) == 0) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "B", event_code, 2)); |
| } |
| } |
| // Clear the FakeObservationStore. |
| ResetObservationStore(); |
| // Generate locally aggregated Observations. |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs[offset], expected_report_participation_obs[offset], |
| observation_store_.get(), update_recipient_.get())) |
| << "offset = " << offset; |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Repeat the CheckObservationValuesMultiDay test, this time calling |
| // GarbageCollect() after each call to GenerateObservations. |
| // |
| // The logging pattern and set of Observations for each day index is the same |
| // as in PerDeviceNumericAggregateStoreTest::CheckObservationValuesMultiDay. |
| // See that test for documentation. |
| TEST_F(PerDeviceNumericAggregateStoreTest, CheckObservationValuesMultiDayWithGarbageCollection) { |
| auto start_day_index = CurrentDayIndex(); |
| const auto& expected_id = |
| logger::testing::per_device_numeric_stats::kSettingsChangedAggregationWindowMetricReportId; |
| const auto& expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| // Form expected Observations for the 10 days of logging. |
| uint32_t num_days = 10; |
| std::vector<ExpectedPerDeviceNumericObservations> expected_per_device_numeric_obs(num_days); |
| std::vector<ExpectedReportParticipationObservations> expected_report_participation_obs(num_days); |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| expected_report_participation_obs[offset] = |
| MakeExpectedReportParticipationObservations(expected_params, start_day_index + offset); |
| } |
| expected_per_device_numeric_obs[0] = {}; |
| expected_per_device_numeric_obs[1][{expected_id, start_day_index + 1}] = {{7, {{"A", 1u, 3}}}, |
| {30, {{"A", 1u, 3}}}}; |
| expected_per_device_numeric_obs[2][{expected_id, start_day_index + 2}] = { |
| {7, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[3][{expected_id, start_day_index + 3}] = { |
| {7, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[4][{expected_id, start_day_index + 4}] = { |
| {7, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[5][{expected_id, start_day_index + 5}] = { |
| {7, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[6][{expected_id, start_day_index + 6}] = { |
| {7, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[7][{expected_id, start_day_index + 7}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[8][{expected_id, start_day_index + 8}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}, |
| {30, {{"A", 1u, 24}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}}; |
| expected_per_device_numeric_obs[9][{expected_id, start_day_index + 9}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 4}}}, |
| {30, {{"A", 1u, 27}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}}; |
| |
| for (uint32_t offset = 0; offset < 10; offset++) { |
| auto day_index = CurrentDayIndex(); |
| for (uint32_t event_code = 1; event_code < 3; event_code++) { |
| if (offset > 0 && offset % event_code == 0) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "A", event_code, 3)); |
| } |
| if (offset > 0 && offset % (2 * event_code) == 0) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "B", event_code, 2)); |
| } |
| } |
| // Advance |test_clock_| by 1 day. |
| AdvanceClock(kDay); |
| // Clear the FakeObservationStore. |
| ResetObservationStore(); |
| // Generate locally aggregated Observations and garbage-collect the |
| // LocalAggregateStore, both for the previous day as measured by |
| // |test_clock_|. Back up the LocalAggregateStore and |
| // AggregatedObservationHistoryStore. |
| DoScheduledTasksNow(); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs[offset], expected_report_participation_obs[offset], |
| observation_store_.get(), update_recipient_.get())); |
| } |
| } |
| |
| // Tests that the expected PerDeviceNumericObservations are generated when |
| // events are logged over multiple days for an EVENT_COUNT |
| // metric with a PER_DEVICE_NUMERIC_STATS report, when Observations are |
| // backfilled for some days during that period, without any garbage-collection |
| // of the LocalAggregateStore. |
| // |
| // The logging pattern and set of Observations for each day index is the same |
| // as in PerDeviceNumericAggregateStoreTest::CheckObservationValuesMultiDay. |
| // See that test for documentation. |
| TEST_F(PerDeviceNumericAggregateStoreTest, CheckObservationValuesWithBackfill) { |
| auto start_day_index = CurrentDayIndex(); |
| const auto& expected_id = |
| logger::testing::per_device_numeric_stats::kSettingsChangedAggregationWindowMetricReportId; |
| const auto& expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| // Add events to the local aggregations for 9 days. Call GenerateObservations() on the first 6 |
| // day indices, and the 9th. |
| uint32_t num_days = 9; |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| ResetObservationStore(); |
| for (uint32_t event_code = 1; event_code < 3; event_code++) { |
| if (offset > 0 && (offset % event_code == 0)) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "A", event_code, 3)); |
| } |
| if (offset > 0 && offset % (2 * event_code) == 0) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "B", event_code, 2)); |
| } |
| } |
| if (offset < 6 || offset == 8) { |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| } |
| // Make the set of Observations which are expected to be generated on |
| // |start_day_index + offset| and check it against the contents of the |
| // FakeObservationStore. |
| ExpectedPerDeviceNumericObservations expected_per_device_numeric_obs; |
| ExpectedReportParticipationObservations expected_report_participation_obs; |
| switch (offset) { |
| case 0: { |
| for (uint32_t day_index = start_day_index - backfill_days; day_index <= start_day_index; |
| day_index++) { |
| for (const auto& pair : |
| MakeExpectedReportParticipationObservations(expected_params, day_index)) { |
| expected_report_participation_obs.insert(pair); |
| } |
| } |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 1: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = {{7, {{"A", 1u, 3}}}, |
| {30, {{"A", 1u, 3}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 2: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 3: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 4: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 5: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 8: { |
| expected_per_device_numeric_obs[{expected_id, start_day_index + 6}] = { |
| {7, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{expected_id, start_day_index + 7}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{expected_id, start_day_index + 8}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}, |
| {30, {{"A", 1u, 24}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}}; |
| for (uint32_t day_index = start_day_index + 6; day_index <= start_day_index + 8; |
| day_index++) { |
| for (const auto& pair : |
| MakeExpectedReportParticipationObservations(expected_params, day_index)) { |
| expected_report_participation_obs.insert(pair); |
| } |
| } |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| default: |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| AdvanceClock(kDay); |
| } |
| } |
| |
| // Tests that the expected Observations are generated for |
| // PerDeviceNumericStats reports when events are logged for over multiple days |
| // for an EVENT_COUNT metric with a PER_DEVICE_NUMERIC_STATS report, when |
| // Observations are backfilled for some days during that period, and when the |
| // LocalAggregatedStore is garbage-collected after each call to |
| // GenerateObservations(). |
| // |
| // The logging pattern and set of Observations for each day index is the same |
| // as in PerDeviceNumericAggregateStoreTest::CheckObservationValuesMultiDay. |
| // See that test for documentation. |
| TEST_F(PerDeviceNumericAggregateStoreTest, EventCountCheckObservationValuesWithBackfillAndGc) { |
| auto start_day_index = CurrentDayIndex(); |
| const auto& expected_id = |
| logger::testing::per_device_numeric_stats::kSettingsChangedAggregationWindowMetricReportId; |
| const auto& expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| // Add events to the local aggregations for 9 days. Call GenerateObservations() on the first 6 |
| // day indices, and the 9th. |
| uint32_t num_days = 9; |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| ResetObservationStore(); |
| for (uint32_t event_code = 1; event_code < 3; event_code++) { |
| if (offset > 0 && (offset % event_code == 0)) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "A", event_code, 3)); |
| } |
| if (offset > 0 && offset % (2 * event_code) == 0) { |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent(expected_id, day_index, "B", event_code, 2)); |
| } |
| } |
| // Advance |test_clock_| by 1 day. |
| AdvanceClock(kDay); |
| if (offset < 6 || offset == 8) { |
| // Generate Observations and garbage-collect, both for the previous day |
| // index according to |test_clock_|. Back up the LocalAggregateStore and |
| // the AggregatedObservationHistoryStore. |
| DoScheduledTasksNow(); |
| } |
| // Make the set of Observations which are expected to be generated on |
| // |start_day_index + offset| and check it against the contents of the |
| // FakeObservationStore. |
| ExpectedPerDeviceNumericObservations expected_per_device_numeric_obs; |
| ExpectedReportParticipationObservations expected_report_participation_obs; |
| switch (offset) { |
| case 0: { |
| for (uint32_t day_index = start_day_index - backfill_days; day_index <= start_day_index; |
| day_index++) { |
| for (const auto& pair : |
| MakeExpectedReportParticipationObservations(expected_params, day_index)) { |
| expected_report_participation_obs.insert(pair); |
| } |
| } |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 1: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = {{7, {{"A", 1u, 3}}}, |
| {30, {{"A", 1u, 3}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 2: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 3: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {30, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 4: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 5: { |
| expected_per_device_numeric_obs[{expected_id, day_index}] = { |
| {7, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 8: { |
| expected_per_device_numeric_obs[{expected_id, start_day_index + 6}] = { |
| {7, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{expected_id, start_day_index + 7}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}, |
| {30, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{expected_id, start_day_index + 8}] = { |
| {7, {{"A", 1u, 21}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}, |
| {30, {{"A", 1u, 24}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}}; |
| for (uint32_t day_index = start_day_index + 6; day_index <= start_day_index + 8; |
| day_index++) { |
| for (const auto& pair : |
| MakeExpectedReportParticipationObservations(expected_params, day_index)) { |
| expected_report_participation_obs.insert(pair); |
| } |
| } |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| default: |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| } |
| } |
| |
| // Tests that the expected Observations are generated for |
| // PerDeviceNumericStats reports when events are logged for over multiple days |
| // for an ELAPSED_TIME metric with PER_DEVICE_NUMERIC_STATS reports with |
| // multiple aggregation types, when Observations are backfilled for some days |
| // during that period, and when the LocalAggregatedStore is garbage-collected |
| // after each call to GenerateObservations(). |
| // |
| // Logged events for the StreamingTime_PerDevice{Total, Min, Max} reports on the |
| // i-th day: |
| // |
| // i (component, event code, count) |
| // ----------------------------------------------------------------------- |
| // 0 |
| // 1 ("A", 1, 3) |
| // 2 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // 3 ("A", 1, 3) |
| // 4 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // 5 ("A", 1, 3) |
| // 6 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // 7 ("A", 1, 3) |
| // 8 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // |
| // Expected PerDeviceNumericObservations for the |
| // StreamingTime_PerDeviceTotal report on the i-th day: |
| // |
| // (day, window size) (event code, component, total) |
| // --------------------------------------------------------------------------- |
| // (0, 1) |
| // (0, 7) |
| // (1, 1) ("A", 1, 3) |
| // (1, 7) ("A", 1, 3) |
| // (2, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (2, 7) ("A", 1, 6), ("A", 2, 3), ("B", 1, 2) |
| // (3, 1) ("A", 1, 3) |
| // (3, 7) ("A", 1, 9), ("A", 2, 3), ("B", 1, 2) |
| // (4, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (4, 7) ("A", 1, 12), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2) |
| // (5, 1) ("A", 1, 3) |
| // (5, 7) ("A", 1, 15), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2) |
| // (6, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (6, 7) ("A", 1, 18), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2) |
| // (7, 1) ("A", 1, 3) |
| // (7, 7) ("A", 1, 21), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2) |
| // (8, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (8, 7) ("A", 1, 21), ("A", 2, 12), ("B", 1, 8), ("B", 2, 4) |
| // |
| // Expected PerDeviceNumericObservations for the |
| // StreamingTime_PerDeviceMin report on the i-th day: |
| // |
| // (day, window size) (event code, component, total) |
| // --------------------------------------------------------------------------- |
| // (0, 1) |
| // (0. 7) |
| // (1, 1) ("A", 1, 3) |
| // (1, 7) ("A", 1, 3) |
| // (2, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (2, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (3, 1) ("A", 1, 3) |
| // (3, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (4, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (4, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (5, 1) ("A", 1, 3) |
| // (5, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (6, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (6, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (7, 1) ("A", 1, 3) |
| // (7, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (8, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (8, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // |
| // Expected PerDeviceNumericObservations for the |
| // StreamingTime_PerDeviceMax report on the i-th day: |
| // |
| // (day, window size) (event code, component, total) |
| // --------------------------------------------------------------------------- |
| // (0, 1) |
| // (0. 7) |
| // (1, 1) ("A", 1, 3) |
| // (1, 7) ("A", 1, 3) |
| // (2, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (2, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (3, 1) ("A", 1, 3) |
| // (3, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (4, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (4, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (5, 1) ("A", 1, 3) |
| // (5, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (6, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2) |
| // (6, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (7, 1) ("A", 1, 3) |
| // (7, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (8, 1) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // (8, 7) ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2) |
| // |
| // In addition, expect 1 ReportParticipationObservation each day for each |
| // report in the registry. |
| TEST_F(PerDeviceNumericAggregateStoreTest, ElapsedTimeCheckObservationValuesWithBackfillAndGc) { |
| auto start_day_index = CurrentDayIndex(); |
| const auto& total_report_id = |
| logger::testing::per_device_numeric_stats::kStreamingTimeTotalMetricReportId; |
| const auto& min_report_id = |
| logger::testing::per_device_numeric_stats::kStreamingTimeMinMetricReportId; |
| const auto& max_report_id = |
| logger::testing::per_device_numeric_stats::kStreamingTimeMaxMetricReportId; |
| std::vector<MetricReportId> streaming_time_ids = {total_report_id, min_report_id, max_report_id}; |
| const auto& expected_params = |
| logger::testing::per_device_numeric_stats::kExpectedAggregationParams; |
| // Set |backfill_days_| to 3. |
| size_t backfill_days = 3; |
| SetBackfillDays(backfill_days); |
| // Add events to the local aggregations for 9 days. Call GenerateObservations() on the first 6 |
| // day indices, and the 9th. |
| uint32_t num_days = 9; |
| for (uint32_t offset = 0; offset < num_days; offset++) { |
| auto day_index = CurrentDayIndex(); |
| ResetObservationStore(); |
| for (uint32_t event_code = 1; event_code < 3; event_code++) { |
| for (const auto& report_id : streaming_time_ids) { |
| if (offset > 0 && (offset % event_code == 0)) { |
| EXPECT_EQ(kOK, AddPerDeviceElapsedTimeEvent(report_id, day_index, "A", event_code, 3)); |
| } |
| if (offset > 0 && offset % (2 * event_code) == 0) { |
| EXPECT_EQ(kOK, AddPerDeviceElapsedTimeEvent(report_id, day_index, "B", event_code, 2)); |
| } |
| } |
| } |
| |
| // Advance |test_clock_| by 1 day. |
| AdvanceClock(kDay); |
| if (offset < 6 || offset == 8) { |
| // Generate Observations and garbage-collect, both for the previous day |
| // index according to |test_clock_|. Back up the LocalAggregateStore and |
| // the AggregatedObservationHistoryStore. |
| DoScheduledTasksNow(); |
| } |
| // Make the set of Observations which are expected to be generated on |
| // |start_day_index + offset| and check it against the contents of the |
| // FakeObservationStore. |
| ExpectedPerDeviceNumericObservations expected_per_device_numeric_obs; |
| ExpectedReportParticipationObservations expected_report_participation_obs; |
| switch (offset) { |
| case 0: { |
| for (uint32_t day_index = start_day_index - backfill_days; day_index <= start_day_index; |
| day_index++) { |
| for (const auto& pair : |
| MakeExpectedReportParticipationObservations(expected_params, day_index)) { |
| expected_report_participation_obs.insert(pair); |
| } |
| } |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 1: { |
| expected_per_device_numeric_obs[{total_report_id, day_index}] = {{1, {{"A", 1u, 3}}}, |
| {7, {{"A", 1u, 3}}}}; |
| expected_per_device_numeric_obs[{min_report_id, day_index}] = {{1, {{"A", 1u, 3}}}, |
| {7, {{"A", 1u, 3}}}}; |
| expected_per_device_numeric_obs[{max_report_id, day_index}] = {{1, {{"A", 1u, 3}}}, |
| {7, {{"A", 1u, 3}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())) |
| << "day 1"; |
| break; |
| } |
| case 2: { |
| expected_per_device_numeric_obs[{total_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {7, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[{min_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[{max_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 3: { |
| expected_per_device_numeric_obs[{total_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[{min_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_per_device_numeric_obs[{max_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 4: { |
| expected_per_device_numeric_obs[{total_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}, |
| {7, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{min_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{max_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 5: { |
| expected_per_device_numeric_obs[{total_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{min_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{max_report_id, day_index}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_report_participation_obs = |
| MakeExpectedReportParticipationObservations(expected_params, day_index); |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| case 8: { |
| expected_per_device_numeric_obs[{total_report_id, start_day_index + 6}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {7, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{total_report_id, start_day_index + 7}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{total_report_id, start_day_index + 8}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}, |
| {7, {{"A", 1u, 21}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}}; |
| |
| expected_per_device_numeric_obs[{min_report_id, start_day_index + 6}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{min_report_id, start_day_index + 7}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{min_report_id, start_day_index + 8}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| |
| expected_per_device_numeric_obs[{max_report_id, start_day_index + 6}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{max_report_id, start_day_index + 7}] = { |
| {1, {{"A", 1u, 3}}}, {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| expected_per_device_numeric_obs[{max_report_id, start_day_index + 8}] = { |
| {1, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}, |
| {7, {{"A", 1u, 3}, {"A", 2u, 3}, {"B", 1u, 2}, {"B", 2u, 2}}}}; |
| |
| for (uint32_t day_index = start_day_index + 6; day_index <= start_day_index + 8; |
| day_index++) { |
| for (const auto& pair : |
| MakeExpectedReportParticipationObservations(expected_params, day_index)) { |
| expected_report_participation_obs.insert(pair); |
| } |
| } |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| break; |
| } |
| default: |
| EXPECT_TRUE(CheckPerDeviceNumericObservations( |
| expected_per_device_numeric_obs, expected_report_participation_obs, |
| observation_store_.get(), update_recipient_.get())); |
| } |
| } |
| } |
| |
| // Check that GenerateObservations returns an OK status after some events have been logged for a |
| // PerDeviceHistogram report. |
| TEST_F(PerDeviceHistogramAggregateStoreTest, GenerateObservations) { |
| const auto day_index = CurrentDayIndex(); |
| // Add several events to the local aggregations on |day_index|. |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent( |
| logger::testing::per_device_histogram::kSettingsChangedMetricReportId, |
| day_index, "component_C", 0u, 5)); |
| EXPECT_EQ(kOK, AddPerDeviceEventCountEvent( |
| logger::testing::per_device_histogram::kSettingsChangedMetricReportId, |
| day_index, "component_C", 0u, 5)); |
| |
| // Generate locally aggregated Observations for |day_index|. |
| EXPECT_EQ(kOK, GenerateObservations(day_index)); |
| } |
| |
| // Tests GenerateObservations() and GarbageCollect() in the case where the |
| // LocalAggregateStore contains aggregates for metrics with both UTC and LOCAL |
| // time zone policies, and where the day index in local time may be less than |
| // the day index in UTC. |
| TEST_F(NoiseFreeMixedTimeZoneAggregateStoreTest, LocalBeforeUTC) { |
| std::vector<ExpectedUniqueActivesObservations> expected_obs(3); |
| // Begin at a time when the current day index is the same in both UTC and local time. Add 1 |
| // event to the local aggregations for event code 0 for each of the 2 reports, then generate |
| // Observations and garbage-collect for the previous day index in each of UTC and local time. |
| auto start_day_index = CurrentDayIndex(); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, |
| start_day_index, 0u); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index, 0u); |
| GenerateObservations(start_day_index - 1, start_day_index - 1); |
| GarbageCollect(start_day_index - 1, start_day_index - 1); |
| // Form the expected contents of the FakeObservationStore. |
| // Since no events were logged on the previous day and no Observations have |
| // been generated for that day yet, expect Observations of non-activity for |
| // all event codes, for both reports. |
| expected_obs[0] = MakeNullExpectedUniqueActivesObservations( |
| logger::testing::mixed_time_zone::kExpectedAggregationParams, start_day_index - 1); |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[0], observation_store_.get(), |
| update_recipient_.get())); |
| ResetObservationStore(); |
| // Advance the day index in UTC, but not in local time, and log 1 event for |
| // event code 1 for each of the 2 reports. Generate Observations and |
| // garbage-collect for the previous day in each of UTC and local time. |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, |
| start_day_index, 1u); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index + 1, 1u); |
| GenerateObservations(start_day_index, start_day_index - 1); |
| GarbageCollect(start_day_index, start_day_index - 1); |
| // Form the expected contents of the FakeObservationStore. Since |
| // Observations have already been generated for the |
| // DeviceBoots_UniqueDevices report for |start_day_index - 1|, expect no |
| // Observations for that report. |
| expected_obs[1][{logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index}] = {{1, {true, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[1], observation_store_.get(), |
| update_recipient_.get())); |
| ResetObservationStore(); |
| // Advance the day index in local time so that it is equal to the day index in UTC. Add 1 event |
| // to the local aggregations for event code 2 for each of the 2 reports, then generate |
| // Observations and garbage-collect for the previous day in each of UTC and local time. |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, |
| start_day_index + 1, 2u); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index + 1, 2u); |
| GenerateObservations(start_day_index, start_day_index); |
| GarbageCollect(start_day_index, start_day_index); |
| // Form the expected contents of the FakeObservationStore. Since |
| // Observations have already been generated for the |
| // FeaturesActive_UniqueDevices report for day |start_day_index|, expect no |
| // Observations for that report. |
| expected_obs[2][{logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, start_day_index}] = |
| {{1, {true, true, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[2], observation_store_.get(), |
| update_recipient_.get())); |
| } |
| |
| // Tests GenerateObservations() and GarbageCollect() in the case where the |
| // LocalAggregateStore contains aggregates for metrics with both UTC and LOCAL |
| // time zone policies, and where the day index in UTC may be less than |
| // the day index in local time. |
| TEST_F(NoiseFreeMixedTimeZoneAggregateStoreTest, LocalAfterUTC) { |
| std::vector<ExpectedUniqueActivesObservations> expected_obs(3); |
| // Begin at a time when the current day index is the same in both UTC and local time. Add 1 |
| // event to the local aggregations for event code 0 for each of the 2 reports, then generate |
| // Observations and garbage-collect for the previous day index in each of UTC and local time. |
| auto start_day_index = CurrentDayIndex(); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, |
| start_day_index, 0u); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index, 0u); |
| GenerateObservations(start_day_index - 1, start_day_index - 1); |
| GarbageCollect(start_day_index - 1, start_day_index - 1); |
| // Form the expected contents of the FakeObservationStore. |
| // Since no events were logged on the previous day and no Observations have |
| // been generated for that day yet, expect Observations of non-activity for |
| // all event codes, for both reports. |
| expected_obs[0] = MakeNullExpectedUniqueActivesObservations( |
| logger::testing::mixed_time_zone::kExpectedAggregationParams, start_day_index - 1); |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[0], observation_store_.get(), |
| update_recipient_.get())); |
| ResetObservationStore(); |
| // Advance the day index in local time, but not in UTC, and log 1 event for |
| // event code 1 for each of the 2 reports. Generate Observations and |
| // garbage-collect for the previous day in each of UTC and local time. |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, |
| start_day_index + 1, 1u); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index, 1u); |
| GenerateObservations(start_day_index - 1, start_day_index); |
| GarbageCollect(start_day_index - 1, start_day_index); |
| // Form the expected contents of the FakeObservationStore. Since |
| // Observations have already been generated for the |
| // FeaturesActive_UniqueDevices report for |start_day_index - 1|, expect no |
| // Observations for that report. |
| expected_obs[1][{logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, start_day_index}] = |
| {{1, {true, false, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[1], observation_store_.get(), |
| update_recipient_.get())); |
| ResetObservationStore(); |
| // Advance the day index in UTC so that it is equal to the day index in local time. Add 1 event |
| // to the local aggregations for event code 2 for each of the 2 reports, then generate |
| // Observations and garbage-collect for the previous day in each of UTC and local time. |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kDeviceBootsMetricReportId, |
| start_day_index + 1, 2u); |
| AddUniqueActivesEvent(logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index + 1, 2u); |
| GenerateObservations(start_day_index, start_day_index); |
| GarbageCollect(start_day_index, start_day_index); |
| // Form the expected contents of the FakeObservationStore. Since |
| // Observations have already been generated for the |
| // DeviceBoots_UniqueDevices report for day |start_day_index|, expect no |
| // Observations for that report. |
| expected_obs[2][{logger::testing::mixed_time_zone::kFeaturesActiveMetricReportId, |
| start_day_index}] = {{1, {true, true, false}}}; |
| EXPECT_TRUE(CheckUniqueActivesObservations(expected_obs[2], observation_store_.get(), |
| update_recipient_.get())); |
| } |
| } // namespace local_aggregation |
| } // namespace cobalt |