blob: f9fac51b4b6806fc3ad14a3c7ca16671e3a3caad [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 "logger/logger.h"
#include <google/protobuf/text_format.h>
#include <google/protobuf/util/message_differencer.h>
#include <chrono>
#include <memory>
#include <string>
#include "./gtest.h"
#include "./observation.pb.h"
#include "./observation2.pb.h"
#include "algorithms/rappor/rappor_encoder.h"
#include "config/encodings.pb.h"
#include "encoder/client_secret.h"
#include "encoder/encoder.h"
#include "logger/encoder.h"
#include "logger/event_aggregator.h"
#include "logger/logger_test_utils.h"
#include "logger/project_context.h"
#include "logger/status.h"
#include "logger/testing_constants.h"
#include "util/clock.h"
#include "util/datetime_util.h"
#include "util/encrypted_message_util.h"
using ::google::protobuf::RepeatedPtrField;
using ::google::protobuf::util::MessageDifferencer;
namespace cobalt {
using config::ProjectConfigs;
using encoder::ClientSecret;
using encoder::SystemDataInterface;
using rappor::BasicRapporEncoder;
using util::EncryptedMessageMaker;
using util::IncrementingClock;
using util::MessageDecrypter;
using util::TimeToDayIndex;
namespace logger {
using testing::CheckNumericEventObservations;
using testing::CheckUniqueActivesObservations;
using testing::ExpectedAggregationParams;
using testing::ExpectedPerDeviceCountObservations;
using testing::ExpectedReportParticipationObservations;
using testing::ExpectedUniqueActivesObservations;
using testing::FakeObservationStore;
using testing::FetchAggregatedObservations;
using testing::FetchObservations;
using testing::FetchSingleObservation;
using testing::GetTestProject;
using testing::MakeNullExpectedUniqueActivesObservations;
using testing::MockConsistentProtoStore;
using testing::TestUpdateRecipient;
namespace {
// Number of seconds in a day
const int kDay = 60 * 60 * 24;
// Number of seconds in an ideal year
const int kYear = kDay * 365;
// Filenames for constructors of ConsistentProtoStores
static const char kAggregateStoreFilename[] = "local_aggregate_store_backup";
static const char kObsHistoryFilename[] = "obs_history_backup";
HistogramPtr NewHistogram(std::vector<uint32_t> indices,
std::vector<uint32_t> counts) {
CHECK(indices.size() == counts.size());
HistogramPtr histogram =
std::make_unique<RepeatedPtrField<HistogramBucket>>();
for (auto i = 0u; i < indices.size(); i++) {
auto* bucket = histogram->Add();
bucket->set_index(indices[i]);
bucket->set_count(counts[i]);
}
return histogram;
}
EventValuesPtr NewCustomEvent(std::vector<std::string> dimension_names,
std::vector<CustomDimensionValue> values) {
CHECK(dimension_names.size() == values.size());
EventValuesPtr custom_event = std::make_unique<
google::protobuf::Map<std::string, CustomDimensionValue>>();
for (auto i = 0u; i < values.size(); i++) {
(*custom_event)[dimension_names[i]] = values[i];
}
return custom_event;
}
} // namespace
class LoggerTest : public ::testing::Test {
protected:
void SetUp() {
SetUpFromMetrics(testing::all_report_types::kCobaltRegistryBase64,
testing::all_report_types::kExpectedAggregationParams);
}
// Set up LoggerTest using a base64-encoded registry. The argument
// |registry_var_name| should be the name of a variable to which the
// base64-encoded registry is assigned.
void SetUpFromMetrics(
const std::string& registry_var_name,
const ExpectedAggregationParams& expected_aggregation_params) {
expected_aggregation_params_ = expected_aggregation_params;
observation_store_.reset(new FakeObservationStore);
update_recipient_.reset(new TestUpdateRecipient);
observation_encrypter_ = EncryptedMessageMaker::MakeUnencrypted();
observation_writer_.reset(
new ObservationWriter(observation_store_.get(), update_recipient_.get(),
observation_encrypter_.get()));
encoder_.reset(
new Encoder(ClientSecret::GenerateNewSecret(), system_data_.get()));
local_aggregate_proto_store_.reset(
new MockConsistentProtoStore(kAggregateStoreFilename));
obs_history_proto_store_.reset(
new MockConsistentProtoStore(kObsHistoryFilename));
event_aggregator_.reset(new EventAggregator(
encoder_.get(), observation_writer_.get(),
local_aggregate_proto_store_.get(), obs_history_proto_store_.get()));
logger_.reset(new Logger(GetTestProject(registry_var_name), encoder_.get(),
event_aggregator_.get(),
observation_writer_.get()));
// Create a mock clock which does not increment by default when called.
// Set the time to 1 year after the start of Unix time so that the start
// date of any aggregation window falls after the start of time.
mock_clock_ = new IncrementingClock(std::chrono::system_clock::duration(0));
mock_clock_->set_time(
std::chrono::system_clock::time_point(std::chrono::seconds(kYear)));
logger_->SetClock(mock_clock_);
}
void TearDown() {
event_aggregator_.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);
}
// Advances |mock_clock_| by |num_days| days.
void AdvanceDay(int num_days) {
mock_clock_->increment_by(std::chrono::seconds(kDay) * num_days);
}
// 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;
}
Status GenerateAggregatedObservations(uint32_t day_index) {
return event_aggregator_->GenerateObservationsNoWorker(day_index);
}
Status GarbageCollectAggregateStore(uint32_t day_index) {
return event_aggregator_->GarbageCollect(day_index);
}
std::unique_ptr<Logger> logger_;
std::unique_ptr<Encoder> encoder_;
std::unique_ptr<EventAggregator> event_aggregator_;
std::unique_ptr<ObservationWriter> observation_writer_;
std::unique_ptr<FakeObservationStore> observation_store_;
std::unique_ptr<TestUpdateRecipient> update_recipient_;
std::unique_ptr<EncryptedMessageMaker> observation_encrypter_;
std::unique_ptr<SystemDataInterface> system_data_;
std::unique_ptr<MockConsistentProtoStore> local_aggregate_proto_store_;
std::unique_ptr<MockConsistentProtoStore> obs_history_proto_store_;
ExpectedAggregationParams expected_aggregation_params_;
IncrementingClock* mock_clock_;
};
// Creates a Logger whose ProjectContext contains only EVENT_OCCURRED metrics,
// each of which has a report of type UNIQUE_N_DAY_ACTIVES with
// local_privacy_noise_level set to NONE. Used to test the values of
// UniqueActivesObservations generated by the EventAggregator.
class UniqueActivesLoggerTest : public LoggerTest {
protected:
void SetUp() {
SetUpFromMetrics(
testing::unique_actives_noise_free::kCobaltRegistryBase64,
testing::unique_actives_noise_free::kExpectedAggregationParams);
}
};
// Creates a Logger whose ProjectContext contains only EVENT_COUNT metrics,
// each of which has a report of type PER_DEVICE_COUNT_STATS.
class PerDeviceCountLoggerTest : public LoggerTest {
protected:
void SetUp() {
SetUpFromMetrics(testing::per_device_count::kCobaltRegistryBase64,
testing::per_device_count::kExpectedAggregationParams);
}
};
// Tests the method LogEvent().
TEST_F(LoggerTest, LogEvent) {
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kErrorOccurredMetricId, 42));
Observation2 observation;
uint32_t expected_report_id =
testing::all_report_types::kErrorCountsByCodeReportId;
ASSERT_TRUE(FetchSingleObservation(&observation, expected_report_id,
observation_store_.get(),
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::kReadCacheHitCountsReportId,
testing::all_report_types::kReadCacheHitHistogramsReportId};
ASSERT_EQ(kOK, logger_->LogEventCount(
testing::all_report_types::kReadCacheHitsMetricId, 43,
"component2", 1, 303));
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids, 43u, "component2", 303, observation_store_.get(),
update_recipient_.get()));
}
// Tests the method LogElapsedTime().
TEST_F(LoggerTest, LogElapsedTime) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kModuleLoadTimeAggregatedReportId,
testing::all_report_types::kModuleLoadTimeHistogramReportId,
testing::all_report_types::kModuleLoadTimeRawDumpReportId};
ASSERT_EQ(kOK, logger_->LogElapsedTime(
testing::all_report_types::kModuleLoadTimeMetricId, 44,
"component4", 4004));
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids, 44u, "component4", 4004, observation_store_.get(),
update_recipient_.get()));
}
// Tests the method LogFrameRate().
TEST_F(LoggerTest, LogFrameRate) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kLoginModuleFrameRateAggregatedReportId,
testing::all_report_types::kLoginModuleFrameRateHistogramReportId,
testing::all_report_types::kLoginModuleFrameRateRawDumpReportId};
ASSERT_EQ(kOK, logger_->LogFrameRate(
testing::all_report_types::kLoginModuleFrameRateMetricId,
45, "component5", 5.123));
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids, 45u, "component5", 5123, observation_store_.get(),
update_recipient_.get()));
}
// Tests the method LogMemoryUsage().
TEST_F(LoggerTest, LogMemoryUsage) {
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kLedgerMemoryUsageAggregatedReportId,
testing::all_report_types::kLedgerMemoryUsageHistogramReportId};
ASSERT_EQ(kOK, logger_->LogMemoryUsage(
testing::all_report_types::kLedgerMemoryUsageMetricId, 46,
"component6", 606));
EXPECT_TRUE(CheckNumericEventObservations(
expected_report_ids, 46u, "component6", 606, observation_store_.get(),
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 = NewHistogram(indices, counts);
ASSERT_EQ(kOK, logger_->LogIntHistogram(
testing::all_report_types::kFileSystemWriteTimesMetricId,
47, "component7", std::move(histogram)));
Observation2 observation;
uint32_t expected_report_id =
testing::all_report_types::kFileSystemWriteTimesHistogramReportId;
ASSERT_TRUE(FetchSingleObservation(&observation, expected_report_id,
observation_store_.get(),
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]);
}
}
// Tests the method LogString().
TEST_F(LoggerTest, LogString) {
ASSERT_EQ(kOK, logger_->LogString(
testing::all_report_types::kModuleDownloadsMetricId,
"www.mymodule.com"));
std::vector<Observation2> observations(2);
std::vector<uint32_t> expected_report_ids = {
testing::all_report_types::kModuleDownloadsHeavyHittersReportId,
testing::all_report_types::kModuleDownloadsWithThresholdReportId};
ASSERT_TRUE(FetchObservations(&observations, expected_report_ids,
observation_store_.get(),
update_recipient_.get()));
ASSERT_TRUE(observations[0].has_string_rappor());
EXPECT_FALSE(observations[0].string_rappor().data().empty());
ASSERT_TRUE(observations[1].has_forculus());
EXPECT_FALSE(observations[1].forculus().ciphertext().empty());
}
// 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 = NewCustomEvent(dimension_names, values);
ASSERT_EQ(kOK, logger_->LogCustomEvent(
testing::all_report_types::kModuleInstallsMetricId,
std::move(custom_event)));
Observation2 observation;
uint32_t expected_report_id =
testing::all_report_types::kModuleInstallsDetailedDataReportId;
ASSERT_TRUE(FetchSingleObservation(&observation, expected_report_id,
observation_store_.get(),
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]);
EXPECT_TRUE(MessageDifferencer::Equals(obs_dimension, values[i]));
}
}
// Tests that the expected number of locally aggregated Observations are
// generated when no events have been logged.
TEST_F(LoggerTest, CheckNumAggregatedObsNoEvents) {
ASSERT_EQ(kOK, GenerateAggregatedObservations(
CurrentDayIndex(MetricDefinition::UTC)));
std::vector<Observation2> observations(0);
EXPECT_TRUE(FetchAggregatedObservations(
&observations, expected_aggregation_params_, observation_store_.get(),
update_recipient_.get()));
}
// Tests that the expected number of locally aggregated Observations are
// generated when one Event has been logged for a locally aggregated report.
TEST_F(LoggerTest, CheckNumAggregatedObsOneEvent) {
// Log 1 occurrence of event code 0 for the DeviceBoots metric, which has no
// immediate reports.
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kDeviceBootsMetricId, 0));
// Check that no immediate Observation was generated.
std::vector<Observation2> immediate_observations(0);
std::vector<uint32_t> expected_immediate_report_ids = {};
ASSERT_TRUE(
FetchObservations(&immediate_observations, expected_immediate_report_ids,
observation_store_.get(), update_recipient_.get()));
// Generate locally aggregated observations for the current day index.
ASSERT_EQ(kOK, GenerateAggregatedObservations(
CurrentDayIndex(MetricDefinition::UTC)));
// Check that the expected numbers of aggregated observations were generated.
std::vector<Observation2> aggregated_observations;
EXPECT_TRUE(FetchAggregatedObservations(
&aggregated_observations, expected_aggregation_params_,
observation_store_.get(), update_recipient_.get()));
}
// Tests that the expected number of locally aggregated Observations are
// generated when multiple Events 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(kOK, logger_->LogEvent(
testing::all_report_types::kDeviceBootsMetricId, 0));
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kDeviceBootsMetricId, 0));
// Log 2 occurrences of distinct event codes for the FeaturesActive metric,
// which has 1 locally aggregated report and no immediate reports.
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kFeaturesActiveMetricId, 0));
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kFeaturesActiveMetricId, 1));
// 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(kOK, logger_->LogEventCount(
testing::all_report_types::kSettingsChangedMetricId, 0,
"component_A", 0, 10));
ASSERT_EQ(kOK, logger_->LogEventCount(
testing::all_report_types::kSettingsChangedMetricId, 0,
"component_B", 0, 15));
// Check that no immediate Observations were generated.
std::vector<Observation2> immediate_observations(0);
std::vector<uint32_t> expected_immediate_report_ids = {};
ASSERT_TRUE(
FetchObservations(&immediate_observations, expected_immediate_report_ids,
observation_store_.get(), update_recipient_.get()));
// Generate locally aggregated observations for the current day index.
ASSERT_EQ(kOK, GenerateAggregatedObservations(
CurrentDayIndex(MetricDefinition::UTC)));
// 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<Observation2> aggregated_observations;
EXPECT_TRUE(FetchAggregatedObservations(
&aggregated_observations, expected_aggregation_params_,
observation_store_.get(), update_recipient_.get()));
}
// Tests that the expected number of locally aggregated Observations are
// generated when multiple Events have been logged for locally aggregated
// and immediate reports.
TEST_F(LoggerTest, CheckNumAggregatedObsImmediateAndAggregatedEvents) {
// Log 3 occurrences of event codes for the EventsOccurred metric, which
// has 1 locally aggregated report and 1 immediate report.
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kEventsOccurredMetricId, 0));
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kEventsOccurredMetricId, 0));
ASSERT_EQ(kOK, logger_->LogEvent(
testing::all_report_types::kEventsOccurredMetricId, 2));
// Check that each of the 3 logged events resulted in an immediate
// Observation.
std::vector<Observation2> immediate_observations(3);
std::vector<uint32_t> expected_immediate_report_ids(
3, testing::all_report_types::kEventsOccurredGlobalCountReportId);
ASSERT_TRUE(
FetchObservations(&immediate_observations, expected_immediate_report_ids,
observation_store_.get(), update_recipient_.get()));
// Clear the FakeObservationStore.
ResetObservationStore();
// Generate locally aggregated observations for the current day index.
ASSERT_EQ(kOK, GenerateAggregatedObservations(
CurrentDayIndex(MetricDefinition::UTC)));
// Check that the expected aggregated observations were generated.
std::vector<Observation2> observations;
EXPECT_TRUE(FetchAggregatedObservations(
&observations, expected_aggregation_params_, observation_store_.get(),
update_recipient_.get()));
}
// Tests that UniqueActivesObservations with the expected values are generated
// when no events have been logged.
TEST_F(UniqueActivesLoggerTest, CheckUniqueActivesObsValuesNoEvents) {
const auto current_day_index = CurrentDayIndex(MetricDefinition::UTC);
// Generate locally aggregated Observations without logging any events.
ASSERT_EQ(kOK, GenerateAggregatedObservations(current_day_index));
// Check that all generated Observations are of non-activity.
auto expected_obs = MakeNullExpectedUniqueActivesObservations(
expected_aggregation_params_, current_day_index);
EXPECT_TRUE(CheckUniqueActivesObservations(
expected_obs, observation_store_.get(), update_recipient_.get()));
}
// Tests that UniqueActivesObservations with the expected values are generated
// when events have been logged for UNIQUE_N_DAY_ACTIVES reports on a single
// day.
TEST_F(UniqueActivesLoggerTest, CheckUniqueActivesObsValuesSingleDay) {
const auto current_day_index = CurrentDayIndex(MetricDefinition::UTC);
// Log 2 occurrences of event code 0 for the DeviceBoots metric, which has 1
// locally aggregated report and no immediate reports.
ASSERT_EQ(kOK,
logger_->LogEvent(
testing::unique_actives_noise_free::kDeviceBootsMetricId, 0));
ASSERT_EQ(kOK,
logger_->LogEvent(
testing::unique_actives_noise_free::kDeviceBootsMetricId, 0));
// Log 2 occurrences of different event codes for the SomeFeaturesActive
// metric, which has 1 locally aggregated report and no immediate reports.
ASSERT_EQ(
kOK, logger_->LogEvent(
testing::unique_actives_noise_free::kFeaturesActiveMetricId, 0));
ASSERT_EQ(
kOK, logger_->LogEvent(
testing::unique_actives_noise_free::kFeaturesActiveMetricId, 1));
// Generate locally aggregated observations for the current day index.
ASSERT_EQ(kOK, GenerateAggregatedObservations(current_day_index));
// Form the expected observations for the current day index.
auto expected_obs = MakeNullExpectedUniqueActivesObservations(
expected_aggregation_params_, current_day_index);
expected_obs[{testing::unique_actives_noise_free::kDeviceBootsMetricReportId,
current_day_index}] = {{1, {true, false}}};
expected_obs[{
testing::unique_actives_noise_free::kFeaturesActiveMetricReportId,
current_day_index}] = {{1, {true, true, false, false, false}},
{7, {true, true, false, false, false}},
{30, {true, true, false, false, false}}};
// Check that the expected aggregated observations were generated.
EXPECT_TRUE(CheckUniqueActivesObservations(
expected_obs, observation_store_.get(), update_recipient_.get()));
}
// Tests that UniqueActivesObservations with the expected values are generated
// when events have been logged for a UniqueActives report over multiple days.
//
// Logs events for the EventsOccurred_UniqueDevices report (whose parent metric
// has max_event_code = 5, and whose report ID is 401) for 10 days, according to
// the following pattern:
//
// * Never log event code 0.
// * On the i-th day (0-indexed) of logging, log an event for event code k,
// 1 <= k < 5, if 3*k divides i.
//
// Each day following the first day, generates Observations for the previous
// day index and checks them against the expected set of Observations, then
// garbage-collects the LocalAggregateStore for the current day index.
//
// The EventsOccurred_UniqueDevices report has window sizes 1 and 7, and
// the expected pattern of those Observations' values on the i-th day is:
//
// (i, window size) active for event codes
// ------------------------------------------------------
// (0, 1) 1, 2, 3, 4
// (0, 7) 1, 2, 3, 4
// (1, 1) ---
// (1, 7) 1, 2, 3, 4
// (2, 1) ---
// (2, 7) 1, 2, 3, 4
// (3, 1) 1
// (3, 7) 1, 2, 3, 4
// (4, 1) ---
// (4, 7) 1, 2, 3, 4
// (5, 1) ---
// (5, 7) 1, 2, 3, 4
// (6, 1) 1, 2
// (6, 7) 1, 2, 3, 4
// (7, 1) ---
// (7, 7) 1, 2
// (8, 1) ---
// (8, 7) 1, 2
// (9, 1) 1, 3
// (9, 7) 1, 2, 3
//
// All Observations for all other locally aggregated reports should be
// observations of non-occurrence.
TEST_F(UniqueActivesLoggerTest, CheckUniqueActivesObsValuesMultiDay) {
const auto& expected_id =
testing::unique_actives_noise_free::kEventsOccurredMetricReportId;
const auto start_day_index = CurrentDayIndex(MetricDefinition::UTC);
// Form expected Observations for the 10 days of logging.
std::vector<ExpectedUniqueActivesObservations> expected_obs(10);
for (uint32_t i = 0; i < expected_obs.size(); i++) {
expected_obs[i] = MakeNullExpectedUniqueActivesObservations(
expected_aggregation_params_, start_day_index + i);
}
expected_obs[0][{expected_id, start_day_index}] = {
{1, {false, true, true, true, true}},
{7, {false, true, true, true, true}}};
expected_obs[1][{expected_id, start_day_index + 1}] = {
{1, {false, false, false, false, false}},
{7, {false, true, true, true, true}}};
expected_obs[2][{expected_id, start_day_index + 2}] = {
{1, {false, false, false, false, false}},
{7, {false, true, true, true, true}}};
expected_obs[3][{expected_id, start_day_index + 3}] = {
{1, {false, true, false, false, false}},
{7, {false, true, true, true, true}}};
expected_obs[4][{expected_id, start_day_index + 4}] = {
{1, {false, false, false, false, false}},
{7, {false, true, true, true, true}}};
expected_obs[5][{expected_id, start_day_index + 5}] = {
{1, {false, false, false, false, false}},
{7, {false, true, true, true, true}}};
expected_obs[6][{expected_id, start_day_index + 6}] = {
{1, {false, true, true, false, false}},
{7, {false, true, true, true, true}}};
expected_obs[7][{expected_id, start_day_index + 7}] = {
{1, {false, false, false, false, false}},
{7, {false, true, true, false, false}}};
expected_obs[8][{expected_id, start_day_index + 8}] = {
{1, {false, false, false, false, false}},
{7, {false, true, true, false, false}}};
expected_obs[9][{expected_id, start_day_index + 9}] = {
{1, {false, true, false, true, false}},
{7, {false, true, true, true, false}}};
for (uint32_t i = 0; i < 10; i++) {
if (i < 10) {
for (uint32_t event_code = 1; event_code < 5; event_code++) {
if (i % (3 * event_code) == 0) {
ASSERT_EQ(
kOK,
logger_->LogEvent(
testing::unique_actives_noise_free::kEventsOccurredMetricId,
event_code));
}
}
}
// Clear the FakeObservationStore.
ResetObservationStore();
// Advance the Logger's clock by 1 day.
AdvanceDay(1);
// Generate locally aggregated Observations for the previous day
// index.
ASSERT_EQ(kOK, GenerateAggregatedObservations(
CurrentDayIndex(MetricDefinition::UTC) - 1));
// Check the generated Observations against the expectation.
EXPECT_TRUE(CheckUniqueActivesObservations(
expected_obs[i], observation_store_.get(), update_recipient_.get()));
// Garbage-collect the LocalAggregateStore for the current day index.
ASSERT_EQ(kOK, GarbageCollectAggregateStore(
CurrentDayIndex(MetricDefinition::UTC)));
}
}
// Generate Observations without logging any events, and check that the
// resulting Observations are as expected: 1 ReportParticipationObservation for
// each PER_DEVICE_COUNT_STATS report in the config, and no
// PerDeviceCountObservations.
TEST_F(PerDeviceCountLoggerTest, CheckPerDeviceCountObsValuesNoEvents) {
const auto day_index = CurrentDayIndex(MetricDefinition::UTC);
EXPECT_EQ(kOK, GenerateAggregatedObservations(day_index));
const auto& expected_report_participation_obs =
MakeExpectedReportParticipationObservations(
testing::per_device_count::kExpectedAggregationParams, day_index);
EXPECT_TRUE(CheckPerDeviceCountObservations(
{}, expected_report_participation_obs, observation_store_.get(),
update_recipient_.get()));
}
// Tests that Observations with the expected values are generated when events
// have been logged for EVENT_COUNT metrics with a PER_DEVICE_COUNT_STATS
// report, on a single day.
TEST_F(PerDeviceCountLoggerTest, CheckPerDeviceCountObsValuesSingleDay) {
const auto day_index = CurrentDayIndex(MetricDefinition::UTC);
// Log several events on |day_index|.
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kConnectionFailuresMetricId, 0u,
"component_A", 0, 5));
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kConnectionFailuresMetricId, 0u,
"component_B", 0, 5));
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kConnectionFailuresMetricId, 0u,
"component_A", 0, 5));
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kConnectionFailuresMetricId, 1u,
"component_A", 0, 5));
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kSettingsChangedMetricId, 0u,
"component_C", 0, 5));
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kSettingsChangedMetricId, 0u,
"component_C", 0, 5));
// The ConnectionFailures metric has an immediate report of type
// EVENT_COMPONENT_OCCURRENCE_COUNT. Check that 4 immediate Observations were
// generated for that report.
std::vector<Observation2> immediate_observations(4);
std::vector<uint32_t> expected_immediate_report_ids(
4, testing::per_device_count::kConnectionFailuresGlobalCountReportId);
EXPECT_TRUE(
FetchObservations(&immediate_observations, expected_immediate_report_ids,
observation_store_.get(), update_recipient_.get()));
// Clear the FakeObservationStore.
ResetObservationStore();
// Generate locally aggregated Observations for |day_index|.
EXPECT_EQ(kOK, GenerateAggregatedObservations(day_index));
// Form the expected locally aggregated Observations.
auto expected_report_participation_obs =
MakeExpectedReportParticipationObservations(
testing::per_device_count::kExpectedAggregationParams, day_index);
ExpectedPerDeviceCountObservations expected_per_device_count_obs;
expected_per_device_count_obs[{
testing::per_device_count::kConnectionFailuresMetricReportId,
day_index}][1] = {
{"component_A", 0u, 10}, {"component_A", 1u, 5}, {"component_B", 0u, 5}};
expected_per_device_count_obs[{
testing::per_device_count::kSettingsChangedMetricReportId, day_index}]
[7] = {{"component_C", 0u, 10}};
expected_per_device_count_obs[{
testing::per_device_count::kSettingsChangedMetricReportId, day_index}]
[30] = {{"component_C", 0u, 10}};
EXPECT_TRUE(CheckPerDeviceCountObservations(
expected_per_device_count_obs, expected_report_participation_obs,
observation_store_.get(), update_recipient_.get()));
}
// Checks that PerDeviceCountObservations with the expected values are
// generated when some events have been logged for a PER_DEVICE_COUNT
// report over multiple days and GenerateAggregatedObservations() is called
// each day.
//
// Logged events for the SettingsChanged_PerDeviceCount metric on the i-th day:
//
// i (component, event code, count)
// -----------------------------------------------------------------------
// 0
// 1 ("A", 1, 3)
// 2 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2)
// 3 ("A", 1, 3)
// 4 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2)
// 5 ("A", 1, 3)
// 6 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2)
// 7 ("A", 1, 3)
// 8 ("A", 1, 3), ("A", 2, 3), ("B", 1, 2), ("B", 2, 2)
// 9 ("A", 1, 3)
//
// Expected PerDeviceCountObservations for the SettingsChanged_PerDeviceCount
// report on the i-th day:
//
// (i, window size) (component, event code, count)
// -----------------------------------------------------------------------
// (0, 7)
// (0, 30)
// (1, 7) ("A", 1, 3)
// (1, 30) ("A", 1, 3)
// (2, 7) ("A", 1, 6), ("A", 2, 3), ("B", 1, 2)
// (2, 30) ("A", 1, 6), ("A", 2, 3), ("B", 1, 2)
// (3, 7) ("A", 1, 9), ("A", 2, 3), ("B", 1, 2)
// (3, 30) ("A", 1, 9), ("A", 2, 3), ("B", 1, 2)
// (4, 7) ("A", 1, 12), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2)
// (4, 30) ("A", 1, 12), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2)
// (5, 7) ("A", 1, 15), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2)
// (5, 30) ("A", 1, 15), ("A", 2, 6), ("B", 1, 4), ("B", 2, 2)
// (6, 7) ("A", 1, 18), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2)
// (6, 30) ("A", 1, 18), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2)
// (7, 7) ("A", 1, 21), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2)
// (7, 30) ("A", 1, 21), ("A", 2, 9), ("B", 1, 6), ("B", 2, 2)
// (8, 7) ("A", 1, 21), ("A", 2, 12), ("B", 1, 8), ("B", 2, 4)
// (8, 30) ("A", 1, 24), ("A", 2, 12), ("B", 1, 8), ("B", 2, 4)
// (9, 7) ("A", 1, 21), ("A", 2, 9), ("B", 1, 6), ("B", 2, 4)
// (9, 30) ("A", 1, 27), ("A", 2, 12), ("B", 1, 8), ("B", 2, 4)
//
// In addition, expect 2 ReportParticipationObservations each day, 1 for each
// of ConnectionFailures_PerDeviceCount and SettingsChanged_PerDeviceCount.
TEST_F(PerDeviceCountLoggerTest, CheckPerDeviceCountObsValuesMultiDay) {
const auto start_day_index = CurrentDayIndex(MetricDefinition::UTC);
const auto& expected_id =
testing::per_device_count::kSettingsChangedMetricReportId;
// Form expectedObservations for the 10 days of logging.
uint32_t num_days = 10;
std::vector<ExpectedPerDeviceCountObservations> expected_per_device_count_obs(
num_days);
std::vector<ExpectedReportParticipationObservations>
expected_report_participation_obs(num_days);
for (uint32_t offset = 0; offset < num_days; offset++) {
expected_report_participation_obs[offset] =
MakeExpectedReportParticipationObservations(
expected_aggregation_params_, start_day_index + offset);
}
expected_per_device_count_obs[0] = {};
expected_per_device_count_obs[1][{expected_id, start_day_index + 1}] = {
{7, {{"A", 1u, 3}}}, {30, {{"A", 1u, 3}}}};
expected_per_device_count_obs[2][{expected_id, start_day_index + 2}] = {
{7, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}},
{30, {{"A", 1u, 6}, {"A", 2u, 3}, {"B", 1u, 2}}}};
expected_per_device_count_obs[3][{expected_id, start_day_index + 3}] = {
{7, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}},
{30, {{"A", 1u, 9}, {"A", 2u, 3}, {"B", 1u, 2}}}};
expected_per_device_count_obs[4][{expected_id, start_day_index + 4}] = {
{7, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}},
{30, {{"A", 1u, 12}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}};
expected_per_device_count_obs[5][{expected_id, start_day_index + 5}] = {
{7, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}},
{30, {{"A", 1u, 15}, {"A", 2u, 6}, {"B", 1u, 4}, {"B", 2u, 2}}}};
expected_per_device_count_obs[6][{expected_id, start_day_index + 6}] = {
{7, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}},
{30, {{"A", 1u, 18}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}};
expected_per_device_count_obs[7][{expected_id, start_day_index + 7}] = {
{7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}},
{30, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 2}}}};
expected_per_device_count_obs[8][{expected_id, start_day_index + 8}] = {
{7, {{"A", 1u, 21}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}},
{30, {{"A", 1u, 24}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}};
expected_per_device_count_obs[9][{expected_id, start_day_index + 9}] = {
{7, {{"A", 1u, 21}, {"A", 2u, 9}, {"B", 1u, 6}, {"B", 2u, 4}}},
{30, {{"A", 1u, 27}, {"A", 2u, 12}, {"B", 1u, 8}, {"B", 2u, 4}}}};
for (uint32_t offset = 0; offset < 1; offset++) {
auto day_index = CurrentDayIndex(MetricDefinition::UTC);
for (uint32_t event_code = 1; event_code < 3; event_code++) {
if (offset > 0 && offset % event_code == 0) {
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kSettingsChangedMetricId,
event_code, "A", 0, 3));
}
if (offset > 0 && offset % (2 * event_code) == 0) {
EXPECT_EQ(kOK, logger_->LogEventCount(
testing::per_device_count::kSettingsChangedMetricId,
event_code, "B", 0, 2));
}
}
// Clear the FakeObservationStore.
ResetObservationStore();
// Generate locally aggregated Observations.
EXPECT_EQ(kOK, GenerateAggregatedObservations(day_index));
EXPECT_TRUE(CheckPerDeviceCountObservations(
expected_per_device_count_obs[offset],
expected_report_participation_obs[offset], observation_store_.get(),
update_recipient_.get()))
<< "offset = " << offset;
AdvanceDay(1);
}
}
} // namespace logger
} // namespace cobalt