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