| // 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 |