1 // Copyright (C) 2017 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "src/metrics/GaugeMetricProducer.h"
16 
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <math.h>
20 #include <stdio.h>
21 
22 #include <vector>
23 
24 #include "logd/LogEvent.h"
25 #include "metrics_test_helper.h"
26 #include "src/matchers/SimpleAtomMatchingTracker.h"
27 #include "src/metrics/MetricProducer.h"
28 #include "src/stats_log_util.h"
29 #include "stats_event.h"
30 #include "tests/statsd_test_util.h"
31 
32 using namespace testing;
33 using android::sp;
34 using std::set;
35 using std::unordered_map;
36 using std::vector;
37 using std::make_shared;
38 
39 #ifdef __ANDROID__
40 
41 namespace android {
42 namespace os {
43 namespace statsd {
44 
45 namespace {
46 
47 const ConfigKey kConfigKey(0, 12345);
48 const int tagId = 1;
49 const int64_t metricId = 123;
50 const uint64_t protoHash = 0x123456789;
51 const int logEventMatcherIndex = 0;
52 const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
53 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
54 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
55 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
56 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
57 const int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
58 
makeLogEvent(int32_t atomId,int64_t timestampNs,int32_t value1,string str1,int32_t value2)59 shared_ptr<LogEvent> makeLogEvent(int32_t atomId, int64_t timestampNs, int32_t value1, string str1,
60                                   int32_t value2) {
61     AStatsEvent* statsEvent = AStatsEvent_obtain();
62     AStatsEvent_setAtomId(statsEvent, atomId);
63     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
64 
65     AStatsEvent_writeInt32(statsEvent, value1);
66     AStatsEvent_writeString(statsEvent, str1.c_str());
67     AStatsEvent_writeInt32(statsEvent, value2);
68 
69     shared_ptr<LogEvent> logEvent = std::make_shared<LogEvent>(/*uid=*/0, /*pid=*/0);
70     parseStatsEventToLogEvent(statsEvent, logEvent.get());
71     return logEvent;
72 }
73 }  // anonymous namespace
74 
75 // Setup for parameterized tests.
76 class GaugeMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
77 
78 INSTANTIATE_TEST_SUITE_P(GaugeMetricProducerTest_PartialBucket,
79                          GaugeMetricProducerTest_PartialBucket,
80                          testing::Values(APP_UPGRADE, BOOT_COMPLETE));
81 
82 /*
83  * Tests that the first bucket works correctly
84  */
TEST(GaugeMetricProducerTest,TestFirstBucket)85 TEST(GaugeMetricProducerTest, TestFirstBucket) {
86     GaugeMetric metric;
87     metric.set_id(metricId);
88     metric.set_bucket(ONE_MINUTE);
89     metric.mutable_gauge_fields_filter()->set_include_all(false);
90     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
91     gaugeFieldMatcher->set_field(tagId);
92     gaugeFieldMatcher->add_child()->set_field(1);
93     gaugeFieldMatcher->add_child()->set_field(3);
94 
95     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
96 
97     sp<EventMatcherWizard> eventMatcherWizard =
98             createEventMatcherWizard(tagId, logEventMatcherIndex);
99 
100     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
101     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
102 
103     // statsd started long ago.
104     // The metric starts in the middle of the bucket
105     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
106                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
107                                       -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
108                                       pullerManager, provider);
109     gaugeProducer.prepareFirstBucket();
110 
111     EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
112     EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
113     EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
114 }
115 
TEST(GaugeMetricProducerTest,TestPulledEventsNoCondition)116 TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
117     GaugeMetric metric;
118     metric.set_id(metricId);
119     metric.set_bucket(ONE_MINUTE);
120     metric.mutable_gauge_fields_filter()->set_include_all(false);
121     metric.set_max_pull_delay_sec(INT_MAX);
122     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
123     gaugeFieldMatcher->set_field(tagId);
124     gaugeFieldMatcher->add_child()->set_field(1);
125     gaugeFieldMatcher->add_child()->set_field(3);
126 
127     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
128 
129     sp<EventMatcherWizard> eventMatcherWizard =
130             createEventMatcherWizard(tagId, logEventMatcherIndex);
131 
132     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
133     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
134     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
135     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
136             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
137                                 vector<std::shared_ptr<LogEvent>>* data) {
138                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
139                 data->clear();
140                 data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
141                 return true;
142             }));
143 
144     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
145 
146     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
147                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
148                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
149                                       pullerManager, provider);
150     gaugeProducer.prepareFirstBucket();
151 
152     vector<shared_ptr<LogEvent>> allData;
153     allData.clear();
154     allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11));
155 
156     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs);
157     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
158     auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
159     EXPECT_EQ(INT, it->mValue.getType());
160     EXPECT_EQ(10, it->mValue.int_value);
161     it++;
162     EXPECT_EQ(11, it->mValue.int_value);
163     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
164     EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()
165                          ->second.back()
166                          .mAggregatedAtoms.begin()
167                          ->first.getAtomFieldValues()
168                          .getValues()
169                          .begin()
170                          ->mValue.int_value);
171 
172     allData.clear();
173     allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25));
174     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket3StartTimeNs);
175     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
176     it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
177     EXPECT_EQ(INT, it->mValue.getType());
178     EXPECT_EQ(24, it->mValue.int_value);
179     it++;
180     EXPECT_EQ(INT, it->mValue.getType());
181     EXPECT_EQ(25, it->mValue.int_value);
182     // One dimension.
183     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
184     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
185     auto it2 = gaugeProducer.mPastBuckets.begin()
186                        ->second.back()
187                        .mAggregatedAtoms.begin()
188                        ->first.getAtomFieldValues()
189                        .getValues()
190                        .begin();
191     EXPECT_EQ(INT, it2->mValue.getType());
192     EXPECT_EQ(10L, it2->mValue.int_value);
193     it2++;
194     EXPECT_EQ(INT, it2->mValue.getType());
195     EXPECT_EQ(11L, it2->mValue.int_value);
196 
197     gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
198     ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
199     // One dimension.
200     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
201     ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
202     it2 = gaugeProducer.mPastBuckets.begin()
203                   ->second.back()
204                   .mAggregatedAtoms.begin()
205                   ->first.getAtomFieldValues()
206                   .getValues()
207                   .begin();
208     EXPECT_EQ(INT, it2->mValue.getType());
209     EXPECT_EQ(24L, it2->mValue.int_value);
210     it2++;
211     EXPECT_EQ(INT, it2->mValue.getType());
212     EXPECT_EQ(25L, it2->mValue.int_value);
213 }
214 
TEST_P(GaugeMetricProducerTest_PartialBucket,TestPushedEvents)215 TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) {
216     sp<AlarmMonitor> alarmMonitor;
217     GaugeMetric metric;
218     metric.set_id(metricId);
219     metric.set_bucket(ONE_MINUTE);
220     metric.mutable_gauge_fields_filter()->set_include_all(true);
221     metric.set_split_bucket_for_app_upgrade(true);
222 
223     Alert alert;
224     alert.set_id(101);
225     alert.set_metric_id(metricId);
226     alert.set_trigger_if_sum_gt(25);
227     alert.set_num_buckets(100);
228     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
229     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
230 
231     sp<EventMatcherWizard> eventMatcherWizard =
232             createEventMatcherWizard(tagId, logEventMatcherIndex);
233 
234     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
235 
236     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
237                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
238                                       -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
239                                       bucketStartTimeNs, pullerManager, provider);
240     gaugeProducer.prepareFirstBucket();
241 
242     sp<AnomalyTracker> anomalyTracker =
243             gaugeProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
244     EXPECT_TRUE(anomalyTracker != nullptr);
245 
246     LogEvent event1(/*uid=*/0, /*pid=*/0);
247     CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
248     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
249     EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
250 
251     switch (GetParam()) {
252         case APP_UPGRADE:
253             gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
254             break;
255         case BOOT_COMPLETE:
256             gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
257             break;
258     }
259     EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
260     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
261     EXPECT_EQ(bucketStartTimeNs,
262               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
263     EXPECT_EQ(partialBucketSplitTimeNs,
264               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
265     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
266     EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
267     // Partial buckets are not sent to anomaly tracker.
268     EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
269 
270     // Create an event in the same partial bucket.
271     LogEvent event2(/*uid=*/0, /*pid=*/0);
272     CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
273     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
274     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
275     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
276     EXPECT_EQ(bucketStartTimeNs,
277               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
278     EXPECT_EQ(partialBucketSplitTimeNs,
279               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
280     EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
281     // Partial buckets are not sent to anomaly tracker.
282     EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
283 
284     // Next event should trigger creation of new bucket and send previous full bucket to anomaly
285     // tracker.
286     LogEvent event3(/*uid=*/0, /*pid=*/0);
287     CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
288     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
289     EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum);
290     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
291     EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
292     EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
293 
294     // Next event should trigger creation of new bucket.
295     LogEvent event4(/*uid=*/0, /*pid=*/0);
296     CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10);
297     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
298     EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum);
299     ASSERT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
300     EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
301 }
302 
TEST_P(GaugeMetricProducerTest_PartialBucket,TestPulled)303 TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
304     GaugeMetric metric;
305     metric.set_id(metricId);
306     metric.set_bucket(ONE_MINUTE);
307     metric.set_max_pull_delay_sec(INT_MAX);
308     metric.set_split_bucket_for_app_upgrade(true);
309     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
310     gaugeFieldMatcher->set_field(tagId);
311     gaugeFieldMatcher->add_child()->set_field(2);
312 
313     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
314 
315     sp<EventMatcherWizard> eventMatcherWizard =
316             createEventMatcherWizard(tagId, logEventMatcherIndex);
317 
318     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
319 
320     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
321     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
322     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
323     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
324             .WillOnce(Return(false))
325             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
326                                 vector<std::shared_ptr<LogEvent>>* data) {
327                 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
328                 data->clear();
329                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
330                 return true;
331             }));
332 
333     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
334                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
335                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
336                                       pullerManager, provider);
337     gaugeProducer.prepareFirstBucket();
338 
339     vector<shared_ptr<LogEvent>> allData;
340     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
341     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs);
342     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
343     EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
344                          ->second.front()
345                          .mFields->begin()
346                          ->mValue.int_value);
347 
348     switch (GetParam()) {
349         case APP_UPGRADE:
350             gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
351             break;
352         case BOOT_COMPLETE:
353             gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
354             break;
355     }
356     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
357     EXPECT_EQ(bucketStartTimeNs,
358               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
359     EXPECT_EQ(partialBucketSplitTimeNs,
360               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
361     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
362     EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
363     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
364     EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
365                          ->second.front()
366                          .mFields->begin()
367                          ->mValue.int_value);
368 
369     allData.clear();
370     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3));
371     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
372                                bucketStartTimeNs + bucketSizeNs);
373     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
374     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
375     EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
376                          ->second.front()
377                          .mFields->begin()
378                          ->mValue.int_value);
379 }
380 
TEST(GaugeMetricProducerTest,TestPulledWithAppUpgradeDisabled)381 TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
382     GaugeMetric metric;
383     metric.set_id(metricId);
384     metric.set_bucket(ONE_MINUTE);
385     metric.set_max_pull_delay_sec(INT_MAX);
386     metric.set_split_bucket_for_app_upgrade(false);
387     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
388     gaugeFieldMatcher->set_field(tagId);
389     gaugeFieldMatcher->add_child()->set_field(2);
390 
391     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
392 
393     sp<EventMatcherWizard> eventMatcherWizard =
394             createEventMatcherWizard(tagId, logEventMatcherIndex);
395 
396     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
397     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
398     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
399     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
400             .WillOnce(Return(false));
401 
402     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
403 
404     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
405                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
406                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
407                                       pullerManager, provider);
408     gaugeProducer.prepareFirstBucket();
409 
410     vector<shared_ptr<LogEvent>> allData;
411     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
412     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs);
413     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
414     EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
415                          ->second.front()
416                          .mFields->begin()
417                          ->mValue.int_value);
418 
419     gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
420     ASSERT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
421     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
422     EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
423     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
424     EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
425                          ->second.front()
426                          .mFields->begin()
427                          ->mValue.int_value);
428 }
429 
TEST(GaugeMetricProducerTest,TestPulledEventsWithCondition)430 TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
431     GaugeMetric metric;
432     metric.set_id(metricId);
433     metric.set_bucket(ONE_MINUTE);
434     metric.set_max_pull_delay_sec(INT_MAX);
435     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
436     gaugeFieldMatcher->set_field(tagId);
437     gaugeFieldMatcher->add_child()->set_field(2);
438     metric.set_condition(StringToId("SCREEN_ON"));
439 
440     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
441 
442     sp<EventMatcherWizard> eventMatcherWizard =
443             createEventMatcherWizard(tagId, logEventMatcherIndex);
444 
445     int64_t conditionChangeNs = bucketStartTimeNs + 8;
446 
447     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
448     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
449     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
450     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
451             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
452                                 vector<std::shared_ptr<LogEvent>>* data) {
453                 data->clear();
454                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
455                 return true;
456             }));
457 
458     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
459 
460     GaugeMetricProducer gaugeProducer(
461             kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
462             protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
463             bucketStartTimeNs, bucketStartTimeNs, pullerManager, provider);
464     gaugeProducer.prepareFirstBucket();
465 
466     gaugeProducer.onConditionChanged(true, conditionChangeNs);
467     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
468     EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
469                            ->second.front()
470                            .mFields->begin()
471                            ->mValue.int_value);
472     ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
473 
474     vector<shared_ptr<LogEvent>> allData;
475     allData.clear();
476     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
477     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs);
478 
479     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
480     EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
481                            ->second.front()
482                            .mFields->begin()
483                            ->mValue.int_value);
484     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
485 
486     EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
487                            ->second.back()
488                            .mAggregatedAtoms.begin()
489                            ->first.getAtomFieldValues()
490                            .getValues()
491                            .begin()
492                            ->mValue.int_value);
493 
494     gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
495     gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
496     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
497     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
498     EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
499                             ->second.back()
500                             .mAggregatedAtoms.begin()
501                             ->first.getAtomFieldValues()
502                             .getValues()
503                             .begin()
504                             ->mValue.int_value);
505 }
506 
TEST(GaugeMetricProducerTest,TestPulledEventsWithSlicedCondition)507 TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
508     const int conditionTag = 65;
509     GaugeMetric metric;
510     metric.set_id(1111111);
511     metric.set_bucket(ONE_MINUTE);
512     metric.mutable_gauge_fields_filter()->set_include_all(true);
513     metric.set_condition(StringToId("APP_DIED"));
514     metric.set_max_pull_delay_sec(INT_MAX);
515     auto dim = metric.mutable_dimensions_in_what();
516     dim->set_field(tagId);
517     dim->add_child()->set_field(1);
518 
519     sp<EventMatcherWizard> eventMatcherWizard =
520             createEventMatcherWizard(tagId, logEventMatcherIndex);
521 
522     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
523     EXPECT_CALL(*wizard, query(_, _, _))
524             .WillRepeatedly(
525                     Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
526                               const bool isPartialLink) {
527                         int pos[] = {1, 0, 0};
528                         Field f(conditionTag, pos, 0);
529                         HashableDimensionKey key;
530                         key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
531 
532                         return ConditionState::kTrue;
533                     }));
534 
535     int64_t sliceConditionChangeNs = bucketStartTimeNs + 8;
536 
537     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
538     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
539     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
540     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
541             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
542                                 vector<std::shared_ptr<LogEvent>>* data) {
543                 data->clear();
544                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
545                 return true;
546             }));
547 
548     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
549 
550     GaugeMetricProducer gaugeProducer(
551             kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
552             protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
553             bucketStartTimeNs, bucketStartTimeNs, pullerManager, provider);
554     gaugeProducer.prepareFirstBucket();
555 
556     gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
557 
558     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
559     const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
560     ASSERT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
561     EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
562 
563     ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
564 
565     vector<shared_ptr<LogEvent>> allData;
566     allData.clear();
567     allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110));
568     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs);
569 
570     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
571     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
572 }
573 
TEST(GaugeMetricProducerTest,TestPulledEventsAnomalyDetection)574 TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
575     sp<AlarmMonitor> alarmMonitor;
576     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
577 
578     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
579     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
580     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
581     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
582             .WillOnce(Return(false));
583 
584     GaugeMetric metric;
585     metric.set_id(metricId);
586     metric.set_bucket(ONE_MINUTE);
587     metric.set_max_pull_delay_sec(INT_MAX);
588     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
589     gaugeFieldMatcher->set_field(tagId);
590     gaugeFieldMatcher->add_child()->set_field(2);
591 
592     sp<EventMatcherWizard> eventMatcherWizard =
593             createEventMatcherWizard(tagId, logEventMatcherIndex);
594 
595     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
596 
597     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
598                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
599                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
600                                       pullerManager, provider);
601     gaugeProducer.prepareFirstBucket();
602 
603     Alert alert;
604     alert.set_id(101);
605     alert.set_metric_id(metricId);
606     alert.set_trigger_if_sum_gt(25);
607     alert.set_num_buckets(2);
608     const int32_t refPeriodSec = 60;
609     alert.set_refractory_period_secs(refPeriodSec);
610     sp<AnomalyTracker> anomalyTracker =
611             gaugeProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
612 
613     int tagId = 1;
614     vector<shared_ptr<LogEvent>> allData;
615     allData.clear();
616     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13));
617     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucketStartTimeNs);
618     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
619     EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
620                            ->second.front()
621                            .mFields->begin()
622                            ->mValue.int_value);
623     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
624 
625     std::shared_ptr<LogEvent> event2 =
626             CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15);
627 
628     allData.clear();
629     allData.push_back(event2);
630     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
631                                bucketStartTimeNs + bucketSizeNs);
632     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
633     EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
634                            ->second.front()
635                            .mFields->begin()
636                            ->mValue.int_value);
637     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
638               std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
639 
640     allData.clear();
641     allData.push_back(
642             CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26));
643     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
644                                bucket2StartTimeNs + 2 * bucketSizeNs);
645     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
646     EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
647                            ->second.front()
648                            .mFields->begin()
649                            ->mValue.int_value);
650     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
651               std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
652 
653     // This event does not have the gauge field. Thus the current bucket value is 0.
654     allData.clear();
655     allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10));
656     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS,
657                                bucketStartTimeNs + 3 * bucketSizeNs);
658     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
659     EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
660 }
661 
TEST(GaugeMetricProducerTest,TestPullOnTrigger)662 TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
663     GaugeMetric metric;
664     metric.set_id(metricId);
665     metric.set_bucket(ONE_MINUTE);
666     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
667     metric.mutable_gauge_fields_filter()->set_include_all(false);
668     metric.set_max_pull_delay_sec(INT_MAX);
669     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
670     gaugeFieldMatcher->set_field(tagId);
671     gaugeFieldMatcher->add_child()->set_field(1);
672 
673     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
674 
675     sp<EventMatcherWizard> eventMatcherWizard =
676             createEventMatcherWizard(tagId, logEventMatcherIndex);
677 
678     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
679     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
680             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
681                                 vector<std::shared_ptr<LogEvent>>* data) {
682                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
683                 data->clear();
684                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
685                 return true;
686             }))
687             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
688                                 vector<std::shared_ptr<LogEvent>>* data) {
689                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
690                 data->clear();
691                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
692                 return true;
693             }))
694             .WillOnce(Return(true));
695 
696     int triggerId = 5;
697     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
698 
699     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
700                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
701                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
702                                       pullerManager, provider);
703     gaugeProducer.prepareFirstBucket();
704 
705     ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
706 
707     LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
708     CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10);
709     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
710     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
711     triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
712     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
713     ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
714     triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
715     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
716 
717     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
718     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.size());
719     auto it = gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.begin();
720     vector<int> atomValues;
721     atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
722     it++;
723     atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
724     EXPECT_THAT(atomValues, UnorderedElementsAre(4, 5));
725 }
726 
TEST(GaugeMetricProducerTest,TestPullNWithoutTrigger)727 TEST(GaugeMetricProducerTest, TestPullNWithoutTrigger) {
728     GaugeMetric metric;
729     metric.set_id(metricId);
730     metric.set_bucket(ONE_MINUTE);
731     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
732     metric.set_max_pull_delay_sec(INT_MAX);
733     metric.set_max_num_gauge_atoms_per_bucket(3);
734     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
735     gaugeFieldMatcher->set_field(tagId);
736 
737     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
738 
739     sp<EventMatcherWizard> eventMatcherWizard =
740             createEventMatcherWizard(tagId, logEventMatcherIndex);
741 
742     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
743     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
744     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
745     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
746             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
747                                 vector<std::shared_ptr<LogEvent>>* data) {
748                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
749                 data->clear();
750                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
751                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
752                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 6));
753                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 7));
754                 return true;
755             }));
756 
757     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
758 
759     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
760                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
761                                       tagId, /*triggerId=*/-1, tagId, bucketStartTimeNs,
762                                       bucketStartTimeNs, pullerManager, provider);
763 
764     EXPECT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
765     gaugeProducer.prepareFirstBucket();
766     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
767     EXPECT_EQ(3UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
768 
769     vector<std::shared_ptr<LogEvent>> allData;
770     allData.push_back(CreateNoValuesLogEvent(tagId, bucket2StartTimeNs + 10));
771     gaugeProducer.onDataPulled(allData, PullResult::PULL_RESULT_SUCCESS, bucket2StartTimeNs + 30);
772     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
773     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
774 
775     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
776     ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.size());
777     auto it = gaugeProducer.mPastBuckets.begin()->second.back().mAggregatedAtoms.begin();
778     vector<int> atomValues;
779     atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
780     it++;
781     atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
782     it++;
783     atomValues.emplace_back(it->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
784     EXPECT_THAT(atomValues, UnorderedElementsAre(4, 5, 6));
785 }
786 
TEST(GaugeMetricProducerTest,TestRemoveDimensionInOutput)787 TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
788     GaugeMetric metric;
789     metric.set_id(metricId);
790     metric.set_bucket(ONE_MINUTE);
791     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
792     metric.mutable_gauge_fields_filter()->set_include_all(true);
793     metric.set_max_pull_delay_sec(INT_MAX);
794     auto dimensionMatcher = metric.mutable_dimensions_in_what();
795     // use field 1 as dimension.
796     dimensionMatcher->set_field(tagId);
797     dimensionMatcher->add_child()->set_field(1);
798 
799     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
800 
801     sp<EventMatcherWizard> eventMatcherWizard =
802             createEventMatcherWizard(tagId, logEventMatcherIndex);
803 
804     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
805     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
806             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
807                                 vector<std::shared_ptr<LogEvent>>* data) {
808                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
809                 data->clear();
810                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
811                 return true;
812             }))
813             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
814                                 vector<std::shared_ptr<LogEvent>>* data) {
815                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
816                 data->clear();
817                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
818                 return true;
819             }))
820             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
821                                 vector<std::shared_ptr<LogEvent>>* data) {
822                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
823                 data->clear();
824                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
825                 return true;
826             }))
827             .WillOnce(Return(true));
828 
829     int triggerId = 5;
830     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
831 
832     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
833                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
834                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
835                                       pullerManager, provider);
836     gaugeProducer.prepareFirstBucket();
837 
838     LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
839     CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
840     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
841     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
842     triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10);
843     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
844     ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
845     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
846     triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
847     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
848     ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
849     triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
850     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
851 
852     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.size());
853     auto bucketIt = gaugeProducer.mPastBuckets.begin();
854     ASSERT_EQ(1UL, bucketIt->second.back().mAggregatedAtoms.size());
855     EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
856     EXPECT_EQ(4, bucketIt->second.back()
857                          .mAggregatedAtoms.begin()
858                          ->first.getAtomFieldValues()
859                          .getValues()
860                          .begin()
861                          ->mValue.int_value);
862     bucketIt++;
863     ASSERT_EQ(2UL, bucketIt->second.back().mAggregatedAtoms.size());
864     EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
865     auto atomIt = bucketIt->second.back().mAggregatedAtoms.begin();
866     vector<int> atomValues;
867     atomValues.emplace_back(
868             atomIt->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
869     atomIt++;
870     atomValues.emplace_back(
871             atomIt->first.getAtomFieldValues().getValues().begin()->mValue.int_value);
872     EXPECT_THAT(atomValues, UnorderedElementsAre(5, 6));
873 }
874 
875 /*
876  * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
877  * is smaller than the "min_bucket_size_nanos" specified in the metric config.
878  */
TEST(GaugeMetricProducerTest_BucketDrop,TestBucketDropWhenBucketTooSmall)879 TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
880     GaugeMetric metric;
881     metric.set_id(metricId);
882     metric.set_bucket(FIVE_MINUTES);
883     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
884     metric.set_min_bucket_size_nanos(10000000000);  // 10 seconds
885 
886     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
887 
888     sp<EventMatcherWizard> eventMatcherWizard =
889             createEventMatcherWizard(tagId, logEventMatcherIndex);
890 
891     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
892     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
893             // Bucket start.
894             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
895                                 vector<std::shared_ptr<LogEvent>>* data) {
896                 data->clear();
897                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
898                 return true;
899             }));
900 
901     int triggerId = 5;
902     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
903 
904     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
905                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
906                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
907                                       pullerManager, provider);
908     gaugeProducer.prepareFirstBucket();
909 
910     LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
911     CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
912     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
913 
914     // Check dump report.
915     ProtoOutputStream output;
916     std::set<string> strSet;
917     gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true,
918                                FAST /* dump_latency */, &strSet, &output);
919 
920     StatsLogReport report = outputStreamToProto(&output);
921     EXPECT_TRUE(report.has_gauge_metrics());
922     ASSERT_EQ(0, report.gauge_metrics().data_size());
923     ASSERT_EQ(1, report.gauge_metrics().skipped_size());
924 
925     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
926               report.gauge_metrics().skipped(0).start_bucket_elapsed_millis());
927     EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
928               report.gauge_metrics().skipped(0).end_bucket_elapsed_millis());
929     ASSERT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size());
930 
931     auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0);
932     EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
933     EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
934 }
935 
TEST(GaugeMetricProducerTest,TestPullDimensionalSampling)936 TEST(GaugeMetricProducerTest, TestPullDimensionalSampling) {
937     ShardOffsetProvider::getInstance().setShardOffset(5);
938 
939     StatsdConfig config;
940 
941     int triggerId = 5;
942     int shardCount = 2;
943     GaugeMetric sampledGaugeMetric = createGaugeMetric(
944             "GaugePullSampled", metricId, GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerId);
945     sampledGaugeMetric.set_max_pull_delay_sec(INT_MAX);
946     *sampledGaugeMetric.mutable_dimensions_in_what() = CreateDimensions(tagId, {1});
947     *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
948             CreateDimensions(tagId, {1});
949     sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(shardCount);
950     *config.add_gauge_metric() = sampledGaugeMetric;
951 
952     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
953 
954     sp<EventMatcherWizard> eventMatcherWizard =
955             createEventMatcherWizard(tagId, logEventMatcherIndex);
956 
957     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
958 
959     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
960             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
961                                 vector<std::shared_ptr<LogEvent>>* data) {
962                 data->clear();
963                 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1001, 5, 10));
964                 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1002, 10, 10));
965                 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 10, 1003, 15, 10));
966                 return true;
967             }))
968             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
969                                 vector<std::shared_ptr<LogEvent>>* data) {
970                 data->clear();
971                 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1001, 6, 10));
972                 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1002, 12, 10));
973                 data->push_back(makeUidLogEvent(tagId, bucketStartTimeNs + 20, 1003, 18, 10));
974                 return true;
975             }));
976     sp<MockConfigMetadataProvider> provider = makeMockConfigMetadataProvider(/*enabled=*/false);
977     GaugeMetricProducer gaugeProducer(
978             kConfigKey, sampledGaugeMetric, -1 /*-1 meaning no condition*/, {}, wizard, protoHash,
979             logEventMatcherIndex, eventMatcherWizard, tagId, triggerId, tagId, bucketStartTimeNs,
980             bucketStartTimeNs, pullerManager, provider);
981     SamplingInfo samplingInfo;
982     samplingInfo.shardCount = shardCount;
983     translateFieldMatcher(sampledGaugeMetric.dimensional_sampling_info().sampled_what_field(),
984                           &samplingInfo.sampledWhatFields);
985     gaugeProducer.setSamplingInfo(samplingInfo);
986     gaugeProducer.prepareFirstBucket();
987 
988     LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
989     CreateRepeatedValueLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10, 5);
990     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
991 
992     triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
993     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
994 
995     // Check dump report.
996     ProtoOutputStream output;
997     std::set<string> strSet;
998     int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000;
999     gaugeProducer.onDumpReport(dumpReportTimeNs, true /* include current buckets */, true,
1000                                NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
1001 
1002     StatsLogReport report = outputStreamToProto(&output);
1003     backfillDimensionPath(&report);
1004     backfillStartEndTimestamp(&report);
1005     backfillAggregatedAtoms(&report);
1006 
1007     EXPECT_TRUE(report.has_gauge_metrics());
1008     StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
1009     sortMetricDataByDimensionsValue(report.gauge_metrics(), &gaugeMetrics);
1010     ASSERT_EQ(2, gaugeMetrics.data_size());
1011     EXPECT_EQ(0, report.gauge_metrics().skipped_size());
1012 
1013     // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0
1014     GaugeMetricData data = gaugeMetrics.data(0);
1015     ValidateUidDimension(data.dimensions_in_what(), tagId, 1001);
1016     ValidateGaugeBucketTimes(data.bucket_info(0), bucketStartTimeNs, dumpReportTimeNs,
1017                              {bucketStartTimeNs + 10, bucketStartTimeNs + 20});
1018 
1019     data = gaugeMetrics.data(1);
1020     ValidateUidDimension(data.dimensions_in_what(), tagId, 1003);
1021     ValidateGaugeBucketTimes(data.bucket_info(0), bucketStartTimeNs, dumpReportTimeNs,
1022                              {bucketStartTimeNs + 10, bucketStartTimeNs + 20});
1023 }
1024 
1025 }  // namespace statsd
1026 }  // namespace os
1027 }  // namespace android
1028 #else
1029 GTEST_LOG_(INFO) << "This test does nothing.\n";
1030 #endif
1031