blob: efcc6e67d1cfb4346986fe1ede7532f05b0a3a05 [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/algorithms/rappor/rappor_encoder.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/hash.h"
#include "src/lib/util/testing/test_with_files.h"
#include "src/local_aggregation/event_aggregator_mgr.h"
#include "src/local_aggregation/test_utils/test_event_aggregator_mgr.h"
#include "src/local_aggregation_1_1/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/test_registries/cobalt1.1_migration_registry.cb.h"
#include "src/logger/testing_constants.h"
#include "src/pb/metadata_builder.h"
#include "src/pb/observation.pb.h"
#include "src/registry/packed_event_codes.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::EventAggregatorManager;
using local_aggregation::LocalAggregation;
using local_aggregation::TestEventAggregatorManager;
using system_data::ClientSecret;
using testing::CheckNumericEventObservations;
using testing::ExpectedAggregationParams;
using testing::ExpectedPerDeviceNumericObservations;
using testing::ExpectedReportParticipationObservations;
using testing::ExpectedUniqueActivesObservations;
using testing::FakeObservationStore;
using testing::FetchObservations;
using testing::FetchSingleObservation;
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();
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_proto_store_path = aggregate_store_path();
cfg.obs_history_proto_store_path = obs_history_path();
cfg.local_aggregate_store_dir = test_folder() + "/local_aggregation_store_dir";
project_context_factory_ = GetTestProjectContextFactory(registry_base64);
event_aggregator_mgr_ = std::make_unique<TestEventAggregatorManager>(
cfg, fs(), *encoder_, *observation_writer_, *metadata_builder_);
local_aggregation_ = std::make_unique<LocalAggregation>(
cfg, project_context_factory_.get(), 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);
undated_event_manager_ = std::make_shared<UndatedEventManager>(
*encoder_, event_aggregator_mgr_->GetEventAggregator(), *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_, event_aggregator_mgr_->GetEventAggregator(),
*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 {
event_aggregator_mgr_.reset();
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<TestEventAggregatorManager> event_aggregator_mgr_;
std::unique_ptr<LocalAggregation> local_aggregation_;
std::unique_ptr<EncryptedMessageMaker> observation_encrypter_;
system_data::FakeSystemData system_data_;
std::unique_ptr<IncrementingSystemClock> mock_clock_;
};
// Creates a Logger for a ProjectContext where each of the metrics
// has a report of type PER_DEVICE_NUMERIC_STATS.
class PerDeviceNumericLoggerTest : public LoggerTest {
protected:
void SetUp() override {
SetUpFromMetrics(testing::per_device_numeric_stats::kCobaltRegistryBase64,
testing::per_device_numeric_stats::kExpectedAggregationParams);
}
};
// Tests the method LogEvent().
TEST_F(LoggerTest, LogEvent) {
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
Observation observation;
uint32_t expected_report_id = testing::all_report_types::kErrorOccurredErrorCountsByCodeReportId;
ASSERT_TRUE(FetchSingleObservation(&observation, expected_report_id, *observation_store_,
update_recipient_.get()));
ASSERT_TRUE(observation.has_basic_rappor());
EXPECT_FALSE(observation.basic_rappor().data().empty());
}
// Tests the method LogEventCount().
TEST_F(LoggerTest, LogEventCount) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kReadCacheHitsReadCacheHitCountsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitHistogramsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitStatsReportId};
ASSERT_EQ(StatusCode::OK, logger_
->LogEventCount(testing::all_report_types::kReadCacheHitsMetricId,
{43}, "component2", 1, 303)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 43u, "component2", 303,
*observation_store_, update_recipient_.get()));
}
// Tests the method LogEventCount() can accept large numbers correctly.
TEST_F(LoggerTest, LogEventCountWithLargeCounts) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kReadCacheHitsReadCacheHitCountsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitHistogramsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitStatsReportId};
int64_t large_value = 3'147'483'647;
ASSERT_EQ(StatusCode::OK, logger_
->LogEventCount(testing::all_report_types::kReadCacheHitsMetricId,
{43}, "component2", 1, large_value)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 43u, "component2", large_value,
*observation_store_, update_recipient_.get()));
}
// Tests the version of the method LogEventCount() that accepts a vector of
// event codes.
TEST_F(LoggerTest, LogEventCountMultiDimension) {
// Use no event codes when the metric has one dimension. Expect kOk.
EXPECT_EQ(StatusCode::OK, logger_
->LogEventCount(testing::all_report_types::kReadCacheHitsMetricId,
std::vector<uint32_t>({}), "", 0, 303)
.error_code());
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kReadCacheHitsReadCacheHitCountsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitHistogramsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitStatsReportId};
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0, "", 303, *observation_store_,
update_recipient_.get()));
ResetObservationStore();
// Use two event codes when the metric has one dimension. Expect
// StatusCode::INVALID_ARGUMENT.
EXPECT_EQ(StatusCode::INVALID_ARGUMENT,
logger_
->LogEventCount(testing::all_report_types::kReadCacheHitsMetricId,
std::vector<uint32_t>({43, 44}), "", 0, 303)
.error_code());
// All good, expect OK.
EXPECT_EQ(StatusCode::OK, logger_
->LogEventCount(testing::all_report_types::kReadCacheHitsMetricId,
std::vector<uint32_t>({43}), "", 0, 303)
.error_code());
expected_report_ids = {testing::all_report_types::kReadCacheHitsReadCacheHitCountsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitHistogramsReportId,
testing::all_report_types::kReadCacheHitsReadCacheHitStatsReportId};
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 43u, "", 303, *observation_store_,
update_recipient_.get()));
}
// Tests the method LogElapsedTime().
TEST_F(LoggerTest, LogElapsedTime) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kModuleLoadTimeModuleLoadTimeAggregatedReportId,
testing::all_report_types::kModuleLoadTimeModuleLoadTimeHistogramReportId};
// Use a zero event code when the metric does not have any metric dimensions
// set. This is OK by convention. The zero will be ignored.
ASSERT_EQ(StatusCode::OK, logger_
->LogElapsedTime(testing::all_report_types::kModuleLoadTimeMetricId,
{0}, "component4", 4004)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0u, "component4", 4004,
*observation_store_, update_recipient_.get()));
}
// Tests the version of the method LogElapsedTime() that accepts a vector of
// event codes.
TEST_F(LoggerTest, LogElapsedTimeMultiDimension) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kModuleLoadTimeModuleLoadTimeAggregatedReportId,
testing::all_report_types::kModuleLoadTimeModuleLoadTimeHistogramReportId};
// 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_
->LogElapsedTime(testing::all_report_types::kModuleLoadTimeMetricId,
std::vector<uint32_t>({44}), "component4", 4004)
.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_
->LogElapsedTime(testing::all_report_types::kModuleLoadTimeMetricId,
std::vector<uint32_t>({0, 0}), "component4", 4004)
.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_
->LogElapsedTime(testing::all_report_types::kModuleLoadTimeMetricId,
std::vector<uint32_t>({}), "component4", 4004)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0u, "component4", 4004,
*observation_store_, update_recipient_.get()));
}
// Tests the method LogFrameRate().
TEST_F(LoggerTest, LogFrameRate) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kLoginModuleFrameRateLoginModuleFrameRateAggregatedReportId,
testing::all_report_types::kLoginModuleFrameRateLoginModuleFrameRateHistogramReportId};
ASSERT_EQ(StatusCode::OK,
logger_
->LogFrameRate(testing::all_report_types::kLoginModuleFrameRateMetricId, {45},
"component5", 5.123)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 45u, "component5", 5123,
*observation_store_, update_recipient_.get()));
}
// Tests the version of the method LogFrameRate() that accepts a vector of
// event codes.
TEST_F(LoggerTest, LogFrameRateMultiDimension) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kLoginModuleFrameRateLoginModuleFrameRateAggregatedReportId,
testing::all_report_types::kLoginModuleFrameRateLoginModuleFrameRateHistogramReportId};
// Use no event codes when the metric has one dimension. Expect StatusCode::OK.
ASSERT_EQ(StatusCode::OK,
logger_
->LogFrameRate(testing::all_report_types::kLoginModuleFrameRateMetricId,
std::vector<uint32_t>({}), "", 5.123)
.error_code());
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_
->LogFrameRate(testing::all_report_types::kLoginModuleFrameRateMetricId,
std::vector<uint32_t>({45, 46}), "", 5.123)
.error_code());
// All good
ASSERT_EQ(StatusCode::OK,
logger_
->LogFrameRate(testing::all_report_types::kLoginModuleFrameRateMetricId,
std::vector<uint32_t>({45}), "", 5.123)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 45u, "", 5123, *observation_store_,
update_recipient_.get()));
}
// Tests the method LogMemoryUsage().
TEST_F(LoggerTest, LogMemoryUsage) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kLedgerMemoryUsageLedgerMemoryUsageAggregatedReportId,
testing::all_report_types::kLedgerMemoryUsageLedgerMemoryUsageHistogramReportId};
// The simple version of LogMemoryUsage() works, even though the metric has 2 dimensions.
ASSERT_EQ(StatusCode::OK,
logger_
->LogMemoryUsage(testing::all_report_types::kLedgerMemoryUsageMetricId, {46},
"component6", 606)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 46, "component6", 606,
*observation_store_, update_recipient_.get()));
}
// Tests the version of the method LogMemoryUsage() that accepts a vector of
// event codes.
TEST_F(LoggerTest, LogMemoryUsageMultiDimension) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kLedgerMemoryUsageLedgerMemoryUsageAggregatedReportId,
testing::all_report_types::kLedgerMemoryUsageLedgerMemoryUsageHistogramReportId};
// Use no event codes when the metric has two dimension. Expect StatusCode::OK.
ASSERT_EQ(StatusCode::OK,
logger_
->LogMemoryUsage(testing::all_report_types::kLedgerMemoryUsageMetricId,
std::vector<uint32_t>({}), "component6", 606)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0, "component6", 606,
*observation_store_, update_recipient_.get()));
ResetObservationStore();
// Use one event code when the metric has two dimension. Expect StatusCode::OK.
ASSERT_EQ(StatusCode::OK,
logger_
->LogMemoryUsage(testing::all_report_types::kLedgerMemoryUsageMetricId,
std::vector<uint32_t>({45}), "component6", 606)
.error_code());
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 45, "component6", 606,
*observation_store_, update_recipient_.get()));
ResetObservationStore();
// Use three event codes when the metric has two dimension. Expect
// StatusCode::INVALID_ARGUMENT.
ASSERT_EQ(StatusCode::INVALID_ARGUMENT,
logger_
->LogMemoryUsage(testing::all_report_types::kLedgerMemoryUsageMetricId,
std::vector<uint32_t>({45, 46, 47}), "component6", 606)
.error_code());
// All good
ASSERT_EQ(StatusCode::OK,
logger_
->LogMemoryUsage(testing::all_report_types::kLedgerMemoryUsageMetricId,
std::vector<uint32_t>({1, 46}), "component6", 606)
.error_code());
uint64_t expected_packed_event_code = (1) | (46 << 10);
EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, expected_packed_event_code,
"component6", 606, *observation_store_,
update_recipient_.get()));
}
// Tests the method LogIntHistogram().
TEST_F(LoggerTest, LogIntHistogram) {
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_
->LogIntHistogram(testing::all_report_types::kFileSystemWriteTimesMetricId, {47},
"component7", std::move(histogram))
.error_code());
Observation observation;
uint32_t expected_report_id =
testing::all_report_types::kFileSystemWriteTimesFileSystemWriteTimesHistogramReportId;
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 (size_t i = 0; i < indices.size(); i++) {
const auto& bucket = histogram_observation.buckets(static_cast<int>(i));
EXPECT_EQ(bucket.index(), indices[i]);
EXPECT_EQ(bucket.count(), counts[i]);
}
}
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_1_1 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_1_1 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_1_1 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_1_1 supports testing, enable this.
// EXPECT_TRUE(CheckNumericEventObservations(expected_report_ids, 0u, "component4", 4004,
// *observation_store_, update_recipient_.get()));
}
// Tests the method LogCustomEvent().
TEST_F(LoggerTest, LogCustomEvent) {
CustomDimensionValue module_value, number_value;
module_value.set_string_value("gmail");
number_value.set_int_value(3);
std::vector<std::string> dimension_names = {"module", "number"};
std::vector<CustomDimensionValue> values = {module_value, number_value};
auto custom_event = testing::NewCustomEvent(dimension_names, values);
ASSERT_EQ(StatusCode::OK, logger_
->LogCustomEvent(testing::all_report_types::kModuleInstallsMetricId,
std::move(custom_event))
.error_code());
Observation observation;
uint32_t expected_report_id =
testing::all_report_types::kModuleInstallsModuleInstallsDetailedDataReportId;
ASSERT_TRUE(FetchSingleObservation(&observation, expected_report_id, *observation_store_,
update_recipient_.get()));
ASSERT_TRUE(observation.has_custom());
const CustomObservation& custom_observation = observation.custom();
for (auto i = 0u; i < values.size(); i++) {
auto obs_dimension = custom_observation.values().at(dimension_names[i]);
#ifdef PROTO_LITE
#else
EXPECT_TRUE(MessageDifferencer::Equals(obs_dimension, values[i]));
#endif
}
}
// Tests the method LogSerializedCustomEvent().
TEST_F(LoggerTest, LogSerializedCustomEvent) {
std::string serialized_proto = "serialized proto";
auto serialized_proto_ptr = std::make_unique<std::string>(serialized_proto);
ASSERT_EQ(StatusCode::OK,
logger_
->LogSerializedCustomEvent(testing::all_report_types::kModuleInstallsMetricId,
std::move(serialized_proto_ptr))
.error_code());
Observation observation;
uint32_t expected_report_id =
testing::all_report_types::kModuleInstallsModuleInstallsDetailedDataReportId;
ASSERT_TRUE(FetchSingleObservation(&observation, expected_report_id, *observation_store_,
update_recipient_.get()));
ASSERT_TRUE(observation.has_custom());
EXPECT_EQ(observation.custom().serialized_proto(), serialized_proto);
}
TEST_F(LoggerTest, ReplaceEventOccurredWithOccurrence) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
// Use no event codes when the metric has one dimension. Expect kOk.
EXPECT_EQ(
StatusCode::OK,
logger_
->LogEvent(testing::cobalt_1_1_migration::kEventOccurredOldMetricId,
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green)
.error_code());
// Generate up until today
GenerateAggregatedObservations(1);
ResetObservationStore();
// Generate just for tomorrow
GenerateAggregatedObservations(2);
std::vector<uint32_t> expected_report_ids = {
testing::cobalt_1_1_migration::kEventOccuredNewEventOccurredNew7DayActivesReportId,
};
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids,
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green, "", 1,
*observation_store_, update_recipient_.get()));
}
TEST_F(LoggerTest, ReplaceEventCountWithOccurrence) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
// Use no event codes when the metric has one dimension. Expect kOk.
EXPECT_EQ(StatusCode::OK,
logger_
->LogEventCount(
testing::cobalt_1_1_migration::kEventCountMetricId,
std::vector<uint32_t>{
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green},
"", 0, 303)
.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::cobalt_1_1_migration::kEventCountNewEventCountNewNumericStatsReportId,
};
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids,
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green, "", 303,
*observation_store_, update_recipient_.get()));
}
TEST_F(LoggerTest, ReplaceEventCountWithInteger) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
// Use no event codes when the metric has one dimension. Expect kOk.
EXPECT_EQ(StatusCode::OK,
logger_
->LogEventCount(
testing::cobalt_1_1_migration::kBytesUploadedMetricId,
std::vector<uint32_t>{
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green},
"", 0, 303)
.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::cobalt_1_1_migration::kBytesUploadedNewNumericStatsReportId,
};
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids,
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green, "", 303,
*observation_store_, update_recipient_.get()));
}
TEST_F(LoggerTest, ReplaceEventCountWithString) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
// Use no event codes when the metric has one dimension. Expect kOk.
EXPECT_EQ(StatusCode::OK,
logger_
->LogEventCount(
testing::cobalt_1_1_migration::kEventComponentCountMetricId,
std::vector<uint32_t>{
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green},
"string_component", 0, 303)
.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::cobalt_1_1_migration::kEventComponentCountNewStringCounstReportId};
// Check that no immediate Observations were generated.
std::vector<Observation> observations(1);
ASSERT_TRUE(FetchObservations(&observations, expected_report_ids, *observation_store_,
update_recipient_.get()));
ASSERT_EQ(observations.at(0).observation_type_case(),
Observation::ObservationTypeCase::kStringHistogram);
const StringHistogramObservation& obs = observations.at(0).string_histogram();
ASSERT_EQ(obs.string_hashes_size(), 1);
EXPECT_EQ(obs.string_hashes(0), util::FarmhashFingerprint("string_component"));
ASSERT_EQ(obs.string_histograms_size(), 1);
const IndexHistogram& hist = obs.string_histograms(0);
EXPECT_EQ(hist.event_codes(0),
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green);
ASSERT_EQ(hist.bucket_indices_size(), 1);
ASSERT_EQ(hist.bucket_counts_size(), 1);
EXPECT_EQ(hist.bucket_indices(0), 0);
EXPECT_EQ(hist.bucket_counts(0), 303);
}
TEST_F(LoggerTest, ReplaceElapsedTimeWithInteger) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
EXPECT_EQ(StatusCode::OK,
logger_
->LogElapsedTime(
testing::cobalt_1_1_migration::kElapsedTimeMetricId,
std::vector<uint32_t>{
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green},
"", 1023)
.error_code());
// Generate up until today
GenerateAggregatedObservations(1);
ResetObservationStore();
// Generate just for tomorrow
GenerateAggregatedObservations(2);
std::vector<uint32_t> expected_report_ids = {
testing::cobalt_1_1_migration::kNewElapsedTimeNumericStatsReportId,
};
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids,
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green, "", 1023,
*observation_store_, update_recipient_.get()));
}
TEST_F(LoggerTest, ReplaceFrameRateWithInteger) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
EXPECT_EQ(StatusCode::OK,
logger_
->LogFrameRate(
testing::cobalt_1_1_migration::kFrameRateMetricId,
std::vector<uint32_t>{
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green},
"", 59)
.error_code());
// Generate up until today
GenerateAggregatedObservations(1);
ResetObservationStore();
// Generate just for tomorrow
GenerateAggregatedObservations(2);
std::vector<uint32_t> expected_report_ids = {
testing::cobalt_1_1_migration::kNewFrameRateNumericStatsReportId,
};
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids,
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green, "", 59000,
*observation_store_, update_recipient_.get()));
}
TEST_F(LoggerTest, ReplaceMemoryUsageWithInteger) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
EXPECT_EQ(StatusCode::OK,
logger_
->LogMemoryUsage(
testing::cobalt_1_1_migration::kMemoryUsageMetricId,
std::vector<uint32_t>{
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green},
"", 59)
.error_code());
// Generate up until today
GenerateAggregatedObservations(1);
ResetObservationStore();
// Generate just for tomorrow
GenerateAggregatedObservations(2);
std::vector<uint32_t> expected_report_ids = {
testing::cobalt_1_1_migration::kNewMemoryUsageNumericStatsReportId,
};
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids,
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green, "", 59,
*observation_store_, update_recipient_.get()));
}
TEST_F(LoggerTest, ReplaceIntHistogramWithIntegerHistogram) {
SetUpFromMetrics(testing::cobalt_1_1_migration::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
std::vector<uint32_t> indices = {0, 1, 2, 3};
std::vector<uint32_t> counts = {100, 101, 102, 103};
auto histogram = testing::NewHistogram(indices, counts);
EXPECT_EQ(StatusCode::OK,
logger_
->LogIntHistogram(
testing::cobalt_1_1_migration::kIntHistogramMetricId,
std::vector<uint32_t>{
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green},
"", std::move(histogram))
.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::cobalt_1_1_migration::kIntegerHistogramFleetwideReportId,
};
// Check that no immediate Observations were generated.
std::vector<Observation> observations(1);
ASSERT_TRUE(FetchObservations(&observations, expected_report_ids, *observation_store_,
update_recipient_.get()));
ASSERT_EQ(observations.at(0).observation_type_case(),
Observation::ObservationTypeCase::kIndexHistogram);
const IndexHistogramObservation& obs = observations.at(0).index_histogram();
ASSERT_EQ(obs.index_histograms_size(), 1);
const IndexHistogram& hist = obs.index_histograms(0);
EXPECT_EQ(hist.event_codes(0),
testing::cobalt_1_1_migration::EventOccurredOldMetricDimensionColor::Green);
ASSERT_EQ(hist.bucket_indices_size(), 4);
ASSERT_EQ(hist.bucket_counts_size(), 4);
for (int i = 0; i < indices.size(); i++) {
EXPECT_EQ(hist.bucket_counts(i), counts[hist.bucket_indices(i)]);
}
}
// Tests that the expected number of locally aggregated Observations are
// generated when multiple Events of different types have been logged for
// locally aggregated reports.
TEST_F(LoggerTest, CheckNumAggregatedObsMultipleEvents) {
auto expected_params = expected_aggregation_params_;
// Log 2 occurrences of event code 0 for the DeviceBoots metric, which has 1
// locally aggregated report and no immediate reports.
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kDeviceBootsMetricId, 0).error_code());
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kDeviceBootsMetricId, 0).error_code());
// Log 2 occurrences of distinct event codes for the FeaturesActive metric,
// which has 1 locally aggregated report and no immediate reports.
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kFeaturesActiveMetricId, 0).error_code());
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kFeaturesActiveMetricId, 1).error_code());
// Log 2 event counts for event code 0, for distinct components, for the
// SettingsChanged metric. This metric has 1 locally aggregated report and no
// immediate reports.
ASSERT_EQ(StatusCode::OK, logger_
->LogEventCount(testing::all_report_types::kSettingsChangedMetricId,
{0}, "component_A", 0, 10)
.error_code());
ASSERT_EQ(StatusCode::OK, logger_
->LogEventCount(testing::all_report_types::kSettingsChangedMetricId,
{0}, "component_B", 0, 15)
.error_code());
// Check that no immediate Observations were generated.
std::vector<Observation> immediate_observations(0);
std::vector<uint32_t> expected_immediate_report_ids = {};
ASSERT_TRUE(FetchObservations(&immediate_observations, expected_immediate_report_ids,
*observation_store_, update_recipient_.get()));
// Generate locally aggregated observations for the current day index.
ASSERT_EQ(StatusCode::OK,
event_aggregator_mgr_->GenerateObservations(CurrentDayIndex(MetricDefinition::UTC))
.error_code());
// Update |expected_aggregation_params_| to account for the events logged by
// this test. In addition to the initial values, we expect 1 Observation for
// the SettingsChanged_PerDeviceCount report, for each of the 2 window sizes
// of the report, for each of the 2 components appearing in logged events.
expected_aggregation_params_.daily_num_obs += 4;
expected_aggregation_params_
.num_obs_per_report[testing::all_report_types::kSettingsChangedMetricReportId] += 4;
// Check that the expected numbers of aggregated observations were
// generated.
std::vector<Observation> aggregated_observations;
EXPECT_TRUE(FetchAggregatedObservations(&aggregated_observations, expected_aggregation_params_,
*observation_store_, update_recipient_.get()));
}
TEST_F(LoggerTest, TestPausingLogging) {
ASSERT_EQ(internal_logger_->call_count(), 0);
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
ASSERT_EQ(internal_logger_->call_count(), 2);
logger_->PauseInternalLogging();
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
ASSERT_EQ(internal_logger_->call_count(), 2);
logger_->ResumeInternalLogging();
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
ASSERT_EQ(internal_logger_->call_count(), 4);
}
// Tests the events are not sent to the UndatedEventManager.
TEST_F(LoggerTest, AccurateClockEventsLogged) {
validated_clock_->SetAccurate(true);
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that only an Observation is generated, no events are cached.
CHECK_EQ(1, observation_store_->num_observations_added());
CHECK_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_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that no immediate Observations were generated, and an event is cached.
CHECK_EQ(0, observation_store_->num_observations_added());
CHECK_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_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that no immediate Observations were generated, and an event is cached.
CHECK_EQ(0, observation_store_->num_observations_added());
CHECK_EQ(1, undated_event_manager_->NumSavedEvents());
// Clock becomes accurate.
validated_clock_->SetAccurate(true);
ASSERT_EQ(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that now Observations are generated, the one event is still cached.
CHECK_EQ(1, observation_store_->num_observations_added());
CHECK_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_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that no immediate Observations were generated, and no events are cached.
CHECK_EQ(0, observation_store_->num_observations_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_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that the immediate Observation was generated.
CHECK_EQ(1, observation_store_->num_observations_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_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that one immediate Observation was generated, and no events are cached.
CHECK_EQ(1, observation_store_->num_observations_added());
CHECK_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_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that an immediate Observation is generated, and no events are cached after Flush.
CHECK_EQ(1, observation_store_->num_observations_added());
CHECK_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();
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_proto_store_path = aggregate_store_path();
cfg.obs_history_proto_store_path = obs_history_path();
project_context_factory_ =
GetTestProjectContextFactory(testing::all_report_types::kCobaltRegistryBase64);
event_aggregator_mgr_ = std::make_unique<TestEventAggregatorManager>(
cfg, fs(), *encoder_, *observation_writer_, *metadata_builder_);
local_aggregation_ = std::make_unique<LocalAggregation>(
cfg, project_context_factory_.get(), 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_,
event_aggregator_mgr_->GetEventAggregator(), *local_aggregation_, *observation_writer_,
system_data_, *civil_time_converter_, empty_experiment_ids, internal_logger_.get());
}
void TearDown() override {
event_aggregator_mgr_.reset();
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_;
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<EventAggregatorManager> event_aggregator_mgr_;
std::unique_ptr<LocalAggregation> local_aggregation_;
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(StatusCode::OK,
logger_->LogEvent(testing::all_report_types::kErrorOccurredMetricId, 42).error_code());
// Check that an Observation is generated.
CHECK_EQ(1, observation_store_->num_observations_added());
// Verify a reasonable day index is generated by the logger's system clock.
CHECK_GT(observation_store_->metadata_received[0]->day_index(), 18000);
}
} // namespace cobalt::logger