blob: d86c65294672be5b0512470a44acba08fd1ecb98 [file] [log] [blame]
// 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