| // Copyright 2018 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/logger/logger.h" |
| |
| #include <memory> |
| #include <string> |
| |
| #include <google/protobuf/text_format.h> |
| #include <google/protobuf/util/message_differencer.h> |
| #include <gtest/gtest.h> |
| |
| #include "src/lib/util/clock.h" |
| #include "src/lib/util/datetime_util.h" |
| #include "src/lib/util/encrypted_message_util.h" |
| #include "src/lib/util/testing/test_with_files.h" |
| #include "src/local_aggregation/testing/test_local_aggregation.h" |
| #include "src/logger/encoder.h" |
| #include "src/logger/fake_logger.h" |
| #include "src/logger/logger_test_utils.h" |
| #include "src/logger/project_context.h" |
| #include "src/logger/testing_constants.h" |
| #include "src/pb/metadata_builder.h" |
| #include "src/pb/observation.pb.h" |
| #include "src/system_data/client_secret.h" |
| #include "src/system_data/fake_system_data.h" |
| |
| #ifndef PROTO_LITE |
| using ::google::protobuf::util::MessageDifferencer; |
| #endif |
| |
| namespace cobalt::logger { |
| |
| using local_aggregation::TestLocalAggregation; |
| using system_data::ClientSecret; |
| using testing::CheckNumericEventObservations; |
| using testing::ExpectedAggregationParams; |
| using testing::ExpectedPerDeviceNumericObservations; |
| using testing::ExpectedReportParticipationObservations; |
| using testing::ExpectedUniqueActivesObservations; |
| using testing::FakeObservationStore; |
| using testing::GetTestProject; |
| using testing::GetTestProjectContextFactory; |
| using testing::TestUpdateRecipient; |
| using util::EncryptedMessageMaker; |
| using util::FakeValidatedClock; |
| using util::IncrementingSystemClock; |
| using util::TimeToDayIndex; |
| using util::TimeToHourId; |
| |
| 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; |
| |
| } // namespace |
| |
| class LoggerTest : public util::testing::TestWithFiles { |
| protected: |
| void SetUp() override { |
| SetUpFromMetrics(testing::all_report_types::kCobaltRegistryBase64, |
| testing::all_report_types::kExpectedAggregationParams); |
| } |
| |
| // Set up LoggerTest using a base64-encoded registry. |
| void SetUpFromMetrics(const std::string& registry_base64, |
| const ExpectedAggregationParams& expected_aggregation_params) { |
| MakeTestFolder(); |
| |
| // Create a mock clock which does not increment by default when called. Set |
| // the time to 10 years and 6 hours after the start of Unix time so that the |
| // start date of any aggregation window falls after the start of time. |
| start_time_ = |
| std::chrono::system_clock::time_point(std::chrono::seconds(10 * kYear + 6 * 3600)); |
| mock_clock_ = std::make_unique<IncrementingSystemClock>(std::chrono::system_clock::duration(0)); |
| mock_clock_->set_time(start_time_); |
| validated_clock_ = std::make_unique<FakeValidatedClock>(mock_clock_.get()); |
| validated_clock_->SetAccurate(true); |
| // Set the test local time converter to switch time zones 1 hour after `start_time_`. |
| // This should only affect metrics with the OTHER_TIME_ZONE TimeZonePolicy. |
| civil_time_converter_ = std::make_unique<util::FakeCivilTimeConverter>( |
| /*start_utc_offset=*/-8, /*start_isdst=*/false, /*end_utc_offset=*/-7, /*end_isdst=*/true, |
| /*threshold=*/start_time_ + util::kOneHour); |
| |
| expected_aggregation_params_ = expected_aggregation_params; |
| observation_store_ = std::make_unique<FakeObservationStore>(); |
| update_recipient_ = std::make_unique<TestUpdateRecipient>(); |
| observation_encrypter_ = EncryptedMessageMaker::MakeUnencrypted().Unwrap(); |
| observation_writer_ = std::make_unique<ObservationWriter>( |
| *observation_store_, update_recipient_.get(), observation_encrypter_.get()); |
| metadata_builder_ = std::make_unique<MetadataBuilder>(system_data_, validated_clock_.get(), |
| system_data_cache_path(), fs()); |
| metadata_builder_->SnapshotSystemData(); |
| encoder_ = std::make_unique<Encoder>(ClientSecret::GenerateNewSecret(), *metadata_builder_); |
| |
| CobaltConfig cfg = {.client_secret = system_data::ClientSecret::GenerateNewSecret()}; |
| |
| cfg.local_aggregation_backfill_days = 0; |
| cfg.local_aggregate_store_dir = test_folder() + "/local_aggregation_store_dir"; |
| |
| project_context_factory_ = GetTestProjectContextFactory(registry_base64); |
| |
| local_aggregation_ = std::make_unique<TestLocalAggregation>( |
| cfg, *project_context_factory_, system_data_, *metadata_builder_, fs(), |
| *observation_writer_, *civil_time_converter_); |
| internal_logger_ = std::make_unique<testing::FakeLogger>(); |
| internal_metrics_ = InternalMetrics::NewWithLogger(internal_logger_.get(), nullptr).Unwrap(); |
| |
| undated_event_manager_ = std::make_shared<UndatedEventManager>( |
| *encoder_, *local_aggregation_, *observation_writer_, system_data_, *civil_time_converter_); |
| |
| std::vector<uint32_t> fake_experiment_ids = {123456789, 987654321}; |
| logger_ = std::make_unique<Logger>( |
| GetTestProject(registry_base64), *encoder_, *local_aggregation_, *observation_writer_, |
| system_data_, validated_clock_.get(), *civil_time_converter_, undated_event_manager_, |
| fake_experiment_ids, true, internal_metrics_.get()); |
| } |
| |
| void TearDown() override { logger_.reset(); } |
| |
| // Returns the day index of the current day according to |mock_clock_|, in |
| // |time_zone|, without incrementing the clock. |
| uint32_t CurrentDayIndex(MetricDefinition::TimeZonePolicy time_zone) { |
| return TimeToDayIndex(std::chrono::system_clock::to_time_t(mock_clock_->peek_now()), time_zone); |
| } |
| |
| // Returns the hour ID of the current time according to |mock_clock_|, in |
| // |time_zone|, without incrementing the clock. |
| uint32_t CurrentHourId(MetricDefinition::TimeZonePolicy time_zone) { |
| return TimeToHourId(std::chrono::system_clock::to_time_t(mock_clock_->peek_now()), time_zone); |
| } |
| |
| // Clears the FakeObservationStore and resets 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; |
| } |
| |
| void GenerateAggregatedObservations(int day_delta) { |
| std::chrono::system_clock::time_point time = |
| mock_clock_->peek_now() + std::chrono::hours(util::kNumHoursPerDay * day_delta + 12); |
| local_aggregation_->GenerateAggregatedObservations(time); |
| } |
| |
| void GenerateHourlyAggregatedObservations(int hour_delta) { |
| std::chrono::system_clock::time_point time = |
| mock_clock_->peek_now() + std::chrono::hours(hour_delta); |
| local_aggregation_->GenerateAggregatedObservations(time); |
| } |
| |
| std::unique_ptr<Logger> logger_; |
| std::unique_ptr<testing::FakeLogger> internal_logger_; |
| std::unique_ptr<InternalMetrics> internal_metrics_; |
| std::unique_ptr<ObservationWriter> observation_writer_; |
| std::unique_ptr<FakeObservationStore> observation_store_; |
| std::chrono::system_clock::time_point start_time_; |
| std::unique_ptr<FakeValidatedClock> validated_clock_; |
| std::unique_ptr<util::FakeCivilTimeConverter> civil_time_converter_; |
| std::shared_ptr<UndatedEventManager> undated_event_manager_; |
| std::unique_ptr<TestUpdateRecipient> update_recipient_; |
| ExpectedAggregationParams expected_aggregation_params_; |
| |
| std::unique_ptr<MetadataBuilder> metadata_builder_; |
| std::unique_ptr<Encoder> encoder_; |
| std::unique_ptr<ProjectContextFactory> project_context_factory_; |
| std::unique_ptr<TestLocalAggregation> local_aggregation_; |
| std::unique_ptr<EncryptedMessageMaker> observation_encrypter_; |
| system_data::FakeSystemData system_data_; |
| std::unique_ptr<IncrementingSystemClock> mock_clock_; |
| }; |
| |
| TEST_F(LoggerTest, LogOccurrence) { |
| // Use no event codes when the metric has one dimension. Expect kOk. |
| EXPECT_EQ(StatusCode::OK, |
| logger_ |
| ->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 303, |
| std::vector<uint32_t>({})) |
| .error_code()); |
| |
| // Generate up until last hour |
| GenerateHourlyAggregatedObservations(-1); |
| ResetObservationStore(); |
| // Generate just for this hour |
| GenerateHourlyAggregatedObservations(1); |
| |
| std::vector<uint32_t> expected_report_ids = { |
| testing::all_report_types::kNewReadCacheHitsNewReadCacheHitCountsReportId, |
| testing::all_report_types::kNewReadCacheHitsNewReadCacheHitHistogramsReportId, |
| testing::all_report_types::kNewReadCacheHitsNewReadCacheHitStatsReportId}; |
| EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0, "", 303, *observation_store_, |
| update_recipient_.get())); |
| ResetObservationStore(); |
| |
| // Advance the clock to the next hour. |
| mock_clock_->increment_by(std::chrono::hours(1)); |
| |
| // Use two event codes when the metric has one dimension. Expect StatusCode::INVALID_ARGUMENT. |
| EXPECT_EQ(StatusCode::INVALID_ARGUMENT, |
| logger_ |
| ->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 303, |
| std::vector<uint32_t>({43, 44})) |
| .error_code()); |
| |
| // All good, expect OK. |
| EXPECT_EQ(StatusCode::OK, |
| logger_ |
| ->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 303, |
| std::vector<uint32_t>({43})) |
| .error_code()); |
| |
| // Generate just for the current (next) hour |
| GenerateHourlyAggregatedObservations(1); |
| |
| EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 43u, "", 303, *observation_store_, |
| update_recipient_.get())); |
| } |
| |
| TEST_F(LoggerTest, LogOccurrenceWithInvalidMetric) { |
| // Use no event codes when the metric has one dimension. Expect kOk. |
| EXPECT_NE(StatusCode::OK, logger_ |
| ->LogOccurrence(100000 /* A metric id that doesn't exist */, 303, |
| std::vector<uint32_t>({})) |
| .error_code()); |
| } |
| |
| TEST_F(LoggerTest, LogInteger) { |
| std::vector<uint32_t> expected_report_ids = { |
| testing::all_report_types::kNewLoginModuleFrameRateNewLoginModuleFrameRateAggregatedReportId, |
| testing::all_report_types::kNewLoginModuleFrameRateNewLoginModuleFrameRateHistogramReportId, |
| testing::all_report_types::kNewLoginModuleFrameRateNewLoginModuleFrameRatePerDeviceReportId}; |
| |
| // Use no event codes when the metric has one dimension. Expect StatusCode::OK. |
| ASSERT_EQ(StatusCode::OK, |
| logger_ |
| ->LogInteger(testing::all_report_types::kNewLoginModuleFrameRateMetricId, 5123, |
| std::vector<uint32_t>({})) |
| .error_code()); |
| // TODO(fxbug.dev/87105): once local_aggregation supports testing, enable this. |
| // EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0u, "", 5123, |
| // *observation_store_, update_recipient_.get())); |
| ResetObservationStore(); |
| |
| // Use two event codes when the metric has one dimension. Expect StatusCode::INVALID_ARGUMENT. |
| ASSERT_EQ(StatusCode::INVALID_ARGUMENT, |
| logger_ |
| ->LogInteger(testing::all_report_types::kNewLoginModuleFrameRateMetricId, 5123, |
| std::vector<uint32_t>({45, 46})) |
| .error_code()); |
| |
| // All good |
| ASSERT_EQ(StatusCode::OK, |
| logger_ |
| ->LogInteger(testing::all_report_types::kNewLoginModuleFrameRateMetricId, 5123, |
| std::vector<uint32_t>({45})) |
| .error_code()); |
| // TODO(fxbug.dev/87105): once local_aggregation supports testing, enable this. |
| // EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 45u, "", 5123, |
| // *observation_store_, update_recipient_.get())); |
| } |
| |
| TEST_F(LoggerTest, LogIntegerHistogram) { |
| std::vector<uint32_t> indices = {0, 1, 2, 3}; |
| std::vector<uint32_t> counts = {100, 101, 102, 103}; |
| auto histogram = testing::NewHistogram(indices, counts); |
| ASSERT_EQ(StatusCode::OK, |
| logger_ |
| ->LogIntegerHistogram(testing::all_report_types::kNewFileSystemWriteTimesMetricId, |
| std::move(histogram), std::vector<uint32_t>({47})) |
| .error_code()); |
| // TODO(fxbug.dev/87105): once local_aggregation supports testing, enable this. |
| // Observation observation; |
| // uint32_t expected_report_id = |
| // testing::all_report_types::kNewFileSystemWriteTimesNewFileSystemWriteTimesHistogramReportId; |
| // ASSERT_TRUE(FetchSingleObservation(&observation, expected_report_id, *observation_store_, |
| // update_recipient_.get())); |
| // ASSERT_TRUE(observation.has_histogram()); |
| // auto histogram_observation = observation.histogram(); |
| // EXPECT_EQ(47u, histogram_observation.event_code()); |
| // EXPECT_EQ(histogram_observation.component_name_hash().size(), 32u); |
| // EXPECT_EQ(static_cast<size_t>(histogram_observation.buckets_size()), indices.size()); |
| // for (auto i = 0u; i < indices.size(); i++) { |
| // const auto& bucket = histogram_observation.buckets(i); |
| // EXPECT_EQ(bucket.index(), indices[i]); |
| // EXPECT_EQ(bucket.count(), counts[i]); |
| // } |
| } |
| |
| TEST_F(LoggerTest, LogString) { |
| std::vector<uint32_t> expected_report_ids = { |
| testing::all_report_types::kNewComponentNewComponentHistogramReportId}; |
| |
| // Use a non-zero event code even though the metric does not have any |
| // metric dimensions defined. Expect kInvalidArgument. |
| ASSERT_EQ(StatusCode::INVALID_ARGUMENT, |
| logger_ |
| ->LogString(testing::all_report_types::kNewComponentMetricId, "component4", |
| std::vector<uint32_t>({44})) |
| .error_code()); |
| |
| // Use a vector of length two even though the metric does not have any |
| // metric dimensions defined. Expect kInvalidArgument. |
| ASSERT_EQ(StatusCode::INVALID_ARGUMENT, |
| logger_ |
| ->LogString(testing::all_report_types::kNewComponentMetricId, "component4", |
| std::vector<uint32_t>({0, 0})) |
| .error_code()); |
| |
| // Use an empty vector of event codes when the metric does not have any metric |
| // dimensions set. This is good. |
| ASSERT_EQ(StatusCode::OK, logger_ |
| ->LogString(testing::all_report_types::kNewComponentMetricId, |
| "component4", std::vector<uint32_t>({})) |
| .error_code()); |
| |
| // TODO(fxbug.dev/87105): once local_aggregation supports testing, enable this. |
| // EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0u, "component4", 4004, |
| // *observation_store_, update_recipient_.get())); |
| } |
| |
| TEST_F(LoggerTest, TestPausingLogging) { |
| ASSERT_EQ(internal_logger_->call_count(), 0); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| ASSERT_EQ(internal_logger_->call_count(), 1); |
| logger_->PauseInternalLogging(); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| ASSERT_EQ(internal_logger_->call_count(), 1); |
| logger_->ResumeInternalLogging(); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| ASSERT_EQ(internal_logger_->call_count(), 2); |
| } |
| |
| // Tests the events are not sent to the UndatedEventManager. |
| TEST_F(LoggerTest, AccurateClockEventsLogged) { |
| validated_clock_->SetAccurate(true); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that only an Observation is generated, no events are cached. |
| ASSERT_EQ(1, local_aggregation_->num_events_added()); |
| ASSERT_EQ(0, undated_event_manager_->NumSavedEvents()); |
| } |
| |
| // Tests the diversion of events to the UndatedEventManager. |
| TEST_F(LoggerTest, InaccurateClockEventsSaved) { |
| validated_clock_->SetAccurate(false); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that no immediate Observations were generated, and an event is cached. |
| ASSERT_EQ(0, local_aggregation_->num_events_added()); |
| ASSERT_EQ(1, undated_event_manager_->NumSavedEvents()); |
| } |
| |
| // Tests the diversion of events to the UndatedEventManager stops once the clock becomes accurate. |
| TEST_F(LoggerTest, InaccurateClockEventsSavedOnlyWhileClockIsInaccurate) { |
| validated_clock_->SetAccurate(false); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that no immediate Observations were generated, and an event is cached. |
| ASSERT_EQ(0, local_aggregation_->num_events_added()); |
| ASSERT_EQ(1, undated_event_manager_->NumSavedEvents()); |
| |
| // Clock becomes accurate. |
| validated_clock_->SetAccurate(true); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that now Observations are generated, the one event is still cached. |
| ASSERT_EQ(1, local_aggregation_->num_events_added()); |
| ASSERT_EQ(1, undated_event_manager_->NumSavedEvents()); |
| } |
| |
| // Tests the error when the UndatedEventManager was deleted early and the clock is invalid. |
| TEST_F(LoggerTest, AlreadyDeletedError) { |
| validated_clock_->SetAccurate(false); |
| // Delete the UndatedEventManager. |
| undated_event_manager_.reset(); |
| // Error is returned because the clock should become accurate if the UndatedEventManager is |
| // deleted. |
| ASSERT_EQ(StatusCode::FAILED_PRECONDITION, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that no immediate Observations were generated, and no events are cached. |
| ASSERT_EQ(0, local_aggregation_->num_events_added()); |
| } |
| |
| // Tests the race condition when the UndatedEventManager is deleted and the clock becomes valid. |
| TEST_F(LoggerTest, AlreadyDeletedRaceCondition) { |
| // Clock is initially invalid, but becomes valid on subsequent attempts. |
| validated_clock_->SetAccurate({false, true}); |
| // Delete the UndatedEventManager. |
| undated_event_manager_.reset(); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that the immediate Observation was generated. |
| ASSERT_EQ(1, local_aggregation_->num_events_added()); |
| } |
| |
| // Tests the UndatedEventManager forwarding events when it has been flushed before the call to |
| // Save(). |
| TEST_F(LoggerTest, AlreadyFlushedError) { |
| validated_clock_->SetAccurate(false); |
| IncrementingSystemClock system_clock(std::chrono::system_clock::duration(0)); |
| system_clock.set_time(std::chrono::system_clock::time_point(std::chrono::seconds(10 * kYear))); |
| undated_event_manager_->Flush(&system_clock, internal_metrics_.get()); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that one immediate Observation was generated, and no events are cached. |
| ASSERT_EQ(1, local_aggregation_->num_events_added()); |
| ASSERT_EQ(0, undated_event_manager_->NumSavedEvents()); |
| } |
| |
| // Tests the race condition when the UndatedEventManager is being flushed while the event is being |
| // logged. |
| TEST_F(LoggerTest, ClockBecomesAccurateRaceCondition) { |
| // Clock is initially invalid, but becomes valid on subsequent attempts. |
| validated_clock_->SetAccurate({false, true}); |
| IncrementingSystemClock system_clock(std::chrono::system_clock::duration(0)); |
| system_clock.set_time(std::chrono::system_clock::time_point(std::chrono::seconds(10 * kYear))); |
| undated_event_manager_->Flush(&system_clock, internal_metrics_.get()); |
| ASSERT_EQ(StatusCode::OK, |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42}) |
| .error_code()); |
| // Check that an immediate Observation is generated, and no events are cached after Flush. |
| ASSERT_EQ(1, local_aggregation_->num_events_added()); |
| ASSERT_EQ(0, undated_event_manager_->NumSavedEvents()); |
| } |
| |
| TEST_F(LoggerTest, LogInOtherTimeZone) { |
| // Pass an empty ExpectedAggregationParams to SetUpFromMetrics(), since we don't need to check |
| // against the params in this test. |
| ExpectedAggregationParams params; |
| SetUpFromMetrics(testing::cobalt_new::kCobaltRegistryBase64, params); |
| |
| // The day index of `start_time_` with respect to UTC. |
| // Since `start_time_` is 6 hours after midnight UTC, the day index of `start_time_` should be 1 |
| // less in the initial time zone of `civil_time_converter_` (UTC-8h). |
| const uint32_t kUtcStartDayIndex = util::TimePointToDayIndexUtc(start_time_); |
| |
| // Log an event at `start_time_`. |
| EXPECT_EQ(StatusCode::OK, logger_ |
| ->LogOccurrence(testing::cobalt_new::kOccurrenceOtherTzMetricId, 1, |
| std::vector<uint32_t>({})) |
| .error_code()); |
| |
| // Generate up until last hour and reset to make sure the store is clear. |
| GenerateHourlyAggregatedObservations(-1); |
| ResetObservationStore(); |
| // Generate just for the hour containing `start_time_`. |
| GenerateHourlyAggregatedObservations(1); |
| |
| ASSERT_EQ(observation_store_->metadata_received.size(), 1); |
| ObservationMetadata metadata = *observation_store_->metadata_received.at(0); |
| EXPECT_EQ(metadata.day_index(), kUtcStartDayIndex - 1); |
| |
| ResetObservationStore(); |
| |
| // Advance the clock by 7 hours and log an event. The time zone of `civil_time_converter_` is now |
| // UTC-7h (with DST), so we should now be logging events with day index equal to |
| // `kStartDayIndexUtc`. |
| mock_clock_->increment_by(std::chrono::hours(7)); |
| EXPECT_EQ(StatusCode::OK, logger_ |
| ->LogOccurrence(testing::cobalt_new::kOccurrenceOtherTzMetricId, 1, |
| std::vector<uint32_t>({})) |
| .error_code()); |
| |
| // Generate for the hour containing the current time. |
| GenerateHourlyAggregatedObservations(1); |
| |
| ASSERT_EQ(observation_store_->metadata_received.size(), 1); |
| metadata = *observation_store_->metadata_received.at(0); |
| EXPECT_EQ(metadata.day_index(), kUtcStartDayIndex); |
| } |
| |
| class NoValidatedClockLoggerTest : 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().Unwrap(); |
| observation_writer_ = std::make_unique<ObservationWriter>( |
| *observation_store_, update_recipient_.get(), observation_encrypter_.get()); |
| metadata_builder_ = |
| std::make_unique<MetadataBuilder>(system_data_, system_data_cache_path(), fs()); |
| encoder_ = std::make_unique<Encoder>(ClientSecret::GenerateNewSecret(), *metadata_builder_); |
| CobaltConfig cfg = {.client_secret = system_data::ClientSecret::GenerateNewSecret()}; |
| civil_time_converter_ = std::make_unique<util::UtcTimeConverter>(); |
| |
| cfg.local_aggregation_backfill_days = 0; |
| cfg.local_aggregate_store_dir = test_folder() + "/local_aggregation_store_dir"; |
| |
| project_context_factory_ = |
| GetTestProjectContextFactory(testing::all_report_types::kCobaltRegistryBase64); |
| |
| local_aggregation_ = std::make_unique<TestLocalAggregation>( |
| cfg, *project_context_factory_, system_data_, *metadata_builder_, fs(), |
| *observation_writer_, *civil_time_converter_); |
| |
| internal_logger_ = std::make_unique<testing::FakeLogger>(); |
| |
| std::vector<uint32_t> empty_experiment_ids = {}; |
| logger_ = std::make_unique<Logger>( |
| GetTestProject(testing::all_report_types::kCobaltRegistryBase64), *encoder_, |
| *local_aggregation_, *observation_writer_, system_data_, *civil_time_converter_, |
| empty_experiment_ids, internal_logger_.get()); |
| } |
| |
| void TearDown() override { logger_.reset(); } |
| |
| std::unique_ptr<Logger> logger_; |
| std::unique_ptr<testing::FakeLogger> internal_logger_; |
| std::unique_ptr<ObservationWriter> observation_writer_; |
| std::unique_ptr<FakeObservationStore> observation_store_; |
| std::unique_ptr<TestUpdateRecipient> update_recipient_; |
| std::unique_ptr<TestLocalAggregation> local_aggregation_; |
| |
| private: |
| std::unique_ptr<util::UtcTimeConverter> civil_time_converter_; |
| std::unique_ptr<MetadataBuilder> metadata_builder_; |
| std::unique_ptr<Encoder> encoder_; |
| std::unique_ptr<ProjectContextFactory> project_context_factory_; |
| std::unique_ptr<EncryptedMessageMaker> observation_encrypter_; |
| system_data::FakeSystemData system_data_; |
| }; |
| |
| // Tests the events are logged to the event loggers. |
| TEST_F(NoValidatedClockLoggerTest, AccurateClockEventsLogged) { |
| ASSERT_EQ(Status::OkStatus(), |
| logger_->LogOccurrence(testing::all_report_types::kNewReadCacheHitsMetricId, 1, {42})); |
| // Check that an Observation is generated. |
| ASSERT_EQ(1, local_aggregation_->num_events_added()); |
| // Verify a reasonable day index is generated by the logger's system clock. |
| ASSERT_GT(local_aggregation_->last_timestamp(), |
| std::chrono::system_clock::now() - std::chrono::minutes(1)); |
| } |
| |
| } // namespace cobalt::logger |