blob: 346cf821a1af65786cf63c032e35e9dd3e69ed1c [file] [log] [blame]
// Copyright 2020 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "src/lib/util/datetime_util.h"
#include "src/lib/util/hash.h"
#include "src/local_aggregation_1_1/aggregation_procedures/aggregation_procedure.h"
#include "src/local_aggregation_1_1/aggregation_procedures/testing/test_aggregation_procedure.h"
#include "src/local_aggregation_1_1/testing/test_registry.cb.h"
#include "src/logger/project_context_factory.h"
#include "src/pb/metadata_builder.h"
namespace cobalt::local_aggregation {
using ::testing::Contains;
class StringHistogramAggregationProcedureTest : public testing::TestAggregationProcedure {
public:
void LogStringEvents(uint32_t hour_id, uint32_t num_event_codes,
const std::vector<std::string>& strings, AggregationProcedure* procedure,
ReportAggregate* aggregate) {
logger::EventRecord record = MakeEventRecord(util::TimeInfo::FromHourId(hour_id));
StringEvent* event = record.event()->mutable_string_event();
event->add_event_code(0);
for (int i = 0; i < num_event_codes; i++) {
event->set_event_code(0, i);
for (const auto& str : strings) {
event->set_string_value(str);
procedure->UpdateAggregate(record, aggregate);
}
}
}
};
TEST_F(StringHistogramAggregationProcedureTest, UpdateAggregateWorks) {
auto procedure = GetProcedureFor(kStringMetricMetricId, kStringMetricStringCountsReportIndex);
ReportAggregate aggregate;
const uint32_t kNumEventCodes = 100;
const uint32_t kHourId = 1;
const std::vector<std::string> kTestStrings = {
"Nunc dictum justo ac arcu.",
"Suspendisse ullamcorper mi vel pulvinar dictum.",
"Aenean feugiat consectetur vestibulum.",
"Integer a ullamcorper dolor.",
"Praesent vel nulla quis metus consectetur aliquam sed ut felis.",
};
LogStringEvents(kHourId, kNumEventCodes, kTestStrings, procedure.get(), &aggregate);
ASSERT_EQ(aggregate.hourly().by_hour_id_size(), 1);
ASSERT_EQ(aggregate.hourly().by_hour_id().at(kHourId).by_event_code_size(), kNumEventCodes);
}
TEST_F(StringHistogramAggregationProcedureTest, GenerateObservationWorks) {
auto procedure = GetProcedureFor(kStringMetricMetricId, kStringMetricStringCountsReportIndex);
ReportAggregate aggregate;
const uint32_t kNumEventCodes = 10;
const uint32_t kEndHourId = 11;
const std::vector<std::string> kTestStrings = {
"Nunc dictum justo ac arcu.",
"Suspendisse ullamcorper mi vel pulvinar dictum.",
"Aenean feugiat consectetur vestibulum.",
"Integer a ullamcorper dolor.",
"Praesent vel nulla quis metus consectetur aliquam sed ut felis.",
};
for (auto hour_id = 1; hour_id <= kEndHourId; hour_id += 2) {
LogStringEvents(hour_id, kNumEventCodes, kTestStrings, procedure.get(), &aggregate);
}
auto observation_or =
GenerateObservation(util::TimeInfo::FromHourId(kEndHourId), procedure.get(), &aggregate);
ASSERT_TRUE(observation_or.ok());
auto observation = observation_or.ConsumeValueOrDie();
// Should only generate for kEndHourId
ASSERT_TRUE(observation);
ASSERT_EQ(observation->string_histogram().string_histograms_size(), kNumEventCodes);
const StringHistogramObservation& histogram = observation->string_histogram();
std::vector<std::string> expected_hashes;
expected_hashes.reserve(kTestStrings.size());
for (const auto& string : kTestStrings) {
expected_hashes.push_back(util::FarmhashFingerprint(string));
}
for (const auto& value : histogram.string_histograms()) {
for (int i = 0; i < value.bucket_indices_size(); i++) {
ASSERT_EQ(value.bucket_counts(i), 1);
ASSERT_THAT(expected_hashes, Contains(histogram.string_hashes(value.bucket_indices(i))));
}
}
ASSERT_EQ(aggregate.hourly().by_hour_id_size(), 0);
}
TEST_F(StringHistogramAggregationProcedureTest, RejectExcessStrings) {
auto procedure = GetProcedureFor(kStringMetricMetricId, kStringMetricStringCountsReportIndex);
ReportAggregate aggregate;
const uint32_t kNumEventCodes = 10;
const uint32_t kEndHourId = 11;
// The string_buffer_max is 5, this is too many
const std::vector<std::string> kTestStrings = {
"Nunc dictum justo ac arcu.",
"Suspendisse ullamcorper mi vel pulvinar dictum.",
"Aenean feugiat consectetur vestibulum.",
"Integer a ullamcorper dolor.",
"Praesent vel nulla quis metus consectetur aliquam sed ut felis.",
"Integer quis tortor commodo, rutrum risus.",
"Nam consectetur velit ac sollicitudin tempus.",
"Integer ultricies libero quis suscipit lobortis.",
"Aenean bibendum egestas risus auctor tincidunt.",
"Sed sit amet scelerisque neque.",
"Pellentesque dictum quam nec lectus sagittis interdum.",
};
for (auto hour_id = 1; hour_id <= kEndHourId; hour_id += 2) {
LogStringEvents(hour_id, kNumEventCodes, kTestStrings, procedure.get(), &aggregate);
}
auto observation_or =
GenerateObservation(util::TimeInfo::FromHourId(kEndHourId), procedure.get(), &aggregate);
ASSERT_TRUE(observation_or.ok());
auto observation = observation_or.ConsumeValueOrDie();
// Should only generate for kEndHourId
ASSERT_TRUE(observation);
ASSERT_EQ(observation->string_histogram().string_histograms_size(), kNumEventCodes);
for (const auto& value : observation->string_histogram().string_histograms()) {
ASSERT_EQ(value.bucket_counts_size(), 5);
}
ASSERT_EQ(aggregate.hourly().by_hour_id_size(), 0);
}
} // namespace cobalt::local_aggregation