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 
102     // statsd started long ago.
103     // The metric starts in the middle of the bucket
104     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
105                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
106                                       -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
107                                       pullerManager);
108     gaugeProducer.prepareFirstBucket();
109 
110     EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
111     EXPECT_EQ(10, gaugeProducer.mCurrentBucketNum);
112     EXPECT_EQ(660000000005, gaugeProducer.getCurrentBucketEndTimeNs());
113 }
114 
TEST(GaugeMetricProducerTest,TestPulledEventsNoCondition)115 TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition) {
116     GaugeMetric metric;
117     metric.set_id(metricId);
118     metric.set_bucket(ONE_MINUTE);
119     metric.mutable_gauge_fields_filter()->set_include_all(false);
120     metric.set_max_pull_delay_sec(INT_MAX);
121     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
122     gaugeFieldMatcher->set_field(tagId);
123     gaugeFieldMatcher->add_child()->set_field(1);
124     gaugeFieldMatcher->add_child()->set_field(3);
125 
126     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
127 
128     sp<EventMatcherWizard> eventMatcherWizard =
129             createEventMatcherWizard(tagId, logEventMatcherIndex);
130 
131     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
132     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
133     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
134     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
135             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
136                                 vector<std::shared_ptr<LogEvent>>* data) {
137                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
138                 data->clear();
139                 data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
140                 return true;
141             }));
142 
143     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
144                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
145                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
146                                       pullerManager);
147     gaugeProducer.prepareFirstBucket();
148 
149     vector<shared_ptr<LogEvent>> allData;
150     allData.clear();
151     allData.push_back(makeLogEvent(tagId, bucket2StartTimeNs + 1, 10, "some value", 11));
152 
153     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
154     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
155     auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
156     EXPECT_EQ(INT, it->mValue.getType());
157     EXPECT_EQ(10, it->mValue.int_value);
158     it++;
159     EXPECT_EQ(11, it->mValue.int_value);
160     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
161     EXPECT_EQ(3, gaugeProducer.mPastBuckets.begin()
162                          ->second.back()
163                          .mGaugeAtoms.front()
164                          .mFields->begin()
165                          ->mValue.int_value);
166 
167     allData.clear();
168     allData.push_back(makeLogEvent(tagId, bucket3StartTimeNs + 10, 24, "some value", 25));
169     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
170     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
171     it = gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->begin();
172     EXPECT_EQ(INT, it->mValue.getType());
173     EXPECT_EQ(24, it->mValue.int_value);
174     it++;
175     EXPECT_EQ(INT, it->mValue.getType());
176     EXPECT_EQ(25, it->mValue.int_value);
177     // One dimension.
178     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
179     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
180     it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
181     EXPECT_EQ(INT, it->mValue.getType());
182     EXPECT_EQ(10L, it->mValue.int_value);
183     it++;
184     EXPECT_EQ(INT, it->mValue.getType());
185     EXPECT_EQ(11L, it->mValue.int_value);
186 
187     gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
188     ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
189     // One dimension.
190     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
191     ASSERT_EQ(3UL, gaugeProducer.mPastBuckets.begin()->second.size());
192     it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.front().mFields->begin();
193     EXPECT_EQ(INT, it->mValue.getType());
194     EXPECT_EQ(24L, it->mValue.int_value);
195     it++;
196     EXPECT_EQ(INT, it->mValue.getType());
197     EXPECT_EQ(25L, it->mValue.int_value);
198 }
199 
TEST_P(GaugeMetricProducerTest_PartialBucket,TestPushedEvents)200 TEST_P(GaugeMetricProducerTest_PartialBucket, TestPushedEvents) {
201     sp<AlarmMonitor> alarmMonitor;
202     GaugeMetric metric;
203     metric.set_id(metricId);
204     metric.set_bucket(ONE_MINUTE);
205     metric.mutable_gauge_fields_filter()->set_include_all(true);
206 
207     Alert alert;
208     alert.set_id(101);
209     alert.set_metric_id(metricId);
210     alert.set_trigger_if_sum_gt(25);
211     alert.set_num_buckets(100);
212     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
213     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
214 
215     sp<EventMatcherWizard> eventMatcherWizard =
216             createEventMatcherWizard(tagId, logEventMatcherIndex);
217 
218     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
219                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
220                                       -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
221                                       bucketStartTimeNs, pullerManager);
222     gaugeProducer.prepareFirstBucket();
223 
224     sp<AnomalyTracker> anomalyTracker =
225             gaugeProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
226     EXPECT_TRUE(anomalyTracker != nullptr);
227 
228     LogEvent event1(/*uid=*/0, /*pid=*/0);
229     CreateTwoValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10);
230     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
231     EXPECT_EQ(1UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
232 
233     switch (GetParam()) {
234         case APP_UPGRADE:
235             gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
236             break;
237         case BOOT_COMPLETE:
238             gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
239             break;
240     }
241     EXPECT_EQ(0UL, (*gaugeProducer.mCurrentSlicedBucket).count(DEFAULT_METRIC_DIMENSION_KEY));
242     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
243     EXPECT_EQ(bucketStartTimeNs,
244               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
245     EXPECT_EQ(partialBucketSplitTimeNs,
246               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
247     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
248     EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
249     // Partial buckets are not sent to anomaly tracker.
250     EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
251 
252     // Create an event in the same partial bucket.
253     LogEvent event2(/*uid=*/0, /*pid=*/0);
254     CreateTwoValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 1, 10);
255     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
256     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
257     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
258     EXPECT_EQ(bucketStartTimeNs,
259               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
260     EXPECT_EQ(partialBucketSplitTimeNs,
261               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
262     EXPECT_EQ((int64_t)partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
263     // Partial buckets are not sent to anomaly tracker.
264     EXPECT_EQ(0, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
265 
266     // Next event should trigger creation of new bucket and send previous full bucket to anomaly
267     // tracker.
268     LogEvent event3(/*uid=*/0, /*pid=*/0);
269     CreateTwoValueLogEvent(&event3, tagId, bucketStartTimeNs + 65 * NS_PER_SEC, 1, 10);
270     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
271     EXPECT_EQ(1L, gaugeProducer.mCurrentBucketNum);
272     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
273     EXPECT_EQ((int64_t)bucketStartTimeNs + bucketSizeNs, gaugeProducer.mCurrentBucketStartTimeNs);
274     EXPECT_EQ(1, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
275 
276     // Next event should trigger creation of new bucket.
277     LogEvent event4(/*uid=*/0, /*pid=*/0);
278     CreateTwoValueLogEvent(&event4, tagId, bucketStartTimeNs + 125 * NS_PER_SEC, 1, 10);
279     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
280     EXPECT_EQ(2L, gaugeProducer.mCurrentBucketNum);
281     ASSERT_EQ(3UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
282     EXPECT_EQ(2, anomalyTracker->getSumOverPastBuckets(DEFAULT_METRIC_DIMENSION_KEY));
283 }
284 
TEST_P(GaugeMetricProducerTest_PartialBucket,TestPulled)285 TEST_P(GaugeMetricProducerTest_PartialBucket, TestPulled) {
286     GaugeMetric metric;
287     metric.set_id(metricId);
288     metric.set_bucket(ONE_MINUTE);
289     metric.set_max_pull_delay_sec(INT_MAX);
290     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
291     gaugeFieldMatcher->set_field(tagId);
292     gaugeFieldMatcher->add_child()->set_field(2);
293 
294     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
295 
296     sp<EventMatcherWizard> eventMatcherWizard =
297             createEventMatcherWizard(tagId, logEventMatcherIndex);
298 
299     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
300     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
301     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
302     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
303             .WillOnce(Return(false))
304             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
305                                 vector<std::shared_ptr<LogEvent>>* data) {
306                 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
307                 data->clear();
308                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
309                 return true;
310             }));
311 
312     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
313                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
314                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
315                                       pullerManager);
316     gaugeProducer.prepareFirstBucket();
317 
318     vector<shared_ptr<LogEvent>> allData;
319     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
320     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
321     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
322     EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
323                          ->second.front()
324                          .mFields->begin()
325                          ->mValue.int_value);
326 
327     switch (GetParam()) {
328         case APP_UPGRADE:
329             gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
330             break;
331         case BOOT_COMPLETE:
332             gaugeProducer.onStatsdInitCompleted(partialBucketSplitTimeNs);
333             break;
334     }
335     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
336     EXPECT_EQ(bucketStartTimeNs,
337               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketStartNs);
338     EXPECT_EQ(partialBucketSplitTimeNs,
339               gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mBucketEndNs);
340     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
341     EXPECT_EQ(partialBucketSplitTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
342     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
343     EXPECT_EQ(2, gaugeProducer.mCurrentSlicedBucket->begin()
344                          ->second.front()
345                          .mFields->begin()
346                          ->mValue.int_value);
347 
348     allData.clear();
349     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 1, 3));
350     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
351     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
352     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
353     EXPECT_EQ(3, gaugeProducer.mCurrentSlicedBucket->begin()
354                          ->second.front()
355                          .mFields->begin()
356                          ->mValue.int_value);
357 }
358 
TEST(GaugeMetricProducerTest,TestPulledWithAppUpgradeDisabled)359 TEST(GaugeMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
360     GaugeMetric metric;
361     metric.set_id(metricId);
362     metric.set_bucket(ONE_MINUTE);
363     metric.set_max_pull_delay_sec(INT_MAX);
364     metric.set_split_bucket_for_app_upgrade(false);
365     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
366     gaugeFieldMatcher->set_field(tagId);
367     gaugeFieldMatcher->add_child()->set_field(2);
368 
369     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
370 
371     sp<EventMatcherWizard> eventMatcherWizard =
372             createEventMatcherWizard(tagId, logEventMatcherIndex);
373 
374     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
375     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
376     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
377     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
378             .WillOnce(Return(false));
379 
380     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
381                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
382                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
383                                       pullerManager);
384     gaugeProducer.prepareFirstBucket();
385 
386     vector<shared_ptr<LogEvent>> allData;
387     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
388     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
389     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
390     EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
391                          ->second.front()
392                          .mFields->begin()
393                          ->mValue.int_value);
394 
395     gaugeProducer.notifyAppUpgrade(partialBucketSplitTimeNs);
396     ASSERT_EQ(0UL, gaugeProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
397     EXPECT_EQ(0L, gaugeProducer.mCurrentBucketNum);
398     EXPECT_EQ(bucketStartTimeNs, gaugeProducer.mCurrentBucketStartTimeNs);
399     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
400     EXPECT_EQ(1, gaugeProducer.mCurrentSlicedBucket->begin()
401                          ->second.front()
402                          .mFields->begin()
403                          ->mValue.int_value);
404 }
405 
TEST(GaugeMetricProducerTest,TestPulledEventsWithCondition)406 TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition) {
407     GaugeMetric metric;
408     metric.set_id(metricId);
409     metric.set_bucket(ONE_MINUTE);
410     metric.set_max_pull_delay_sec(INT_MAX);
411     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
412     gaugeFieldMatcher->set_field(tagId);
413     gaugeFieldMatcher->add_child()->set_field(2);
414     metric.set_condition(StringToId("SCREEN_ON"));
415 
416     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
417 
418     sp<EventMatcherWizard> eventMatcherWizard =
419             createEventMatcherWizard(tagId, logEventMatcherIndex);
420 
421     int64_t conditionChangeNs = bucketStartTimeNs + 8;
422 
423     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
424     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
425     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
426     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
427             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
428                                 vector<std::shared_ptr<LogEvent>>* data) {
429                 data->clear();
430                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
431                 return true;
432             }));
433 
434     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
435                                       {ConditionState::kUnknown}, wizard, protoHash,
436                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
437                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
438     gaugeProducer.prepareFirstBucket();
439 
440     gaugeProducer.onConditionChanged(true, conditionChangeNs);
441     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
442     EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()
443                            ->second.front()
444                            .mFields->begin()
445                            ->mValue.int_value);
446     ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
447 
448     vector<shared_ptr<LogEvent>> allData;
449     allData.clear();
450     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
451     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
452 
453     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
454     EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()
455                            ->second.front()
456                            .mFields->begin()
457                            ->mValue.int_value);
458     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
459     EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()
460                            ->second.back()
461                            .mGaugeAtoms.front()
462                            .mFields->begin()
463                            ->mValue.int_value);
464 
465     gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
466     gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
467     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
468     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
469     EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()
470                             ->second.back()
471                             .mGaugeAtoms.front()
472                             .mFields->begin()
473                             ->mValue.int_value);
474 }
475 
TEST(GaugeMetricProducerTest,TestPulledEventsWithSlicedCondition)476 TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition) {
477     const int conditionTag = 65;
478     GaugeMetric metric;
479     metric.set_id(1111111);
480     metric.set_bucket(ONE_MINUTE);
481     metric.mutable_gauge_fields_filter()->set_include_all(true);
482     metric.set_condition(StringToId("APP_DIED"));
483     metric.set_max_pull_delay_sec(INT_MAX);
484     auto dim = metric.mutable_dimensions_in_what();
485     dim->set_field(tagId);
486     dim->add_child()->set_field(1);
487 
488     sp<EventMatcherWizard> eventMatcherWizard =
489             createEventMatcherWizard(tagId, logEventMatcherIndex);
490 
491     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
492     EXPECT_CALL(*wizard, query(_, _, _))
493             .WillRepeatedly(
494                     Invoke([](const int conditionIndex, const ConditionKey& conditionParameters,
495                               const bool isPartialLink) {
496                         int pos[] = {1, 0, 0};
497                         Field f(conditionTag, pos, 0);
498                         HashableDimensionKey key;
499                         key.mutableValues()->emplace_back(f, Value((int32_t)1000000));
500 
501                         return ConditionState::kTrue;
502                     }));
503 
504     int64_t sliceConditionChangeNs = bucketStartTimeNs + 8;
505 
506     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
507     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
508     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
509     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
510             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
511                                 vector<std::shared_ptr<LogEvent>>* data) {
512                 data->clear();
513                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
514                 return true;
515             }));
516 
517     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
518                                       {ConditionState::kUnknown}, wizard, protoHash,
519                                       logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
520                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
521     gaugeProducer.prepareFirstBucket();
522 
523     gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
524 
525     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
526     const auto& key = gaugeProducer.mCurrentSlicedBucket->begin()->first;
527     ASSERT_EQ(1UL, key.getDimensionKeyInWhat().getValues().size());
528     EXPECT_EQ(1000, key.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
529 
530     ASSERT_EQ(0UL, gaugeProducer.mPastBuckets.size());
531 
532     vector<shared_ptr<LogEvent>> allData;
533     allData.clear();
534     allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1000, 110));
535     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
536 
537     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
538     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
539 }
540 
TEST(GaugeMetricProducerTest,TestPulledEventsAnomalyDetection)541 TEST(GaugeMetricProducerTest, TestPulledEventsAnomalyDetection) {
542     sp<AlarmMonitor> alarmMonitor;
543     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
544 
545     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
546     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
547     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
548     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
549             .WillOnce(Return(false));
550 
551     GaugeMetric metric;
552     metric.set_id(metricId);
553     metric.set_bucket(ONE_MINUTE);
554     metric.set_max_pull_delay_sec(INT_MAX);
555     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
556     gaugeFieldMatcher->set_field(tagId);
557     gaugeFieldMatcher->add_child()->set_field(2);
558 
559     sp<EventMatcherWizard> eventMatcherWizard =
560             createEventMatcherWizard(tagId, logEventMatcherIndex);
561 
562     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
563                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
564                                       tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
565                                       pullerManager);
566     gaugeProducer.prepareFirstBucket();
567 
568     Alert alert;
569     alert.set_id(101);
570     alert.set_metric_id(metricId);
571     alert.set_trigger_if_sum_gt(25);
572     alert.set_num_buckets(2);
573     const int32_t refPeriodSec = 60;
574     alert.set_refractory_period_secs(refPeriodSec);
575     sp<AnomalyTracker> anomalyTracker =
576             gaugeProducer.addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
577 
578     int tagId = 1;
579     vector<shared_ptr<LogEvent>> allData;
580     allData.clear();
581     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 13));
582     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
583     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
584     EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()
585                            ->second.front()
586                            .mFields->begin()
587                            ->mValue.int_value);
588     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
589 
590     std::shared_ptr<LogEvent> event2 =
591             CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + bucketSizeNs + 20, 15);
592 
593     allData.clear();
594     allData.push_back(event2);
595     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + bucketSizeNs);
596     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
597     EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()
598                            ->second.front()
599                            .mFields->begin()
600                            ->mValue.int_value);
601     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
602               std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC) + refPeriodSec);
603 
604     allData.clear();
605     allData.push_back(
606             CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10, 26));
607     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 2 * bucketSizeNs);
608     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
609     EXPECT_EQ(26L, gaugeProducer.mCurrentSlicedBucket->begin()
610                            ->second.front()
611                            .mFields->begin()
612                            ->mValue.int_value);
613     EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
614               std::ceil(1.0 * event2->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
615 
616     // This event does not have the gauge field. Thus the current bucket value is 0.
617     allData.clear();
618     allData.push_back(CreateNoValuesLogEvent(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10));
619     gaugeProducer.onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 3 * bucketSizeNs);
620     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
621     EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second.front().mFields->empty());
622 }
623 
TEST(GaugeMetricProducerTest,TestPullOnTrigger)624 TEST(GaugeMetricProducerTest, TestPullOnTrigger) {
625     GaugeMetric metric;
626     metric.set_id(metricId);
627     metric.set_bucket(ONE_MINUTE);
628     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
629     metric.mutable_gauge_fields_filter()->set_include_all(false);
630     metric.set_max_pull_delay_sec(INT_MAX);
631     auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
632     gaugeFieldMatcher->set_field(tagId);
633     gaugeFieldMatcher->add_child()->set_field(1);
634 
635     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
636 
637     sp<EventMatcherWizard> eventMatcherWizard =
638             createEventMatcherWizard(tagId, logEventMatcherIndex);
639 
640     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
641     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
642             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
643                                 vector<std::shared_ptr<LogEvent>>* data) {
644                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
645                 data->clear();
646                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
647                 return true;
648             }))
649             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
650                                 vector<std::shared_ptr<LogEvent>>* data) {
651                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
652                 data->clear();
653                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
654                 return true;
655             }))
656             .WillOnce(Return(true));
657 
658     int triggerId = 5;
659     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
660                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
661                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
662                                       pullerManager);
663     gaugeProducer.prepareFirstBucket();
664 
665     ASSERT_EQ(0UL, gaugeProducer.mCurrentSlicedBucket->size());
666 
667     LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
668     CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 10);
669     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
670     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
671     triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
672     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
673     ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
674     triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
675     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
676 
677     ASSERT_EQ(1UL, gaugeProducer.mPastBuckets.size());
678     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mGaugeAtoms.size());
679     EXPECT_EQ(4, gaugeProducer.mPastBuckets.begin()
680                          ->second.back()
681                          .mGaugeAtoms[0]
682                          .mFields->begin()
683                          ->mValue.int_value);
684     EXPECT_EQ(5, gaugeProducer.mPastBuckets.begin()
685                          ->second.back()
686                          .mGaugeAtoms[1]
687                          .mFields->begin()
688                          ->mValue.int_value);
689 }
690 
TEST(GaugeMetricProducerTest,TestRemoveDimensionInOutput)691 TEST(GaugeMetricProducerTest, TestRemoveDimensionInOutput) {
692     GaugeMetric metric;
693     metric.set_id(metricId);
694     metric.set_bucket(ONE_MINUTE);
695     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
696     metric.mutable_gauge_fields_filter()->set_include_all(true);
697     metric.set_max_pull_delay_sec(INT_MAX);
698     auto dimensionMatcher = metric.mutable_dimensions_in_what();
699     // use field 1 as dimension.
700     dimensionMatcher->set_field(tagId);
701     dimensionMatcher->add_child()->set_field(1);
702 
703     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
704 
705     sp<EventMatcherWizard> eventMatcherWizard =
706             createEventMatcherWizard(tagId, logEventMatcherIndex);
707 
708     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
709     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
710             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
711                                 vector<std::shared_ptr<LogEvent>>* data) {
712                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
713                 data->clear();
714                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
715                 return true;
716             }))
717             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
718                                 vector<std::shared_ptr<LogEvent>>* data) {
719                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
720                 data->clear();
721                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
722                 return true;
723             }))
724             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
725                                 vector<std::shared_ptr<LogEvent>>* data) {
726                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
727                 data->clear();
728                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
729                 return true;
730             }))
731             .WillOnce(Return(true));
732 
733     int triggerId = 5;
734     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
735                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
736                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
737                                       pullerManager);
738     gaugeProducer.prepareFirstBucket();
739 
740     LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
741     CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
742     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
743     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
744     triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 10);
745     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
746     ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->size());
747     ASSERT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
748     triggerEvent.setElapsedTimestampNs(bucketStartTimeNs + 20);
749     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
750     ASSERT_EQ(2UL, gaugeProducer.mCurrentSlicedBucket->begin()->second.size());
751     triggerEvent.setElapsedTimestampNs(bucket2StartTimeNs + 1);
752     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
753 
754     ASSERT_EQ(2UL, gaugeProducer.mPastBuckets.size());
755     auto bucketIt = gaugeProducer.mPastBuckets.begin();
756     ASSERT_EQ(1UL, bucketIt->second.back().mGaugeAtoms.size());
757     EXPECT_EQ(3, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
758     EXPECT_EQ(4, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
759     bucketIt++;
760     ASSERT_EQ(2UL, bucketIt->second.back().mGaugeAtoms.size());
761     EXPECT_EQ(4, bucketIt->first.getDimensionKeyInWhat().getValues().begin()->mValue.int_value);
762     EXPECT_EQ(5, bucketIt->second.back().mGaugeAtoms[0].mFields->begin()->mValue.int_value);
763     EXPECT_EQ(6, bucketIt->second.back().mGaugeAtoms[1].mFields->begin()->mValue.int_value);
764 }
765 
766 /*
767  * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
768  * is smaller than the "min_bucket_size_nanos" specified in the metric config.
769  */
TEST(GaugeMetricProducerTest_BucketDrop,TestBucketDropWhenBucketTooSmall)770 TEST(GaugeMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
771     GaugeMetric metric;
772     metric.set_id(metricId);
773     metric.set_bucket(FIVE_MINUTES);
774     metric.set_sampling_type(GaugeMetric::FIRST_N_SAMPLES);
775     metric.set_min_bucket_size_nanos(10000000000);  // 10 seconds
776 
777     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
778 
779     sp<EventMatcherWizard> eventMatcherWizard =
780             createEventMatcherWizard(tagId, logEventMatcherIndex);
781 
782     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
783     EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
784             // Bucket start.
785             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
786                                 vector<std::shared_ptr<LogEvent>>* data) {
787                 data->clear();
788                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
789                 return true;
790             }));
791 
792     int triggerId = 5;
793     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
794                                       wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
795                                       tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
796                                       pullerManager);
797     gaugeProducer.prepareFirstBucket();
798 
799     LogEvent triggerEvent(/*uid=*/0, /*pid=*/0);
800     CreateNoValuesLogEvent(&triggerEvent, triggerId, bucketStartTimeNs + 3);
801     gaugeProducer.onMatchedLogEvent(1 /*log matcher index*/, triggerEvent);
802 
803     // Check dump report.
804     ProtoOutputStream output;
805     std::set<string> strSet;
806     gaugeProducer.onDumpReport(bucketStartTimeNs + 9000000, true /* include recent buckets */, true,
807                                FAST /* dump_latency */, &strSet, &output);
808 
809     StatsLogReport report = outputStreamToProto(&output);
810     EXPECT_TRUE(report.has_gauge_metrics());
811     ASSERT_EQ(0, report.gauge_metrics().data_size());
812     ASSERT_EQ(1, report.gauge_metrics().skipped_size());
813 
814     EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
815               report.gauge_metrics().skipped(0).start_bucket_elapsed_millis());
816     EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000),
817               report.gauge_metrics().skipped(0).end_bucket_elapsed_millis());
818     ASSERT_EQ(1, report.gauge_metrics().skipped(0).drop_event_size());
819 
820     auto dropEvent = report.gauge_metrics().skipped(0).drop_event(0);
821     EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
822     EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 9000000), dropEvent.drop_time_millis());
823 }
824 
825 }  // namespace statsd
826 }  // namespace os
827 }  // namespace android
828 #else
829 GTEST_LOG_(INFO) << "This test does nothing.\n";
830 #endif
831