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/NumericValueMetricProducer.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 "metrics_test_helper.h"
25 #include "src/FieldValue.h"
26 #include "src/matchers/SimpleAtomMatchingTracker.h"
27 #include "src/metrics/MetricProducer.h"
28 #include "src/stats_log_util.h"
29 #include "tests/statsd_test_util.h"
30
31 using namespace testing;
32 using android::sp;
33 using std::make_shared;
34 using std::nullopt;
35 using std::optional;
36 using std::set;
37 using std::shared_ptr;
38 using std::unordered_map;
39 using std::vector;
40
41 #ifdef __ANDROID__
42
43 namespace android {
44 namespace os {
45 namespace statsd {
46
47 namespace {
48
49 const ConfigKey kConfigKey(0, 12345);
50 const int tagId = 1;
51 const int64_t metricId = 123;
52 const uint64_t protoHash = 0x1234567890;
53 const int logEventMatcherIndex = 0;
54 const int64_t bucketStartTimeNs = 10000000000;
55 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
56 const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
57 const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
58 const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
59 const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs;
60 const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs;
61 double epsilon = 0.001;
62
assertPastBucketValuesSingleKey(const std::unordered_map<MetricDimensionKey,std::vector<PastBucket<Value>>> & mPastBuckets,const std::initializer_list<int> & expectedValuesList,const std::initializer_list<int64_t> & expectedDurationNsList,const std::initializer_list<int64_t> & expectedCorrectionNsList,const std::initializer_list<int64_t> & expectedStartTimeNsList,const std::initializer_list<int64_t> & expectedEndTimeNsList)63 static void assertPastBucketValuesSingleKey(
64 const std::unordered_map<MetricDimensionKey, std::vector<PastBucket<Value>>>& mPastBuckets,
65 const std::initializer_list<int>& expectedValuesList,
66 const std::initializer_list<int64_t>& expectedDurationNsList,
67 const std::initializer_list<int64_t>& expectedCorrectionNsList,
68 const std::initializer_list<int64_t>& expectedStartTimeNsList,
69 const std::initializer_list<int64_t>& expectedEndTimeNsList) {
70 vector<int> expectedValues(expectedValuesList);
71 vector<int64_t> expectedDurationNs(expectedDurationNsList);
72 vector<int64_t> expectedCorrectionNs(expectedCorrectionNsList);
73 vector<int64_t> expectedStartTimeNs(expectedStartTimeNsList);
74 vector<int64_t> expectedEndTimeNs(expectedEndTimeNsList);
75
76 ASSERT_EQ(expectedValues.size(), expectedDurationNs.size());
77 ASSERT_EQ(expectedValues.size(), expectedStartTimeNs.size());
78 ASSERT_EQ(expectedValues.size(), expectedEndTimeNs.size());
79 ASSERT_EQ(expectedValues.size(), expectedCorrectionNs.size());
80
81 if (expectedValues.size() == 0) {
82 ASSERT_EQ(0, mPastBuckets.size());
83 return;
84 }
85
86 ASSERT_EQ(1, mPastBuckets.size());
87 ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
88
89 const vector<PastBucket<Value>>& buckets = mPastBuckets.begin()->second;
90 for (int i = 0; i < expectedValues.size(); i++) {
91 EXPECT_EQ(expectedValues[i], buckets[i].aggregates[0].long_value)
92 << "Values differ at index " << i;
93 EXPECT_EQ(expectedDurationNs[i], buckets[i].mConditionTrueNs)
94 << "Condition duration value differ at index " << i;
95 EXPECT_EQ(expectedStartTimeNs[i], buckets[i].mBucketStartNs)
96 << "Start time differs at index " << i;
97 EXPECT_EQ(expectedEndTimeNs[i], buckets[i].mBucketEndNs)
98 << "End time differs at index " << i;
99 EXPECT_EQ(expectedCorrectionNs[i], buckets[i].mConditionCorrectionNs)
100 << "Condition correction differs at index " << i;
101 }
102 }
103
assertConditionTimer(const ConditionTimer & conditionTimer,bool condition,int64_t timerNs,int64_t lastConditionTrueTimestampNs,int64_t currentBucketStartDelayNs=0)104 static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition,
105 int64_t timerNs, int64_t lastConditionTrueTimestampNs,
106 int64_t currentBucketStartDelayNs = 0) {
107 EXPECT_EQ(condition, conditionTimer.mCondition);
108 EXPECT_EQ(timerNs, conditionTimer.mTimerNs);
109 EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs);
110 EXPECT_EQ(currentBucketStartDelayNs, conditionTimer.mCurrentBucketStartDelayNs);
111 }
112
113 } // anonymous namespace
114
115 class NumericValueMetricProducerTestHelper {
116 public:
createValueProducerNoConditions(sp<MockStatsPullerManager> & pullerManager,ValueMetric & metric,const int pullAtomId=tagId)117 static sp<NumericValueMetricProducer> createValueProducerNoConditions(
118 sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
119 const int pullAtomId = tagId) {
120 return createValueProducer(pullerManager, metric, pullAtomId);
121 }
122
createValueProducerWithCondition(sp<MockStatsPullerManager> & pullerManager,ValueMetric & metric,ConditionState conditionAfterFirstBucketPrepared,const int pullAtomId=tagId)123 static sp<NumericValueMetricProducer> createValueProducerWithCondition(
124 sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
125 ConditionState conditionAfterFirstBucketPrepared, const int pullAtomId = tagId) {
126 return createValueProducer(pullerManager, metric, pullAtomId,
127 conditionAfterFirstBucketPrepared);
128 }
129
createValueProducerWithState(sp<MockStatsPullerManager> & pullerManager,ValueMetric & metric,vector<int32_t> slicedStateAtoms,unordered_map<int,unordered_map<int,int64_t>> stateGroupMap,const int pullAtomId=tagId)130 static sp<NumericValueMetricProducer> createValueProducerWithState(
131 sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
132 vector<int32_t> slicedStateAtoms,
133 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap,
134 const int pullAtomId = tagId) {
135 return createValueProducer(pullerManager, metric, pullAtomId,
136 /*conditionAfterFirstBucketPrepared=*/nullopt, slicedStateAtoms,
137 stateGroupMap);
138 }
139
createValueProducerWithConditionAndState(sp<MockStatsPullerManager> & pullerManager,ValueMetric & metric,vector<int32_t> slicedStateAtoms,unordered_map<int,unordered_map<int,int64_t>> stateGroupMap,ConditionState conditionAfterFirstBucketPrepared,const int pullAtomId=tagId)140 static sp<NumericValueMetricProducer> createValueProducerWithConditionAndState(
141 sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
142 vector<int32_t> slicedStateAtoms,
143 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap,
144 ConditionState conditionAfterFirstBucketPrepared, const int pullAtomId = tagId) {
145 return createValueProducer(pullerManager, metric, pullAtomId,
146 conditionAfterFirstBucketPrepared, slicedStateAtoms,
147 stateGroupMap);
148 }
149
createValueProducerWithBucketParams(sp<MockStatsPullerManager> & pullerManager,ValueMetric & metric,const int64_t timeBaseNs,const int64_t startTimeNs,const int pullAtomId=tagId)150 static sp<NumericValueMetricProducer> createValueProducerWithBucketParams(
151 sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
152 const int64_t timeBaseNs, const int64_t startTimeNs, const int pullAtomId = tagId) {
153 return createValueProducer(
154 pullerManager, metric, pullAtomId, /*conditionAfterFirstBucketPrepared=*/nullopt,
155 /*slicedStateAtoms=*/{}, /*stateGroupMap=*/{}, timeBaseNs, startTimeNs);
156 }
157
createValueProducerWithEventMatcherWizard(sp<MockStatsPullerManager> & pullerManager,ValueMetric & metric,const sp<EventMatcherWizard> & eventMatcherWizard,const int pullAtomId=tagId)158 static sp<NumericValueMetricProducer> createValueProducerWithEventMatcherWizard(
159 sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
160 const sp<EventMatcherWizard>& eventMatcherWizard, const int pullAtomId = tagId) {
161 return createValueProducer(pullerManager, metric, pullAtomId,
162 /*conditionAfterFirstBucketPrepared=*/nullopt,
163 /*slicedStateAtoms=*/{}, /*stateGroupMap=*/{}, bucketStartTimeNs,
164 bucketStartTimeNs, eventMatcherWizard);
165 }
166
createValueProducer(sp<MockStatsPullerManager> & pullerManager,ValueMetric & metric,const int pullAtomId,optional<ConditionState> conditionAfterFirstBucketPrepared=nullopt,vector<int32_t> slicedStateAtoms={},unordered_map<int,unordered_map<int,int64_t>> stateGroupMap={},const int64_t timeBaseNs=bucketStartTimeNs,const int64_t startTimeNs=bucketStartTimeNs,sp<EventMatcherWizard> eventMatcherWizard=nullptr)167 static sp<NumericValueMetricProducer> createValueProducer(
168 sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric, const int pullAtomId,
169 optional<ConditionState> conditionAfterFirstBucketPrepared = nullopt,
170 vector<int32_t> slicedStateAtoms = {},
171 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap = {},
172 const int64_t timeBaseNs = bucketStartTimeNs,
173 const int64_t startTimeNs = bucketStartTimeNs,
174 sp<EventMatcherWizard> eventMatcherWizard = nullptr) {
175 if (eventMatcherWizard == nullptr) {
176 eventMatcherWizard = createEventMatcherWizard(tagId, logEventMatcherIndex);
177 }
178 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
179 if (pullAtomId != -1) {
180 EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
181 .WillOnce(Return());
182 EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _))
183 .WillRepeatedly(Return());
184 }
185 const int64_t bucketSizeNs = MillisToNano(
186 TimeUnitToBucketSizeInMillisGuardrailed(kConfigKey.GetUid(), metric.bucket()));
187 const bool containsAnyPositionInDimensionsInWhat =
188 HasPositionANY(metric.dimensions_in_what());
189 const bool shouldUseNestedDimensions =
190 ShouldUseNestedDimensions(metric.dimensions_in_what());
191
192 vector<Matcher> fieldMatchers;
193 translateFieldMatcher(metric.value_field(), &fieldMatchers);
194
195 const auto [dimensionSoftLimit, dimensionHardLimit] =
196 StatsdStats::getAtomDimensionKeySizeLimits(tagId);
197
198 int conditionIndex = conditionAfterFirstBucketPrepared ? 0 : -1;
199 vector<ConditionState> initialConditionCache;
200 if (conditionAfterFirstBucketPrepared) {
201 initialConditionCache.push_back(ConditionState::kUnknown);
202 }
203
204 // get the condition_correction_threshold_nanos value
205 const optional<int64_t> conditionCorrectionThresholdNs =
206 metric.has_condition_correction_threshold_nanos()
207 ? optional<int64_t>(metric.condition_correction_threshold_nanos())
208 : nullopt;
209
210 sp<NumericValueMetricProducer> valueProducer = new NumericValueMetricProducer(
211 kConfigKey, metric, protoHash, {pullAtomId, pullerManager},
212 {timeBaseNs, startTimeNs, bucketSizeNs, metric.min_bucket_size_nanos(),
213 conditionCorrectionThresholdNs, metric.split_bucket_for_app_upgrade()},
214 {containsAnyPositionInDimensionsInWhat, shouldUseNestedDimensions,
215 logEventMatcherIndex, eventMatcherWizard, metric.dimensions_in_what(),
216 fieldMatchers},
217 {conditionIndex, metric.links(), initialConditionCache, wizard},
218 {metric.state_link(), slicedStateAtoms, stateGroupMap},
219 {/*eventActivationMap=*/{}, /*eventDeactivationMap=*/{}},
220 {dimensionSoftLimit, dimensionHardLimit});
221
222 valueProducer->prepareFirstBucket();
223 if (conditionAfterFirstBucketPrepared) {
224 valueProducer->mCondition = conditionAfterFirstBucketPrepared.value();
225 }
226 return valueProducer;
227 }
228
createMetric()229 static ValueMetric createMetric() {
230 ValueMetric metric;
231 metric.set_id(metricId);
232 metric.set_bucket(ONE_MINUTE);
233 metric.mutable_value_field()->set_field(tagId);
234 metric.mutable_value_field()->add_child()->set_field(2);
235 metric.set_max_pull_delay_sec(INT_MAX);
236 metric.set_split_bucket_for_app_upgrade(true);
237 return metric;
238 }
239
createMetricWithCondition()240 static ValueMetric createMetricWithCondition() {
241 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
242 metric.set_condition(StringToId("SCREEN_ON"));
243 return metric;
244 }
245
createMetricWithState(string state)246 static ValueMetric createMetricWithState(string state) {
247 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
248 metric.add_slice_by_state(StringToId(state));
249 return metric;
250 }
251
createMetricWithConditionAndState(string state)252 static ValueMetric createMetricWithConditionAndState(string state) {
253 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
254 metric.set_condition(StringToId("SCREEN_ON"));
255 metric.add_slice_by_state(StringToId(state));
256 return metric;
257 }
258
createMetricWithRepeatedValueField()259 static ValueMetric createMetricWithRepeatedValueField() {
260 ValueMetric metric;
261 metric.set_id(metricId);
262 metric.set_bucket(ONE_MINUTE);
263 metric.mutable_value_field()->set_field(tagId);
264 FieldMatcher* valueChild = metric.mutable_value_field()->add_child();
265 valueChild->set_field(3);
266 valueChild->set_position(Position::FIRST);
267 metric.set_max_pull_delay_sec(INT_MAX);
268 metric.set_split_bucket_for_app_upgrade(true);
269 metric.set_aggregation_type(ValueMetric_AggregationType_SUM);
270 return metric;
271 }
272 };
273
274 // Setup for parameterized tests.
275 class NumericValueMetricProducerTest_PartialBucket : public TestWithParam<BucketSplitEvent> {};
276
277 INSTANTIATE_TEST_SUITE_P(NumericValueMetricProducerTest_PartialBucket,
278 NumericValueMetricProducerTest_PartialBucket,
279 testing::Values(APP_UPGRADE, BOOT_COMPLETE));
280
281 /*
282 * Tests that the first bucket works correctly
283 */
TEST(NumericValueMetricProducerTest,TestCalcPreviousBucketEndTime)284 TEST(NumericValueMetricProducerTest, TestCalcPreviousBucketEndTime) {
285 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
286
287 int64_t startTimeBase = 11;
288 sp<EventMatcherWizard> eventMatcherWizard =
289 createEventMatcherWizard(tagId, logEventMatcherIndex);
290 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
291 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
292
293 // statsd started long ago.
294 // The metric starts in the middle of the bucket
295 sp<NumericValueMetricProducer> valueProducer =
296 NumericValueMetricProducerTestHelper::createValueProducerWithBucketParams(
297 pullerManager, metric, startTimeBase, /*startTimeNs=*/22, /*pullAtomId=*/-1);
298
299 EXPECT_EQ(startTimeBase, valueProducer->calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
300 EXPECT_EQ(startTimeBase, valueProducer->calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
301 EXPECT_EQ(60 * NS_PER_SEC + startTimeBase,
302 valueProducer->calcPreviousBucketEndTime(2 * 60 * NS_PER_SEC));
303 EXPECT_EQ(2 * 60 * NS_PER_SEC + startTimeBase,
304 valueProducer->calcPreviousBucketEndTime(3 * 60 * NS_PER_SEC));
305 }
306
307 /*
308 * Tests that the first bucket works correctly
309 */
TEST(NumericValueMetricProducerTest,TestFirstBucket)310 TEST(NumericValueMetricProducerTest, TestFirstBucket) {
311 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
312
313 sp<EventMatcherWizard> eventMatcherWizard =
314 createEventMatcherWizard(tagId, logEventMatcherIndex);
315 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
316 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
317
318 // statsd started long ago.
319 // The metric starts in the middle of the bucket
320 sp<NumericValueMetricProducer> valueProducer =
321 NumericValueMetricProducerTestHelper::createValueProducerWithBucketParams(
322 pullerManager, metric, /*timeBaseNs=*/5,
323 /*startTimeNs=*/600 * NS_PER_SEC + NS_PER_SEC / 2, /*pullAtomId=*/-1);
324
325 EXPECT_EQ(600500000000, valueProducer->mCurrentBucketStartTimeNs);
326 EXPECT_EQ(10, valueProducer->mCurrentBucketNum);
327 EXPECT_EQ(660000000005, valueProducer->getCurrentBucketEndTimeNs());
328 }
329
330 /*
331 * Tests pulled atoms with no conditions
332 */
TEST(NumericValueMetricProducerTest,TestPulledEventsNoCondition)333 TEST(NumericValueMetricProducerTest, TestPulledEventsNoCondition) {
334 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
335 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
336 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
337 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
338 vector<std::shared_ptr<LogEvent>>* data) {
339 data->clear();
340 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
341 return true;
342 }));
343
344 sp<NumericValueMetricProducer> valueProducer =
345 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
346 metric);
347
348 vector<shared_ptr<LogEvent>> allData;
349 allData.clear();
350 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
351
352 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
353 // empty since bucket is flushed
354 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
355 // dimInfos holds the base
356 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
357 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
358
359 EXPECT_EQ(true, curBase.has_value());
360 EXPECT_EQ(11, curBase.value().long_value);
361 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
362 {bucketStartTimeNs}, {bucket2StartTimeNs});
363
364 allData.clear();
365 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
366 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
367 // empty since bucket is cleared
368 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
369 // dimInfos holds the base
370 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
371 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
372
373 EXPECT_EQ(true, curBase.has_value());
374 EXPECT_EQ(23, curBase.value().long_value);
375 assertPastBucketValuesSingleKey(
376 valueProducer->mPastBuckets, {8, 12}, {bucketSizeNs, bucketSizeNs}, {0, 0},
377 {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
378
379 allData.clear();
380 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
381 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
382 // empty since bucket is cleared
383 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
384 // dimInfos holds the base
385 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
386 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
387
388 EXPECT_EQ(true, curBase.has_value());
389 EXPECT_EQ(36, curBase.value().long_value);
390 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8, 12, 13},
391 {bucketSizeNs, bucketSizeNs, bucketSizeNs}, {0, 0, 0},
392 {bucketStartTimeNs, bucket2StartTimeNs, bucket3StartTimeNs},
393 {bucket2StartTimeNs, bucket3StartTimeNs, bucket4StartTimeNs});
394 }
395
TEST_P(NumericValueMetricProducerTest_PartialBucket,TestPartialBucketCreated)396 TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPartialBucketCreated) {
397 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
398 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
399 int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
400 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
401 // Initialize bucket.
402 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
403 vector<std::shared_ptr<LogEvent>>* data) {
404 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
405 data->clear();
406 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
407 return true;
408 }))
409 // Partial bucket.
410 .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
411 const int64_t eventTimeNs,
412 vector<std::shared_ptr<LogEvent>>* data) {
413 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
414 data->clear();
415 data->push_back(
416 CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs + 8, 5));
417 return true;
418 }));
419
420 sp<NumericValueMetricProducer> valueProducer =
421 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
422 metric);
423
424 // First bucket ends.
425 vector<shared_ptr<LogEvent>> allData;
426 allData.clear();
427 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 2));
428 valueProducer->onDataPulled(allData, /** success */ true, bucket2StartTimeNs);
429
430 // Partial buckets created in 2nd bucket.
431 switch (GetParam()) {
432 case APP_UPGRADE:
433 valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
434 break;
435 case BOOT_COMPLETE:
436 valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
437 break;
438 }
439 EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
440 EXPECT_EQ(1, valueProducer->getCurrentBucketNum());
441
442 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1, 3},
443 {bucketSizeNs, partialBucketSplitTimeNs - bucket2StartTimeNs},
444 {0, 0}, {bucketStartTimeNs, bucket2StartTimeNs},
445 {bucket2StartTimeNs, partialBucketSplitTimeNs});
446 }
447
448 /*
449 * Tests pulled atoms with filtering
450 */
TEST(NumericValueMetricProducerTest,TestPulledEventsWithFiltering)451 TEST(NumericValueMetricProducerTest, TestPulledEventsWithFiltering) {
452 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
453
454 FieldValueMatcher fvm;
455 fvm.set_field(1);
456 fvm.set_eq_int(3);
457 sp<EventMatcherWizard> eventMatcherWizard =
458 createEventMatcherWizard(tagId, logEventMatcherIndex, {fvm});
459 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
460 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
461 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
462 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
463 vector<std::shared_ptr<LogEvent>>* data) {
464 data->clear();
465 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
466 return true;
467 }));
468
469 sp<NumericValueMetricProducer> valueProducer =
470 NumericValueMetricProducerTestHelper::createValueProducerWithEventMatcherWizard(
471 pullerManager, metric, eventMatcherWizard);
472
473 vector<shared_ptr<LogEvent>> allData;
474 allData.clear();
475 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 3, 11));
476
477 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
478 // empty since bucket is cleared
479 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
480 // dimInfos holds the base
481 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
482 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
483
484 EXPECT_EQ(true, curBase.has_value());
485 EXPECT_EQ(11, curBase.value().long_value);
486 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
487 {bucketStartTimeNs}, {bucket2StartTimeNs});
488
489 allData.clear();
490 allData.push_back(CreateTwoValueLogEvent(tagId, bucket3StartTimeNs + 1, 4, 23));
491 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
492 // No new data seen, so data has been cleared.
493 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
494 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
495
496 allData.clear();
497 allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
498 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
499 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
500 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
501 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
502
503 // the base was reset
504 EXPECT_EQ(true, curBase.has_value());
505 EXPECT_EQ(36, curBase.value().long_value);
506 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
507 {bucketStartTimeNs}, {bucket2StartTimeNs});
508 }
509
510 /*
511 * Tests pulled atoms with no conditions and take absolute value after reset
512 */
TEST(NumericValueMetricProducerTest,TestPulledEventsTakeAbsoluteValueOnReset)513 TEST(NumericValueMetricProducerTest, TestPulledEventsTakeAbsoluteValueOnReset) {
514 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
515 metric.set_use_absolute_value_on_reset(true);
516
517 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
518 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
519 .WillOnce(Return(true));
520 sp<NumericValueMetricProducer> valueProducer =
521 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
522 metric);
523
524 vector<shared_ptr<LogEvent>> allData;
525 allData.clear();
526 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
527
528 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
529 // empty since bucket is cleared
530 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
531 // dimInfos holds the base
532 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
533 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
534
535 EXPECT_EQ(true, curBase.has_value());
536 EXPECT_EQ(11, curBase.value().long_value);
537 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
538
539 allData.clear();
540 // 10 is less than 11, so we reset and keep 10 as the value.
541 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
542 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
543 // empty since the bucket is flushed.
544 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
545 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
546 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
547 EXPECT_EQ(true, curBase.has_value());
548 EXPECT_EQ(10, curBase.value().long_value);
549 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, {0},
550 {bucket2StartTimeNs}, {bucket3StartTimeNs});
551
552 allData.clear();
553 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
554 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
555 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
556 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
557 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
558 EXPECT_EQ(true, curBase.has_value());
559 EXPECT_EQ(36, curBase.value().long_value);
560 assertPastBucketValuesSingleKey(
561 valueProducer->mPastBuckets, {10, 26}, {bucketSizeNs, bucketSizeNs}, {0, 0},
562 {bucket2StartTimeNs, bucket3StartTimeNs}, {bucket3StartTimeNs, bucket4StartTimeNs});
563 }
564
565 /*
566 * Tests pulled atoms with no conditions and take zero value after reset
567 */
TEST(NumericValueMetricProducerTest,TestPulledEventsTakeZeroOnReset)568 TEST(NumericValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
569 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
570 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
571 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
572 .WillOnce(Return(false));
573 sp<NumericValueMetricProducer> valueProducer =
574 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
575 metric);
576
577 vector<shared_ptr<LogEvent>> allData;
578 allData.clear();
579 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
580
581 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
582 // empty since bucket is cleared
583 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
584 // mDimInfos holds the base
585 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
586 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
587
588 EXPECT_EQ(true, curBase.has_value());
589 EXPECT_EQ(11, curBase.value().long_value);
590 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
591
592 allData.clear();
593 // 10 is less than 11, so we reset. 10 only updates the base.
594 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 10));
595 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
596 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
597 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
598 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
599 EXPECT_EQ(true, curBase.has_value());
600 EXPECT_EQ(10, curBase.value().long_value);
601 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
602
603 allData.clear();
604 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
605 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
606 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
607 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
608 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
609 EXPECT_EQ(true, curBase.has_value());
610 EXPECT_EQ(36, curBase.value().long_value);
611 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {26}, {bucketSizeNs}, {0},
612 {bucket3StartTimeNs}, {bucket4StartTimeNs});
613 }
614
615 /*
616 * Test pulled event with non sliced condition.
617 */
TEST(NumericValueMetricProducerTest,TestEventsWithNonSlicedCondition)618 TEST(NumericValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
619 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
620
621 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
622
623 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
624 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
625 vector<std::shared_ptr<LogEvent>>* data) {
626 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
627 data->clear();
628 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
629 return true;
630 }))
631 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
632 vector<std::shared_ptr<LogEvent>>* data) {
633 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
634 data->clear();
635 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
636 return true;
637 }))
638 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
639 vector<std::shared_ptr<LogEvent>>* data) {
640 EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1); // Third condition change.
641 data->clear();
642 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
643 return true;
644 }));
645
646 sp<NumericValueMetricProducer> valueProducer =
647 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
648 pullerManager, metric, ConditionState::kFalse);
649
650 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
651
652 // has one slice
653 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
654 NumericValueMetricProducer::Interval curInterval =
655 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
656 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
657 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
658 // startUpdated:false sum:0 start:100
659 EXPECT_EQ(true, curBase.has_value());
660 EXPECT_EQ(100, curBase.value().long_value);
661 EXPECT_EQ(0, curInterval.sampleSize);
662 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
663
664 vector<shared_ptr<LogEvent>> allData;
665 allData.clear();
666 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
667 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
668 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, {0},
669 {bucketStartTimeNs}, {bucket2StartTimeNs});
670
671 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
672 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
673 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
674 EXPECT_EQ(true, curBase.has_value());
675 EXPECT_EQ(110, curBase.value().long_value);
676
677 valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
678 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs - 8}, {0},
679 {bucketStartTimeNs}, {bucket2StartTimeNs});
680
681 // has one slice
682 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
683 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
684 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
685 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
686 EXPECT_TRUE(curInterval.hasValue());
687 EXPECT_EQ(20, curInterval.aggregate.long_value);
688 EXPECT_EQ(false, curBase.has_value());
689
690 valueProducer->onConditionChanged(true, bucket3StartTimeNs + 1);
691 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20}, {bucketSizeNs - 8, 1},
692 {0, 0}, {bucketStartTimeNs, bucket2StartTimeNs},
693 {bucket2StartTimeNs, bucket3StartTimeNs});
694 }
695
TEST_P(NumericValueMetricProducerTest_PartialBucket,TestPushedEvents)696 TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPushedEvents) {
697 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
698
699 sp<EventMatcherWizard> eventMatcherWizard =
700 createEventMatcherWizard(tagId, logEventMatcherIndex);
701 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
702 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
703
704 sp<NumericValueMetricProducer> valueProducer =
705 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
706 pullerManager, metric, /*pullAtomId=*/-1);
707
708 LogEvent event1(/*uid=*/0, /*pid=*/0);
709 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
710 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
711 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
712
713 int64_t partialBucketSplitTimeNs = bucketStartTimeNs + 150;
714 switch (GetParam()) {
715 case APP_UPGRADE:
716 valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
717 break;
718 case BOOT_COMPLETE:
719 valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
720 break;
721 }
722 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10},
723 {partialBucketSplitTimeNs - bucketStartTimeNs}, {0},
724 {bucketStartTimeNs}, {partialBucketSplitTimeNs});
725 EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
726 EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
727
728 // Event arrives after the bucket split.
729 LogEvent event2(/*uid=*/0, /*pid=*/0);
730 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 59 * NS_PER_SEC, 20);
731 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
732
733 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10},
734 {partialBucketSplitTimeNs - bucketStartTimeNs}, {0},
735 {bucketStartTimeNs}, {partialBucketSplitTimeNs});
736 EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
737 EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
738
739 // Next value should create a new bucket.
740 LogEvent event3(/*uid=*/0, /*pid=*/0);
741 CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 5 * NS_PER_SEC, 10);
742 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
743 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10, 20},
744 {partialBucketSplitTimeNs - bucketStartTimeNs,
745 bucket2StartTimeNs - partialBucketSplitTimeNs},
746 {0, 5 * NS_PER_SEC},
747 {bucketStartTimeNs, partialBucketSplitTimeNs},
748 {partialBucketSplitTimeNs, bucket2StartTimeNs});
749 EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, valueProducer->mCurrentBucketStartTimeNs);
750 EXPECT_EQ(1, valueProducer->getCurrentBucketNum());
751 }
752
TEST_P(NumericValueMetricProducerTest_PartialBucket,TestPulledValue)753 TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPulledValue) {
754 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
755
756 sp<EventMatcherWizard> eventMatcherWizard =
757 createEventMatcherWizard(tagId, logEventMatcherIndex);
758 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
759 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
760 int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
761 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
762 .WillOnce(Return(true))
763 .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
764 const int64_t eventTimeNs,
765 vector<std::shared_ptr<LogEvent>>* data) {
766 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
767 data->clear();
768 data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
769 return true;
770 }));
771
772 sp<NumericValueMetricProducer> valueProducer =
773 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
774 metric);
775
776 vector<shared_ptr<LogEvent>> allData;
777 allData.clear();
778 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
779
780 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
781 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
782
783 switch (GetParam()) {
784 case APP_UPGRADE:
785 valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
786 break;
787 case BOOT_COMPLETE:
788 valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
789 break;
790 }
791 EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
792 EXPECT_EQ(1, valueProducer->getCurrentBucketNum());
793 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {150}, {0},
794 {bucket2StartTimeNs}, {partialBucketSplitTimeNs});
795
796 allData.clear();
797 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 150));
798 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
799 EXPECT_EQ(bucket3StartTimeNs, valueProducer->mCurrentBucketStartTimeNs);
800 EXPECT_EQ(2, valueProducer->getCurrentBucketNum());
801 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20, 30},
802 {150, bucketSizeNs - 150}, {0, 0},
803 {bucket2StartTimeNs, partialBucketSplitTimeNs},
804 {partialBucketSplitTimeNs, bucket3StartTimeNs});
805 }
806
TEST(NumericValueMetricProducerTest,TestPulledWithAppUpgradeDisabled)807 TEST(NumericValueMetricProducerTest, TestPulledWithAppUpgradeDisabled) {
808 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
809 metric.set_split_bucket_for_app_upgrade(false);
810
811 sp<EventMatcherWizard> eventMatcherWizard =
812 createEventMatcherWizard(tagId, logEventMatcherIndex);
813 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
814 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
815 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
816 .WillOnce(Return(true));
817
818 sp<NumericValueMetricProducer> valueProducer =
819 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
820 metric);
821
822 vector<shared_ptr<LogEvent>> allData;
823 allData.clear();
824 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 100));
825
826 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
827 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
828 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
829
830 valueProducer->notifyAppUpgrade(bucket2StartTimeNs + 150);
831 ASSERT_EQ(0UL, valueProducer->mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
832 EXPECT_EQ(bucket2StartTimeNs, valueProducer->mCurrentBucketStartTimeNs);
833 }
834
TEST_P(NumericValueMetricProducerTest_PartialBucket,TestPulledValueWhileConditionFalse)835 TEST_P(NumericValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse) {
836 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
837
838 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
839 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
840 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
841 vector<std::shared_ptr<LogEvent>>* data) {
842 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
843 data->clear();
844 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
845 return true;
846 }))
847 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
848 vector<std::shared_ptr<LogEvent>>* data) {
849 EXPECT_EQ(eventTimeNs,
850 bucket2StartTimeNs - 100); // Condition change to false time.
851 data->clear();
852 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 120));
853 return true;
854 }));
855 sp<NumericValueMetricProducer> valueProducer =
856 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
857 pullerManager, metric, ConditionState::kFalse);
858
859 valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
860
861 valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
862 EXPECT_FALSE(valueProducer->mCondition);
863
864 int64_t partialBucketSplitTimeNs = bucket2StartTimeNs - 50;
865 switch (GetParam()) {
866 case APP_UPGRADE:
867 valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
868 break;
869 case BOOT_COMPLETE:
870 valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
871 break;
872 }
873 // Expect one full buckets already done and starting a partial bucket.
874 EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
875 EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
876 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20},
877 {(bucket2StartTimeNs - 100) - (bucketStartTimeNs + 1)}, {0},
878 {bucketStartTimeNs}, {partialBucketSplitTimeNs});
879 EXPECT_FALSE(valueProducer->mCondition);
880 }
881
TEST(NumericValueMetricProducerTest,TestPushedEventsWithoutCondition)882 TEST(NumericValueMetricProducerTest, TestPushedEventsWithoutCondition) {
883 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
884
885 sp<EventMatcherWizard> eventMatcherWizard =
886 createEventMatcherWizard(tagId, logEventMatcherIndex);
887 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
888 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
889
890 sp<NumericValueMetricProducer> valueProducer =
891 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
892 pullerManager, metric, /*pullAtomId=*/-1);
893
894 LogEvent event1(/*uid=*/0, /*pid=*/0);
895 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
896
897 LogEvent event2(/*uid=*/0, /*pid=*/0);
898 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
899
900 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
901 // has one slice
902 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
903 NumericValueMetricProducer::Interval curInterval =
904 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
905 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
906 EXPECT_EQ(10, curInterval.aggregate.long_value);
907 EXPECT_TRUE(curInterval.hasValue());
908
909 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
910
911 // has one slice
912 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
913 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
914 EXPECT_EQ(30, curInterval.aggregate.long_value);
915
916 valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
917 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}, {0},
918 {bucketStartTimeNs}, {bucket2StartTimeNs});
919 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
920 }
921
TEST(NumericValueMetricProducerTest,TestPushedEventsWithCondition)922 TEST(NumericValueMetricProducerTest, TestPushedEventsWithCondition) {
923 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
924
925 sp<EventMatcherWizard> eventMatcherWizard =
926 createEventMatcherWizard(tagId, logEventMatcherIndex);
927 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
928 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
929
930 sp<NumericValueMetricProducer> valueProducer =
931 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
932 pullerManager, metric, ConditionState::kFalse, /*pullAtomId=*/-1);
933
934 LogEvent event1(/*uid=*/0, /*pid=*/0);
935 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
936 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
937 // has 1 slice
938 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
939
940 valueProducer->onConditionChangedLocked(true, bucketStartTimeNs + 15);
941
942 LogEvent event2(/*uid=*/0, /*pid=*/0);
943 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
944 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
945
946 // has one slice
947 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
948 NumericValueMetricProducer::Interval curInterval =
949 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
950 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
951 EXPECT_EQ(20, curInterval.aggregate.long_value);
952
953 LogEvent event3(/*uid=*/0, /*pid=*/0);
954 CreateRepeatedValueLogEvent(&event3, tagId, bucketStartTimeNs + 30, 30);
955 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
956
957 // has one slice
958 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
959 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
960 EXPECT_EQ(50, curInterval.aggregate.long_value);
961
962 valueProducer->onConditionChangedLocked(false, bucketStartTimeNs + 35);
963
964 LogEvent event4(/*uid=*/0, /*pid=*/0);
965 CreateRepeatedValueLogEvent(&event4, tagId, bucketStartTimeNs + 40, 40);
966 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
967
968 // has one slice
969 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
970 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
971 EXPECT_EQ(50, curInterval.aggregate.long_value);
972
973 valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
974 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {50}, {20}, {0},
975 {bucketStartTimeNs}, {bucket2StartTimeNs});
976 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
977 }
978
TEST(NumericValueMetricProducerTest,TestAnomalyDetection)979 TEST(NumericValueMetricProducerTest, TestAnomalyDetection) {
980 sp<AlarmMonitor> alarmMonitor;
981 Alert alert;
982 alert.set_id(101);
983 alert.set_metric_id(metricId);
984 alert.set_trigger_if_sum_gt(130);
985 alert.set_num_buckets(2);
986 const int32_t refPeriodSec = 3;
987 alert.set_refractory_period_secs(refPeriodSec);
988
989 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
990
991 sp<EventMatcherWizard> eventMatcherWizard =
992 createEventMatcherWizard(tagId, logEventMatcherIndex);
993 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
994 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
995
996 sp<NumericValueMetricProducer> valueProducer =
997 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
998 pullerManager, metric, /*pullAtomId=*/-1);
999
1000 sp<AnomalyTracker> anomalyTracker =
1001 valueProducer->addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
1002
1003 LogEvent event1(/*uid=*/0, /*pid=*/0);
1004 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 1 * NS_PER_SEC, 10);
1005
1006 LogEvent event2(/*uid=*/0, /*pid=*/0);
1007 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 2 + NS_PER_SEC, 20);
1008
1009 LogEvent event3(/*uid=*/0, /*pid=*/0);
1010 CreateRepeatedValueLogEvent(&event3, tagId,
1011 bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC, 130);
1012
1013 LogEvent event4(/*uid=*/0, /*pid=*/0);
1014 CreateRepeatedValueLogEvent(&event4, tagId,
1015 bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC, 1);
1016
1017 LogEvent event5(/*uid=*/0, /*pid=*/0);
1018 CreateRepeatedValueLogEvent(&event5, tagId,
1019 bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC, 150);
1020
1021 LogEvent event6(/*uid=*/0, /*pid=*/0);
1022 CreateRepeatedValueLogEvent(&event6, tagId,
1023 bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC, 160);
1024
1025 // Two events in bucket #0.
1026 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
1027 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
1028 // Value sum == 30 <= 130.
1029 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
1030
1031 // One event in bucket #2. No alarm as bucket #0 is trashed out.
1032 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
1033 // Value sum == 130 <= 130.
1034 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
1035
1036 // Three events in bucket #3.
1037 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
1038 // Anomaly at event 4 since Value sum == 131 > 130!
1039 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
1040 std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
1041 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event5);
1042 // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
1043 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
1044 std::ceil(1.0 * event4.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
1045
1046 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event6);
1047 // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
1048 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
1049 std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
1050 }
1051
TEST(NumericValueMetricProducerTest,TestAnomalyDetectionMultipleBucketsSkipped)1052 TEST(NumericValueMetricProducerTest, TestAnomalyDetectionMultipleBucketsSkipped) {
1053 sp<AlarmMonitor> alarmMonitor;
1054 Alert alert;
1055 alert.set_id(101);
1056 alert.set_metric_id(metricId);
1057 alert.set_trigger_if_sum_gt(100);
1058 alert.set_num_buckets(1);
1059 const int32_t refPeriodSec = 3;
1060 alert.set_refractory_period_secs(refPeriodSec);
1061
1062 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
1063
1064 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1065 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
1066 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1067 vector<std::shared_ptr<LogEvent>>* data) {
1068 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to true time.
1069 data->clear();
1070 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 0));
1071 return true;
1072 }))
1073 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1074 vector<std::shared_ptr<LogEvent>>* data) {
1075 EXPECT_EQ(eventTimeNs,
1076 bucket3StartTimeNs + 100); // Condition changed to false time.
1077 data->clear();
1078 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 100, 120));
1079 return true;
1080 }));
1081 sp<NumericValueMetricProducer> valueProducer =
1082 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
1083 pullerManager, metric, ConditionState::kFalse);
1084 sp<AnomalyTracker> anomalyTracker =
1085 valueProducer->addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
1086
1087 valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
1088
1089 // multiple buckets should be skipped here.
1090 valueProducer->onConditionChanged(false, bucket3StartTimeNs + 100);
1091
1092 // No alert is fired when multiple buckets are skipped.
1093 EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
1094 }
1095
1096 // Test value metric no condition, the pull on bucket boundary come in time and too late
TEST(NumericValueMetricProducerTest,TestBucketBoundaryNoCondition)1097 TEST(NumericValueMetricProducerTest, TestBucketBoundaryNoCondition) {
1098 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1099 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1100 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
1101 .WillOnce(Return(true));
1102 sp<NumericValueMetricProducer> valueProducer =
1103 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
1104 metric);
1105
1106 vector<shared_ptr<LogEvent>> allData;
1107 // pull 1
1108 allData.clear();
1109 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 11));
1110
1111 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
1112 // empty since bucket is finished
1113 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1114 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1115 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1116
1117 // startUpdated:true sum:0 start:11
1118 EXPECT_EQ(true, curBase.has_value());
1119 EXPECT_EQ(11, curBase.value().long_value);
1120 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
1121
1122 // pull 2 at correct time
1123 allData.clear();
1124 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 23));
1125 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
1126 // empty since bucket is finished
1127 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1128 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1129 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1130 // tartUpdated:false sum:12
1131 EXPECT_EQ(true, curBase.has_value());
1132 EXPECT_EQ(23, curBase.value().long_value);
1133 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, {0},
1134 {bucket2StartTimeNs}, {bucket3StartTimeNs});
1135
1136 // pull 3 come late.
1137 // The previous bucket gets closed with error. (Has start value 23, no ending)
1138 // Another bucket gets closed with error. (No start, but ending with 36)
1139 // The new bucket is back to normal.
1140 allData.clear();
1141 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
1142 valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
1143 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1144 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1145 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1146 // startUpdated:false sum:12
1147 EXPECT_EQ(true, curBase.has_value());
1148 EXPECT_EQ(36, curBase.value().long_value);
1149 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {12}, {bucketSizeNs}, {0},
1150 {bucket2StartTimeNs}, {bucket3StartTimeNs});
1151 // The 1st bucket is dropped because of no data
1152 // The 3rd bucket is dropped due to multiple buckets being skipped.
1153 ASSERT_EQ(2, valueProducer->mSkippedBuckets.size());
1154
1155 EXPECT_EQ(bucketStartTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
1156 EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
1157 ASSERT_EQ(1, valueProducer->mSkippedBuckets[0].dropEvents.size());
1158 EXPECT_EQ(NO_DATA, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
1159 EXPECT_EQ(bucket2StartTimeNs, valueProducer->mSkippedBuckets[0].dropEvents[0].dropTimeNs);
1160
1161 EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[1].bucketStartTimeNs);
1162 EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].bucketEndTimeNs);
1163 ASSERT_EQ(1, valueProducer->mSkippedBuckets[1].dropEvents.size());
1164 EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[1].dropEvents[0].reason);
1165 EXPECT_EQ(bucket6StartTimeNs, valueProducer->mSkippedBuckets[1].dropEvents[0].dropTimeNs);
1166 }
1167
1168 /*
1169 * Test pulled event with non sliced condition. The pull on boundary come late because the alarm
1170 * was delivered late.
1171 */
TEST(NumericValueMetricProducerTest,TestBucketBoundaryWithCondition)1172 TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition) {
1173 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
1174
1175 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1176 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
1177 // condition becomes true
1178 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1179 vector<std::shared_ptr<LogEvent>>* data) {
1180 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // First condition change.
1181 data->clear();
1182 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
1183 return true;
1184 }))
1185 // condition becomes false
1186 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1187 vector<std::shared_ptr<LogEvent>>* data) {
1188 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1); // Second condition change.
1189 data->clear();
1190 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
1191 return true;
1192 }));
1193 sp<NumericValueMetricProducer> valueProducer =
1194 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
1195 pullerManager, metric, ConditionState::kFalse);
1196
1197 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
1198
1199 // has one slice
1200 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1201 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1202 NumericValueMetricProducer::Interval curInterval =
1203 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1204 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1205 EXPECT_EQ(true, curBase.has_value());
1206 EXPECT_EQ(100, curBase.value().long_value);
1207 EXPECT_EQ(0, curInterval.sampleSize);
1208 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
1209
1210 // pull on bucket boundary come late, condition change happens before it
1211 valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
1212 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1213 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1214 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1215 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
1216 {bucketStartTimeNs}, {bucket2StartTimeNs});
1217 EXPECT_EQ(false, curBase.has_value());
1218
1219 // Now the alarm is delivered.
1220 // since the condition turned to off before this pull finish, it has no effect
1221 vector<shared_ptr<LogEvent>> allData;
1222 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
1223 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
1224
1225 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
1226 {bucketStartTimeNs}, {bucket2StartTimeNs});
1227 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1228 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1229 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1230 EXPECT_EQ(false, curBase.has_value());
1231 }
1232
1233 /*
1234 * Test pulled event with non sliced condition. The pull on boundary come late, after the condition
1235 * change to false, and then true again. This is due to alarm delivered late.
1236 */
TEST(NumericValueMetricProducerTest,TestBucketBoundaryWithCondition2)1237 TEST(NumericValueMetricProducerTest, TestBucketBoundaryWithCondition2) {
1238 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
1239
1240 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1241 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
1242 // condition becomes true
1243 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1244 vector<std::shared_ptr<LogEvent>>* data) {
1245 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
1246 data->clear();
1247 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
1248 return true;
1249 }))
1250 // condition becomes false
1251 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1252 vector<std::shared_ptr<LogEvent>>* data) {
1253 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
1254 data->clear();
1255 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
1256 return true;
1257 }))
1258 // condition becomes true again
1259 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1260 vector<std::shared_ptr<LogEvent>>* data) {
1261 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
1262 data->clear();
1263 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
1264 return true;
1265 }));
1266
1267 sp<NumericValueMetricProducer> valueProducer =
1268 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
1269 pullerManager, metric, ConditionState::kFalse);
1270
1271 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
1272
1273 // has one slice
1274 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1275 NumericValueMetricProducer::Interval curInterval =
1276 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1277 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1278 // startUpdated:false sum:0 start:100
1279 EXPECT_EQ(true, curBase.has_value());
1280 EXPECT_EQ(100, curBase.value().long_value);
1281 EXPECT_EQ(0, curInterval.sampleSize);
1282 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
1283
1284 // pull on bucket boundary come late, condition change happens before it
1285 valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
1286 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
1287 {bucketStartTimeNs}, {bucket2StartTimeNs});
1288 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1289 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1290 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1291 EXPECT_EQ(false, curBase.has_value());
1292
1293 // condition changed to true again, before the pull alarm is delivered
1294 valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
1295 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
1296 {bucketStartTimeNs}, {bucket2StartTimeNs});
1297 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1298 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1299 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1300 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1301 EXPECT_EQ(true, curBase.has_value());
1302 EXPECT_EQ(130, curBase.value().long_value);
1303 EXPECT_EQ(0, curInterval.sampleSize);
1304
1305 // Now the alarm is delivered, but it is considered late, the data will be used
1306 // for the new bucket since it was just pulled.
1307 vector<shared_ptr<LogEvent>> allData;
1308 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
1309 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
1310
1311 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1312 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1313 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1314 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1315 EXPECT_EQ(true, curBase.has_value());
1316 EXPECT_EQ(140, curBase.value().long_value);
1317 EXPECT_TRUE(curInterval.hasValue());
1318 EXPECT_EQ(10, curInterval.aggregate.long_value);
1319 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8}, {1},
1320 {bucketStartTimeNs}, {bucket2StartTimeNs});
1321
1322 allData.clear();
1323 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 160));
1324 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
1325 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1326 assertPastBucketValuesSingleKey(
1327 valueProducer->mPastBuckets, {20, 30}, {bucketSizeNs - 8, bucketSizeNs - 24}, {1, -1},
1328 {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
1329 }
1330
TEST(NumericValueMetricProducerTest,TestPushedAggregateMin)1331 TEST(NumericValueMetricProducerTest, TestPushedAggregateMin) {
1332 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1333 metric.set_aggregation_type(ValueMetric::MIN);
1334
1335 sp<EventMatcherWizard> eventMatcherWizard =
1336 createEventMatcherWizard(tagId, logEventMatcherIndex);
1337 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
1338 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1339
1340 sp<NumericValueMetricProducer> valueProducer =
1341 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
1342 pullerManager, metric, /*pullAtomId=*/-1);
1343
1344 LogEvent event1(/*uid=*/0, /*pid=*/0);
1345 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
1346
1347 LogEvent event2(/*uid=*/0, /*pid=*/0);
1348 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
1349
1350 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
1351 // has one slice
1352 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1353 NumericValueMetricProducer::Interval curInterval =
1354 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1355 EXPECT_EQ(10, curInterval.aggregate.long_value);
1356 EXPECT_TRUE(curInterval.hasValue());
1357
1358 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
1359
1360 // has one slice
1361 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1362 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1363 EXPECT_EQ(10, curInterval.aggregate.long_value);
1364
1365 valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
1366 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1367 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, {0},
1368 {bucketStartTimeNs}, {bucket2StartTimeNs});
1369 }
1370
TEST(NumericValueMetricProducerTest,TestPushedAggregateMax)1371 TEST(NumericValueMetricProducerTest, TestPushedAggregateMax) {
1372 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1373 metric.set_aggregation_type(ValueMetric::MAX);
1374
1375 sp<EventMatcherWizard> eventMatcherWizard =
1376 createEventMatcherWizard(tagId, logEventMatcherIndex);
1377 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
1378 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1379
1380 sp<NumericValueMetricProducer> valueProducer =
1381 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
1382 pullerManager, metric, /*pullAtomId=*/-1);
1383
1384 LogEvent event1(/*uid=*/0, /*pid=*/0);
1385 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
1386 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
1387
1388 // has one slice
1389 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1390 NumericValueMetricProducer::Interval curInterval =
1391 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1392 EXPECT_EQ(10, curInterval.aggregate.long_value);
1393 EXPECT_TRUE(curInterval.hasValue());
1394
1395 LogEvent event2(/*uid=*/0, /*pid=*/0);
1396 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 20);
1397 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
1398
1399 // has one slice
1400 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1401 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1402 EXPECT_EQ(20, curInterval.aggregate.long_value);
1403
1404 valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
1405 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs}, {0},
1406 {bucketStartTimeNs}, {bucket2StartTimeNs});
1407 }
1408
TEST(NumericValueMetricProducerTest,TestPushedAggregateAvg)1409 TEST(NumericValueMetricProducerTest, TestPushedAggregateAvg) {
1410 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1411 metric.set_aggregation_type(ValueMetric::AVG);
1412
1413 sp<EventMatcherWizard> eventMatcherWizard =
1414 createEventMatcherWizard(tagId, logEventMatcherIndex);
1415 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
1416 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1417
1418 sp<NumericValueMetricProducer> valueProducer =
1419 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
1420 pullerManager, metric, /*pullAtomId=*/-1);
1421
1422 LogEvent event1(/*uid=*/0, /*pid=*/0);
1423 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
1424
1425 LogEvent event2(/*uid=*/0, /*pid=*/0);
1426 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
1427 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
1428 // has one slice
1429 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1430 NumericValueMetricProducer::Interval curInterval;
1431 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1432 EXPECT_EQ(1, curInterval.sampleSize);
1433 EXPECT_EQ(10, curInterval.aggregate.long_value);
1434
1435 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
1436
1437 // has one slice
1438 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1439 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1440 EXPECT_EQ(25, curInterval.aggregate.long_value);
1441 EXPECT_EQ(2, curInterval.sampleSize);
1442
1443 valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
1444 ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
1445 ASSERT_EQ(1UL, valueProducer->mPastBuckets.begin()->second.size());
1446
1447 EXPECT_TRUE(
1448 std::abs(valueProducer->mPastBuckets.begin()->second.back().aggregates[0].double_value -
1449 12.5) < epsilon);
1450 }
1451
TEST(NumericValueMetricProducerTest,TestPushedAggregateSum)1452 TEST(NumericValueMetricProducerTest, TestPushedAggregateSum) {
1453 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1454 metric.set_aggregation_type(ValueMetric::SUM);
1455
1456 sp<EventMatcherWizard> eventMatcherWizard =
1457 createEventMatcherWizard(tagId, logEventMatcherIndex);
1458 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
1459 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1460
1461 sp<NumericValueMetricProducer> valueProducer =
1462 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
1463 pullerManager, metric, /*pullAtomId=*/-1);
1464
1465 LogEvent event1(/*uid=*/0, /*pid=*/0);
1466 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
1467
1468 LogEvent event2(/*uid=*/0, /*pid=*/0);
1469 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 20, 15);
1470 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
1471 // has one slice
1472 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1473 NumericValueMetricProducer::Interval curInterval =
1474 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1475 EXPECT_EQ(10, curInterval.aggregate.long_value);
1476 EXPECT_TRUE(curInterval.hasValue());
1477
1478 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
1479
1480 // has one slice
1481 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1482 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1483 EXPECT_EQ(25, curInterval.aggregate.long_value);
1484
1485 valueProducer->flushIfNeededLocked(bucket2StartTimeNs);
1486 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {25}, {bucketSizeNs}, {0},
1487 {bucketStartTimeNs}, {bucket2StartTimeNs});
1488 }
1489
TEST(NumericValueMetricProducerTest,TestSkipZeroDiffOutput)1490 TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutput) {
1491 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1492 metric.set_aggregation_type(ValueMetric::MIN);
1493 metric.set_use_diff(true);
1494
1495 sp<EventMatcherWizard> eventMatcherWizard =
1496 createEventMatcherWizard(tagId, logEventMatcherIndex);
1497 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
1498 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1499
1500 sp<NumericValueMetricProducer> valueProducer =
1501 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
1502 pullerManager, metric, /*pullAtomId=*/-1);
1503
1504 LogEvent event1(/*uid=*/0, /*pid=*/0);
1505 CreateRepeatedValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 10);
1506 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
1507
1508 // has one slice
1509 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1510 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1511 NumericValueMetricProducer::Interval curInterval =
1512 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1513 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1514 EXPECT_EQ(true, curBase.has_value());
1515 EXPECT_EQ(10, curBase.value().long_value);
1516 EXPECT_EQ(0, curInterval.sampleSize);
1517
1518 LogEvent event2(/*uid=*/0, /*pid=*/0);
1519 CreateRepeatedValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 15);
1520 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
1521
1522 // has one slice
1523 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1524 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1525 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1526 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1527 EXPECT_EQ(true, curBase.has_value());
1528 EXPECT_EQ(15, curBase.value().long_value);
1529 EXPECT_TRUE(curInterval.hasValue());
1530 EXPECT_EQ(5, curInterval.aggregate.long_value);
1531
1532 // no change in data.
1533 LogEvent event3(/*uid=*/0, /*pid=*/0);
1534 CreateRepeatedValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 15);
1535 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
1536
1537 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1538 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1539 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1540 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1541 EXPECT_EQ(true, curBase.has_value());
1542 EXPECT_EQ(15, curBase.value().long_value);
1543 EXPECT_TRUE(curInterval.hasValue());
1544 EXPECT_EQ(0, curInterval.aggregate.long_value);
1545
1546 LogEvent event4(/*uid=*/0, /*pid=*/0);
1547 CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
1548 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
1549 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1550 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1551 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1552 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1553 EXPECT_EQ(true, curBase.has_value());
1554 EXPECT_EQ(15, curBase.value().long_value);
1555 EXPECT_TRUE(curInterval.hasValue());
1556 EXPECT_EQ(0, curInterval.aggregate.long_value);
1557
1558 valueProducer->flushIfNeededLocked(bucket3StartTimeNs);
1559 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {10},
1560 {bucketStartTimeNs}, {bucket2StartTimeNs});
1561 }
1562
TEST(NumericValueMetricProducerTest,TestSkipZeroDiffOutputMultiValue)1563 TEST(NumericValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) {
1564 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1565 metric.mutable_value_field()->add_child()->set_field(3);
1566 metric.set_aggregation_type(ValueMetric::MIN);
1567 metric.set_use_diff(true);
1568
1569 sp<EventMatcherWizard> eventMatcherWizard =
1570 createEventMatcherWizard(tagId, logEventMatcherIndex);
1571 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
1572 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1573
1574 sp<NumericValueMetricProducer> valueProducer =
1575 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(
1576 pullerManager, metric, /*pullAtomId=*/-1);
1577
1578 LogEvent event1(/*uid=*/0, /*pid=*/0);
1579 CreateThreeValueLogEvent(&event1, tagId, bucketStartTimeNs + 10, 1, 10, 20);
1580
1581 LogEvent event2(/*uid=*/0, /*pid=*/0);
1582 CreateThreeValueLogEvent(&event2, tagId, bucketStartTimeNs + 15, 1, 15, 22);
1583
1584 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event1);
1585 // has one slice
1586 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1587 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1588 NumericValueMetricProducer::Interval curInterval =
1589 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1590 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1591 EXPECT_EQ(true, curBase.has_value());
1592 EXPECT_EQ(10, curBase.value().long_value);
1593 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
1594 EXPECT_EQ(true, curBase.has_value());
1595 EXPECT_EQ(20, curBase.value().long_value);
1596 EXPECT_EQ(0, curInterval.sampleSize);
1597
1598 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event2);
1599
1600 // has one slice
1601 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1602 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1603 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1604 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1605 EXPECT_EQ(true, curBase.has_value());
1606 EXPECT_EQ(15, curBase.value().long_value);
1607 EXPECT_TRUE(curInterval.hasValue());
1608 EXPECT_EQ(5, curInterval.aggregate.long_value);
1609 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[1];
1610 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
1611 EXPECT_EQ(true, curBase.has_value());
1612 EXPECT_EQ(22, curBase.value().long_value);
1613 EXPECT_TRUE(curInterval.hasValue());
1614 EXPECT_EQ(2, curInterval.aggregate.long_value);
1615
1616 // no change in first value field
1617 LogEvent event3(/*uid=*/0, /*pid=*/0);
1618 CreateThreeValueLogEvent(&event3, tagId, bucket2StartTimeNs + 10, 1, 15, 25);
1619
1620 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event3);
1621 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1622 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1623 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1624 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1625 EXPECT_EQ(true, curBase.has_value());
1626 EXPECT_EQ(15, curBase.value().long_value);
1627 EXPECT_TRUE(curInterval.hasValue());
1628 EXPECT_EQ(0, curInterval.aggregate.long_value);
1629 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[1];
1630 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
1631 EXPECT_EQ(true, curBase.has_value());
1632 EXPECT_EQ(25, curBase.value().long_value);
1633 EXPECT_TRUE(curInterval.hasValue());
1634 EXPECT_EQ(3, curInterval.aggregate.long_value);
1635
1636 LogEvent event4(/*uid=*/0, /*pid=*/0);
1637 CreateThreeValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 1, 15, 29);
1638
1639 valueProducer->onMatchedLogEvent(1 /*log matcher index*/, event4);
1640 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1641 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1642 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1643 EXPECT_EQ(true, curBase.has_value());
1644 EXPECT_EQ(15, curBase.value().long_value);
1645 EXPECT_TRUE(curInterval.hasValue());
1646 EXPECT_EQ(0, curInterval.aggregate.long_value);
1647 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[1];
1648 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[1];
1649 EXPECT_EQ(true, curBase.has_value());
1650 EXPECT_EQ(29, curBase.value().long_value);
1651 EXPECT_TRUE(curInterval.hasValue());
1652 EXPECT_EQ(3, curInterval.aggregate.long_value);
1653
1654 valueProducer->flushIfNeededLocked(bucket3StartTimeNs);
1655
1656 ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
1657 ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second.size());
1658 ASSERT_EQ(2UL, valueProducer->mPastBuckets.begin()->second[0].aggregates.size());
1659 ASSERT_EQ(1UL, valueProducer->mPastBuckets.begin()->second[1].aggregates.size());
1660
1661 EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[0].mConditionTrueNs);
1662 EXPECT_EQ(5, valueProducer->mPastBuckets.begin()->second[0].aggregates[0].long_value);
1663 EXPECT_EQ(0, valueProducer->mPastBuckets.begin()->second[0].aggIndex[0]);
1664 EXPECT_EQ(2, valueProducer->mPastBuckets.begin()->second[0].aggregates[1].long_value);
1665 EXPECT_EQ(1, valueProducer->mPastBuckets.begin()->second[0].aggIndex[1]);
1666
1667 EXPECT_EQ(bucketSizeNs, valueProducer->mPastBuckets.begin()->second[1].mConditionTrueNs);
1668 EXPECT_EQ(3, valueProducer->mPastBuckets.begin()->second[1].aggregates[0].long_value);
1669 EXPECT_EQ(1, valueProducer->mPastBuckets.begin()->second[1].aggIndex[0]);
1670 }
1671
1672 /*
1673 * Tests zero default base.
1674 */
TEST(NumericValueMetricProducerTest,TestUseZeroDefaultBase)1675 TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBase) {
1676 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1677 metric.mutable_dimensions_in_what()->set_field(tagId);
1678 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
1679 metric.set_use_zero_default_base(true);
1680
1681 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1682 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
1683 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
1684 vector<std::shared_ptr<LogEvent>>* data) {
1685 data->clear();
1686 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
1687 return true;
1688 }));
1689
1690 sp<NumericValueMetricProducer> valueProducer =
1691 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
1692 metric);
1693
1694 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1695 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1696 auto iter = valueProducer->mCurrentSlicedBucket.begin();
1697 auto& interval1 = iter->second.intervals[0];
1698 auto iterBase = valueProducer->mDimInfos.begin();
1699 auto& base1 = iterBase->second.dimExtras[0];
1700 EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
1701 EXPECT_EQ(true, base1.has_value());
1702 EXPECT_EQ(3, base1.value().long_value);
1703 EXPECT_EQ(0, interval1.sampleSize);
1704 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
1705 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
1706 vector<shared_ptr<LogEvent>> allData;
1707
1708 allData.clear();
1709 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
1710 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
1711
1712 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
1713 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1714 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
1715 EXPECT_EQ(true, base1.has_value());
1716 EXPECT_EQ(11, base1.value().long_value);
1717
1718 auto itBase = valueProducer->mDimInfos.begin();
1719 for (; itBase != valueProducer->mDimInfos.end(); itBase++) {
1720 if (itBase != iterBase) {
1721 break;
1722 }
1723 }
1724 EXPECT_TRUE(itBase != iterBase);
1725 auto& base2 = itBase->second.dimExtras[0];
1726 EXPECT_EQ(true, base2.has_value());
1727 EXPECT_EQ(4, base2.value().long_value);
1728
1729 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
1730 auto iterator = valueProducer->mPastBuckets.begin();
1731 EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
1732 EXPECT_EQ(8, iterator->second[0].aggregates[0].long_value);
1733 iterator++;
1734 EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
1735 EXPECT_EQ(4, iterator->second[0].aggregates[0].long_value);
1736 }
1737
1738 /*
1739 * Tests using zero default base with failed pull.
1740 */
TEST(NumericValueMetricProducerTest,TestUseZeroDefaultBaseWithPullFailures)1741 TEST(NumericValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures) {
1742 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1743 metric.mutable_dimensions_in_what()->set_field(tagId);
1744 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
1745 metric.set_use_zero_default_base(true);
1746
1747 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1748 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
1749 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
1750 vector<std::shared_ptr<LogEvent>>* data) {
1751 data->clear();
1752 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
1753 return true;
1754 }));
1755
1756 sp<NumericValueMetricProducer> valueProducer =
1757 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
1758 metric);
1759
1760 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1761 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1762 const auto& it = valueProducer->mCurrentSlicedBucket.begin();
1763 NumericValueMetricProducer::Interval& interval1 = it->second.intervals[0];
1764 optional<Value>& base1 =
1765 valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat())->second.dimExtras[0];
1766 EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
1767 EXPECT_EQ(true, base1.has_value());
1768 EXPECT_EQ(3, base1.value().long_value);
1769 EXPECT_EQ(0, interval1.sampleSize);
1770 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
1771 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
1772 vector<shared_ptr<LogEvent>> allData;
1773
1774 allData.clear();
1775 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
1776 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
1777
1778 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
1779 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1780 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
1781 EXPECT_EQ(true, base1.has_value());
1782 EXPECT_EQ(11, base1.value().long_value);
1783
1784 auto itBase2 = valueProducer->mDimInfos.begin();
1785 for (; itBase2 != valueProducer->mDimInfos.end(); itBase2++) {
1786 if (itBase2->second.dimExtras[0] != base1) {
1787 break;
1788 }
1789 }
1790 optional<Value>& base2 = itBase2->second.dimExtras[0];
1791 EXPECT_TRUE(base2 != base1);
1792 EXPECT_EQ(2, itBase2->first.getValues()[0].mValue.int_value);
1793 EXPECT_EQ(true, base2.has_value());
1794 EXPECT_EQ(4, base2.value().long_value);
1795 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
1796
1797 // next pull somehow did not happen, skip to end of bucket 3
1798 // This pull is incomplete since it's missing dimension 1. Will cause mDimInfos to be trimmed
1799 allData.clear();
1800 allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
1801 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
1802
1803 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1804 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1805 EXPECT_EQ(2, valueProducer->mDimInfos.begin()->first.getValues()[0].mValue.int_value);
1806 optional<Value>& base3 = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1807 EXPECT_EQ(true, base3.has_value());
1808 EXPECT_EQ(5, base3.value().long_value);
1809 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
1810 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
1811
1812 allData.clear();
1813 allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 13));
1814 allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 5));
1815 valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
1816
1817 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1818 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
1819 optional<Value>& base4 = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1820 optional<Value>& base5 = std::next(valueProducer->mDimInfos.begin())->second.dimExtras[0];
1821
1822 EXPECT_EQ(true, base4.has_value());
1823 EXPECT_EQ(5, base4.value().long_value);
1824 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
1825 EXPECT_EQ(true, base5.has_value());
1826 EXPECT_EQ(13, base5.value().long_value);
1827
1828 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
1829 }
1830
1831 /*
1832 * Tests trim unused dimension key if no new data is seen in an entire bucket.
1833 */
TEST(NumericValueMetricProducerTest,TestTrimUnusedDimensionKey)1834 TEST(NumericValueMetricProducerTest, TestTrimUnusedDimensionKey) {
1835 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
1836 metric.mutable_dimensions_in_what()->set_field(tagId);
1837 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
1838
1839 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1840 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
1841 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
1842 vector<std::shared_ptr<LogEvent>>* data) {
1843 data->clear();
1844 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
1845 return true;
1846 }));
1847
1848 sp<NumericValueMetricProducer> valueProducer =
1849 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
1850 metric);
1851
1852 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1853 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1854 auto iter = valueProducer->mCurrentSlicedBucket.begin();
1855 auto& interval1 = iter->second.intervals[0];
1856 auto iterBase = valueProducer->mDimInfos.begin();
1857 auto& base1 = iterBase->second.dimExtras[0];
1858 EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
1859 EXPECT_EQ(true, base1.has_value());
1860 EXPECT_EQ(3, base1.value().long_value);
1861 EXPECT_EQ(0, interval1.sampleSize);
1862 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
1863
1864 vector<shared_ptr<LogEvent>> allData;
1865 allData.clear();
1866 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 2, 4));
1867 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 11));
1868 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
1869
1870 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1871 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
1872 EXPECT_EQ(true, base1.has_value());
1873 EXPECT_EQ(11, base1.value().long_value);
1874 EXPECT_FALSE(iterBase->second.seenNewData);
1875 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
1876 {bucketStartTimeNs}, {bucket2StartTimeNs});
1877
1878 auto itBase = valueProducer->mDimInfos.begin();
1879 for (; itBase != valueProducer->mDimInfos.end(); itBase++) {
1880 if (itBase != iterBase) {
1881 break;
1882 }
1883 }
1884 EXPECT_TRUE(itBase != iterBase);
1885 auto base2 = itBase->second.dimExtras[0];
1886 EXPECT_EQ(2, itBase->first.getValues()[0].mValue.int_value);
1887 EXPECT_EQ(true, base2.has_value());
1888 EXPECT_EQ(4, base2.value().long_value);
1889 EXPECT_FALSE(itBase->second.seenNewData);
1890
1891 // next pull somehow did not happen, skip to end of bucket 3
1892 allData.clear();
1893 allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 2, 5));
1894 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
1895 // Only one dimension left. One was trimmed.
1896 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1897 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1898 base2 = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1899 EXPECT_EQ(2, valueProducer->mDimInfos.begin()->first.getValues()[0].mValue.int_value);
1900 EXPECT_EQ(true, base2.has_value());
1901 EXPECT_EQ(5, base2.value().long_value);
1902 EXPECT_FALSE(valueProducer->mDimInfos.begin()->second.seenNewData);
1903 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {8}, {bucketSizeNs}, {0},
1904 {bucketStartTimeNs}, {bucket2StartTimeNs});
1905
1906 allData.clear();
1907 allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
1908 allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 1, 14));
1909 valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
1910
1911 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1912 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
1913
1914 allData.clear();
1915 allData.push_back(CreateTwoValueLogEvent(tagId, bucket6StartTimeNs + 1, 1, 19));
1916 allData.push_back(CreateTwoValueLogEvent(tagId, bucket6StartTimeNs + 1, 2, 20));
1917 valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
1918
1919 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1920 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
1921
1922 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
1923 // Dimension = 2
1924 auto iterator = valueProducer->mPastBuckets.begin();
1925 ASSERT_EQ(1, iterator->first.getDimensionKeyInWhat().getValues().size());
1926 EXPECT_EQ(2, iterator->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
1927 ASSERT_EQ(2, iterator->second.size());
1928 EXPECT_EQ(bucket4StartTimeNs, iterator->second[0].mBucketStartNs);
1929 EXPECT_EQ(bucket5StartTimeNs, iterator->second[0].mBucketEndNs);
1930 EXPECT_EQ(9, iterator->second[0].aggregates[0].long_value);
1931 EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
1932 EXPECT_EQ(bucket5StartTimeNs, iterator->second[1].mBucketStartNs);
1933 EXPECT_EQ(bucket6StartTimeNs, iterator->second[1].mBucketEndNs);
1934 EXPECT_EQ(6, iterator->second[1].aggregates[0].long_value);
1935 EXPECT_EQ(bucketSizeNs, iterator->second[1].mConditionTrueNs);
1936 iterator++;
1937 // Dimension = 1
1938 ASSERT_EQ(1, iterator->first.getDimensionKeyInWhat().getValues().size());
1939 EXPECT_EQ(1, iterator->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
1940 ASSERT_EQ(2, iterator->second.size());
1941 EXPECT_EQ(bucketStartTimeNs, iterator->second[0].mBucketStartNs);
1942 EXPECT_EQ(bucket2StartTimeNs, iterator->second[0].mBucketEndNs);
1943 EXPECT_EQ(8, iterator->second[0].aggregates[0].long_value);
1944 EXPECT_EQ(bucketSizeNs, iterator->second[0].mConditionTrueNs);
1945 EXPECT_EQ(bucket5StartTimeNs, iterator->second[1].mBucketStartNs);
1946 EXPECT_EQ(bucket6StartTimeNs, iterator->second[1].mBucketEndNs);
1947 EXPECT_EQ(5, iterator->second[1].aggregates[0].long_value);
1948 EXPECT_EQ(bucketSizeNs, iterator->second[1].mConditionTrueNs);
1949 }
1950
TEST(NumericValueMetricProducerTest,TestResetBaseOnPullFailAfterConditionChange_EndOfBucket)1951 TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange_EndOfBucket) {
1952 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
1953
1954 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1955 // Used by onConditionChanged.
1956 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
1957 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
1958 vector<std::shared_ptr<LogEvent>>* data) {
1959 data->clear();
1960 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
1961 return true;
1962 }));
1963
1964 sp<NumericValueMetricProducer> valueProducer =
1965 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
1966 pullerManager, metric, ConditionState::kFalse);
1967
1968 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
1969 // has one slice
1970 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
1971 NumericValueMetricProducer::Interval& curInterval =
1972 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
1973 optional<Value>& curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
1974 EXPECT_EQ(true, curBase.has_value());
1975 EXPECT_EQ(100, curBase.value().long_value);
1976 EXPECT_EQ(0, curInterval.sampleSize);
1977
1978 vector<shared_ptr<LogEvent>> allData;
1979 valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
1980 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
1981 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
1982 EXPECT_EQ(false, curBase.has_value());
1983 EXPECT_EQ(false, valueProducer->mHasGlobalBase);
1984 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
1985 ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
1986 }
1987
TEST(NumericValueMetricProducerTest,TestResetBaseOnPullFailAfterConditionChange)1988 TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailAfterConditionChange) {
1989 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
1990
1991 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
1992 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
1993 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
1994 vector<std::shared_ptr<LogEvent>>* data) {
1995 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8); // Condition change to true.
1996 data->clear();
1997 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
1998 return true;
1999 }))
2000 .WillOnce(Return(false));
2001
2002 sp<NumericValueMetricProducer> valueProducer =
2003 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2004 pullerManager, metric, ConditionState::kFalse);
2005
2006 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
2007
2008 // has one slice
2009 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2010 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2011 NumericValueMetricProducer::Interval& curInterval =
2012 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2013 optional<Value>& curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2014 EXPECT_EQ(true, curBase.has_value());
2015 EXPECT_EQ(100, curBase.value().long_value);
2016 EXPECT_EQ(0, curInterval.sampleSize);
2017 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
2018
2019 valueProducer->onConditionChanged(false, bucketStartTimeNs + 20);
2020
2021 // has one slice
2022 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2023 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2024 EXPECT_EQ(0, curInterval.sampleSize);
2025 EXPECT_EQ(false, curBase.has_value());
2026 EXPECT_EQ(false, valueProducer->mHasGlobalBase);
2027 }
2028
TEST(NumericValueMetricProducerTest,TestResetBaseOnPullFailBeforeConditionChange)2029 TEST(NumericValueMetricProducerTest, TestResetBaseOnPullFailBeforeConditionChange) {
2030 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2031
2032 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2033 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2034 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2035 vector<std::shared_ptr<LogEvent>>* data) {
2036 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
2037 data->clear();
2038 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
2039 return false;
2040 }))
2041 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2042 vector<std::shared_ptr<LogEvent>>* data) {
2043 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1); // Condition change to false.
2044 data->clear();
2045 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
2046 return true;
2047 }));
2048
2049 sp<NumericValueMetricProducer> valueProducer =
2050 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2051 pullerManager, metric, ConditionState::kFalse);
2052
2053 valueProducer->onConditionChanged(true, bucketStartTimeNs);
2054 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2055 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
2056
2057 valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
2058 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2059 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2060 NumericValueMetricProducer::Interval& curInterval =
2061 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2062 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2063 EXPECT_EQ(false, curBase.has_value());
2064 EXPECT_EQ(0, curInterval.sampleSize);
2065 EXPECT_EQ(false, valueProducer->mHasGlobalBase);
2066 }
2067
TEST(NumericValueMetricProducerTest,TestResetBaseOnPullDelayExceeded)2068 TEST(NumericValueMetricProducerTest, TestResetBaseOnPullDelayExceeded) {
2069 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2070 metric.set_condition(StringToId("SCREEN_ON"));
2071 metric.set_max_pull_delay_sec(0);
2072
2073 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2074 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
2075 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
2076 vector<std::shared_ptr<LogEvent>>* data) {
2077 data->clear();
2078 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
2079 return true;
2080 }));
2081
2082 sp<NumericValueMetricProducer> valueProducer =
2083 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2084 pullerManager, metric, ConditionState::kFalse);
2085
2086 // Max delay is set to 0 so pull will exceed max delay.
2087 valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
2088 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2089 }
2090
TEST(NumericValueMetricProducerTest,TestResetBaseOnPullTooLate)2091 TEST(NumericValueMetricProducerTest, TestResetBaseOnPullTooLate) {
2092 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2093
2094 sp<EventMatcherWizard> eventMatcherWizard =
2095 createEventMatcherWizard(tagId, logEventMatcherIndex);
2096 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
2097 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2098
2099 sp<NumericValueMetricProducer> valueProducer =
2100 NumericValueMetricProducerTestHelper::createValueProducer(
2101 pullerManager, metric, tagId, ConditionState::kFalse,
2102 /*slicedStateAtoms=*/{},
2103 /*stateGroupMap=*/{}, bucket2StartTimeNs, bucket2StartTimeNs,
2104 eventMatcherWizard);
2105
2106 // Event should be skipped since it is from previous bucket.
2107 // Pull should not be called.
2108 valueProducer->onConditionChanged(true, bucketStartTimeNs);
2109 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2110 }
2111
TEST(NumericValueMetricProducerTest,TestBaseSetOnConditionChange)2112 TEST(NumericValueMetricProducerTest, TestBaseSetOnConditionChange) {
2113 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2114
2115 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2116 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
2117 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
2118 vector<std::shared_ptr<LogEvent>>* data) {
2119 data->clear();
2120 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
2121 return true;
2122 }));
2123
2124 sp<NumericValueMetricProducer> valueProducer =
2125 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2126 pullerManager, metric, ConditionState::kFalse);
2127
2128 valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
2129 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2130 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2131 NumericValueMetricProducer::Interval& curInterval =
2132 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2133 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2134 EXPECT_EQ(true, curBase.has_value());
2135 EXPECT_EQ(100, curBase.value().long_value);
2136 EXPECT_EQ(0, curInterval.sampleSize);
2137 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2138 }
2139
2140 /*
2141 * Tests that a bucket is marked invalid when a condition change pull fails.
2142 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenOneConditionFailed)2143 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed) {
2144 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2145
2146 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2147 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2148 // First onConditionChanged
2149 .WillOnce(Return(false))
2150 // Second onConditionChanged
2151 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2152 vector<std::shared_ptr<LogEvent>>* data) {
2153 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
2154 data->clear();
2155 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
2156 return true;
2157 }));
2158
2159 sp<NumericValueMetricProducer> valueProducer =
2160 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2161 pullerManager, metric, ConditionState::kTrue);
2162
2163 // Bucket start.
2164 vector<shared_ptr<LogEvent>> allData;
2165 allData.clear();
2166 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
2167 valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
2168
2169 // This will fail and should invalidate the whole bucket since we do not have all the data
2170 // needed to compute the metric value when the screen was on.
2171 valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
2172 valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
2173
2174 // Bucket end.
2175 allData.clear();
2176 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
2177 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2178
2179 valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
2180
2181 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
2182 // Contains base from last pull which was successful.
2183 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2184 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2185 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2186 EXPECT_EQ(true, curBase.has_value());
2187 EXPECT_EQ(140, curBase.value().long_value);
2188 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2189
2190 // Check dump report.
2191 ProtoOutputStream output;
2192 std::set<string> strSet;
2193 valueProducer->onDumpReport(bucket2StartTimeNs + 10, false /* include partial bucket */, true,
2194 FAST /* dumpLatency */, &strSet, &output);
2195
2196 StatsLogReport report = outputStreamToProto(&output);
2197 EXPECT_TRUE(report.has_value_metrics());
2198 ASSERT_EQ(0, report.value_metrics().data_size());
2199 ASSERT_EQ(1, report.value_metrics().skipped_size());
2200
2201 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
2202 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
2203 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
2204 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
2205 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
2206
2207 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
2208 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
2209 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
2210 }
2211
2212 /*
2213 * Tests that a bucket is marked invalid when the guardrail is hit.
2214 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenGuardRailHit)2215 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenGuardRailHit) {
2216 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2217 metric.mutable_dimensions_in_what()->set_field(tagId);
2218 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
2219 metric.set_condition(StringToId("SCREEN_ON"));
2220
2221 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2222 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _))
2223 // First onConditionChanged
2224 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
2225 vector<std::shared_ptr<LogEvent>>* data) {
2226 for (int i = 0; i < 2000; i++) {
2227 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
2228 }
2229 return true;
2230 }));
2231
2232 sp<NumericValueMetricProducer> valueProducer =
2233 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2234 pullerManager, metric, ConditionState::kFalse);
2235
2236 valueProducer->onConditionChanged(true, bucketStartTimeNs + 2);
2237 EXPECT_EQ(true, valueProducer->mCurrentBucketIsSkipped);
2238 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2239 ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size());
2240
2241 // Bucket 2 start.
2242 vector<shared_ptr<LogEvent>> allData;
2243 allData.clear();
2244 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 1, 1, 10));
2245 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2246
2247 // First bucket added to mSkippedBuckets after flush.
2248 ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
2249
2250 // Check dump report.
2251 ProtoOutputStream output;
2252 std::set<string> strSet;
2253 valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
2254 true, FAST /* dumpLatency */, &strSet, &output);
2255
2256 StatsLogReport report = outputStreamToProto(&output);
2257 EXPECT_TRUE(report.has_value_metrics());
2258 ASSERT_EQ(0, report.value_metrics().data_size());
2259 ASSERT_EQ(1, report.value_metrics().skipped_size());
2260
2261 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
2262 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
2263 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
2264 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
2265 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
2266
2267 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
2268 EXPECT_EQ(BucketDropReason::DIMENSION_GUARDRAIL_REACHED, dropEvent.drop_reason());
2269 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
2270 }
2271
2272 /*
2273 * Tests that a bucket is marked invalid when the bucket's initial pull fails.
2274 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenInitialPullFailed)2275 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed) {
2276 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2277
2278 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2279 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2280 // First onConditionChanged
2281 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2282 vector<std::shared_ptr<LogEvent>>* data) {
2283 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
2284 data->clear();
2285 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
2286 return true;
2287 }))
2288 // Second onConditionChanged
2289 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2290 vector<std::shared_ptr<LogEvent>>* data) {
2291 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
2292 data->clear();
2293 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
2294 return true;
2295 }));
2296
2297 sp<NumericValueMetricProducer> valueProducer =
2298 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2299 pullerManager, metric, ConditionState::kTrue);
2300
2301 // Bucket start.
2302 vector<shared_ptr<LogEvent>> allData;
2303 allData.clear();
2304 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
2305 valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs);
2306
2307 valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
2308 valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
2309
2310 // Bucket end.
2311 allData.clear();
2312 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
2313 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2314
2315 valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
2316
2317 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
2318 // Contains base from last pull which was successful.
2319 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2320 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2321 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2322 EXPECT_EQ(true, curBase.has_value());
2323 EXPECT_EQ(140, curBase.value().long_value);
2324 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2325
2326 // Check dump report.
2327 ProtoOutputStream output;
2328 std::set<string> strSet;
2329 valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
2330 true, FAST /* dumpLatency */, &strSet, &output);
2331
2332 StatsLogReport report = outputStreamToProto(&output);
2333 EXPECT_TRUE(report.has_value_metrics());
2334 ASSERT_EQ(0, report.value_metrics().data_size());
2335 ASSERT_EQ(1, report.value_metrics().skipped_size());
2336
2337 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
2338 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
2339 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
2340 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
2341 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
2342
2343 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
2344 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
2345 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 2), dropEvent.drop_time_millis());
2346 }
2347
2348 /*
2349 * Tests that a bucket is marked invalid when the bucket's final pull fails
2350 * (i.e. failed pull on bucket boundary).
2351 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenLastPullFailed)2352 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenLastPullFailed) {
2353 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2354
2355 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2356 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2357 // First onConditionChanged
2358 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2359 vector<std::shared_ptr<LogEvent>>* data) {
2360 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
2361 data->clear();
2362 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
2363 return true;
2364 }))
2365 // Second onConditionChanged
2366 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2367 vector<std::shared_ptr<LogEvent>>* data) {
2368 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
2369 data->clear();
2370 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
2371 return true;
2372 }));
2373
2374 sp<NumericValueMetricProducer> valueProducer =
2375 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2376 pullerManager, metric, ConditionState::kTrue);
2377
2378 // Bucket start.
2379 vector<shared_ptr<LogEvent>> allData;
2380 allData.clear();
2381 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 110));
2382 valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs);
2383
2384 valueProducer->onConditionChanged(false, bucketStartTimeNs + 2);
2385 valueProducer->onConditionChanged(true, bucketStartTimeNs + 3);
2386
2387 // Bucket end.
2388 allData.clear();
2389 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 140));
2390 valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
2391
2392 valueProducer->flushIfNeededLocked(bucket2StartTimeNs + 1);
2393
2394 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
2395 // Last pull failed so base has been reset.
2396 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2397 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2398 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2399 EXPECT_EQ(false, curBase.has_value());
2400 EXPECT_EQ(false, valueProducer->mHasGlobalBase);
2401
2402 // Check dump report.
2403 ProtoOutputStream output;
2404 std::set<string> strSet;
2405 valueProducer->onDumpReport(bucket2StartTimeNs + 10000, false /* include recent buckets */,
2406 true, FAST /* dumpLatency */, &strSet, &output);
2407
2408 StatsLogReport report = outputStreamToProto(&output);
2409 EXPECT_TRUE(report.has_value_metrics());
2410 ASSERT_EQ(0, report.value_metrics().data_size());
2411 ASSERT_EQ(1, report.value_metrics().skipped_size());
2412
2413 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
2414 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
2415 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
2416 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
2417 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
2418
2419 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
2420 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
2421 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
2422 }
2423
TEST(NumericValueMetricProducerTest,TestEmptyDataResetsBase_onDataPulled)2424 TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
2425 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2426 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2427 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
2428 // Start bucket.
2429 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
2430 vector<std::shared_ptr<LogEvent>>* data) {
2431 data->clear();
2432 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
2433 return true;
2434 }));
2435
2436 sp<NumericValueMetricProducer> valueProducer =
2437 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
2438 metric);
2439
2440 // Bucket 2 start.
2441 vector<shared_ptr<LogEvent>> allData;
2442 allData.clear();
2443 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 110));
2444 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2445 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2446 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2447 EXPECT_EQ(valueProducer->mDimInfos.begin()->second.seenNewData, false);
2448 ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
2449 ASSERT_EQ(0UL, valueProducer->mSkippedBuckets.size());
2450
2451 // Bucket 3 empty.
2452 allData.clear();
2453 allData.push_back(CreateNoValuesLogEvent(tagId, bucket3StartTimeNs + 1));
2454 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
2455 // Data has been trimmed.
2456 ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
2457 ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
2458 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2459 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
2460
2461 // Bucket 4 start.
2462 allData.clear();
2463 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 150));
2464 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
2465 ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
2466 ASSERT_EQ(2UL, valueProducer->mSkippedBuckets.size());
2467 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2468 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2469
2470 // Bucket 5 start.
2471 allData.clear();
2472 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket5StartTimeNs + 1, 170));
2473 valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
2474 assertPastBucketValuesSingleKey(
2475 valueProducer->mPastBuckets, {107, 20}, {bucketSizeNs, bucketSizeNs}, {0, 0},
2476 {bucketStartTimeNs, bucket4StartTimeNs}, {bucket2StartTimeNs, bucket5StartTimeNs});
2477 ASSERT_EQ(2UL, valueProducer->mSkippedBuckets.size());
2478 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2479 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2480 }
2481
TEST(NumericValueMetricProducerTest,TestEmptyDataResetsBase_onConditionChanged)2482 TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onConditionChanged) {
2483 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2484
2485 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2486 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2487 // First onConditionChanged
2488 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2489 vector<std::shared_ptr<LogEvent>>* data) {
2490 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
2491 data->clear();
2492 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
2493 return true;
2494 }))
2495 // Empty pull when change to false
2496 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2497 vector<std::shared_ptr<LogEvent>>* data) {
2498 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
2499 data->clear();
2500 return true;
2501 }))
2502 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2503 vector<std::shared_ptr<LogEvent>>* data) {
2504 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30);
2505 data->clear();
2506 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
2507 return true;
2508 }));
2509
2510 sp<NumericValueMetricProducer> valueProducer =
2511 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2512 pullerManager, metric, ConditionState::kFalse);
2513
2514 valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
2515 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2516 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2517 NumericValueMetricProducer::Interval& curInterval =
2518 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2519 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2520 EXPECT_EQ(true, curBase.has_value());
2521 EXPECT_EQ(0, curInterval.sampleSize);
2522 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2523
2524 // Empty pull.
2525 valueProducer->onConditionChanged(false, bucketStartTimeNs + 20);
2526 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2527 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
2528 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2529 EXPECT_EQ(0, curInterval.sampleSize);
2530 EXPECT_EQ(false, valueProducer->mHasGlobalBase);
2531
2532 valueProducer->onConditionChanged(true, bucketStartTimeNs + 30);
2533 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2534 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2535 curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2536 EXPECT_EQ(0, curInterval.sampleSize);
2537 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2538 EXPECT_EQ(true, curBase.has_value());
2539 EXPECT_EQ(10, curBase.value().long_value);
2540 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2541
2542 vector<shared_ptr<LogEvent>> allData;
2543 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
2544 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2545 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2546 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2547 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2548 EXPECT_EQ(true, curBase.has_value());
2549 EXPECT_EQ(120, curBase.value().long_value);
2550 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2551 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {110}, {bucketSizeNs - 20}, {0},
2552 {bucketStartTimeNs}, {bucket2StartTimeNs});
2553 }
2554
TEST(NumericValueMetricProducerTest,TestEmptyDataResetsBase_onBucketBoundary)2555 TEST(NumericValueMetricProducerTest, TestEmptyDataResetsBase_onBucketBoundary) {
2556 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2557
2558 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2559 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2560 // First onConditionChanged
2561 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2562 vector<std::shared_ptr<LogEvent>>* data) {
2563 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
2564 data->clear();
2565 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
2566 return true;
2567 }))
2568 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2569 vector<std::shared_ptr<LogEvent>>* data) {
2570 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
2571 data->clear();
2572 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
2573 return true;
2574 }))
2575 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2576 vector<std::shared_ptr<LogEvent>>* data) {
2577 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
2578 data->clear();
2579 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
2580 return true;
2581 }));
2582
2583 sp<NumericValueMetricProducer> valueProducer =
2584 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2585 pullerManager, metric, ConditionState::kFalse);
2586
2587 valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
2588 valueProducer->onConditionChanged(false, bucketStartTimeNs + 11);
2589 valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
2590 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2591 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2592 NumericValueMetricProducer::Interval& curInterval =
2593 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2594 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2595 EXPECT_EQ(true, curBase.has_value());
2596 EXPECT_TRUE(curInterval.hasValue());
2597 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2598
2599 // End of bucket
2600 vector<shared_ptr<LogEvent>> allData;
2601 allData.clear();
2602 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2603 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2604 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
2605
2606 ASSERT_EQ(1UL, valueProducer->mPastBuckets.size());
2607 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {1}, {bucketSizeNs - 12 + 1}, {0},
2608 {bucketStartTimeNs}, {bucket2StartTimeNs});
2609 }
2610
TEST(NumericValueMetricProducerTest,TestPartialResetOnBucketBoundaries)2611 TEST(NumericValueMetricProducerTest, TestPartialResetOnBucketBoundaries) {
2612 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2613 metric.mutable_dimensions_in_what()->set_field(tagId);
2614 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
2615 metric.set_condition(StringToId("SCREEN_ON"));
2616
2617 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2618 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
2619 // First onConditionChanged
2620 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
2621 vector<std::shared_ptr<LogEvent>>* data) {
2622 data->clear();
2623 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
2624 return true;
2625 }));
2626
2627 sp<NumericValueMetricProducer> valueProducer =
2628 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2629 pullerManager, metric, ConditionState::kFalse);
2630
2631 valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
2632 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2633 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2634
2635 // End of bucket
2636 vector<shared_ptr<LogEvent>> allData;
2637 allData.clear();
2638 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 2));
2639 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2640
2641 // Key 1 should be removed from mDimInfos since in not present in the most pull.
2642 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2643 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2644 auto baseInfoIter = valueProducer->mDimInfos.begin();
2645 EXPECT_EQ(true, baseInfoIter->second.dimExtras[0].has_value());
2646 EXPECT_EQ(2, baseInfoIter->second.dimExtras[0].value().long_value);
2647
2648 EXPECT_EQ(true, valueProducer->mHasGlobalBase);
2649 }
2650
TEST_P(NumericValueMetricProducerTest_PartialBucket,TestFullBucketResetWhenLastBucketInvalid)2651 TEST_P(NumericValueMetricProducerTest_PartialBucket, TestFullBucketResetWhenLastBucketInvalid) {
2652 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2653
2654 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2655 int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
2656 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2657 // Initialization.
2658 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2659 vector<std::shared_ptr<LogEvent>>* data) {
2660 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
2661 data->clear();
2662 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
2663 return true;
2664 }))
2665 // notifyAppUpgrade.
2666 .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
2667 const int64_t eventTimeNs,
2668 vector<std::shared_ptr<LogEvent>>* data) {
2669 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
2670 data->clear();
2671 data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
2672 return true;
2673 }));
2674 sp<NumericValueMetricProducer> valueProducer =
2675 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
2676 metric);
2677
2678 sp<AlarmMonitor> alarmMonitor;
2679 Alert alert;
2680 alert.set_id(101);
2681 alert.set_metric_id(metricId);
2682 alert.set_trigger_if_sum_gt(100);
2683 alert.set_num_buckets(1);
2684 alert.set_refractory_period_secs(3);
2685 sp<AnomalyTracker> anomalyTracker =
2686 valueProducer->addAnomalyTracker(alert, alarmMonitor, UPDATE_NEW, bucketStartTimeNs);
2687 ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
2688
2689 switch (GetParam()) {
2690 case APP_UPGRADE:
2691 valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
2692 break;
2693 case BOOT_COMPLETE:
2694 valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
2695 break;
2696 }
2697 EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mCurrentBucketStartTimeNs);
2698 EXPECT_EQ(0, valueProducer->getCurrentBucketNum());
2699 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
2700 {partialBucketSplitTimeNs - bucketStartTimeNs}, {0},
2701 {bucketStartTimeNs}, {partialBucketSplitTimeNs});
2702 ASSERT_EQ(1UL, valueProducer->mCurrentFullBucket.size());
2703
2704 vector<shared_ptr<LogEvent>> allData;
2705 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 4));
2706 // Pull fails and arrives late.
2707 valueProducer->onDataPulled(allData, /** fails */ false, bucket3StartTimeNs + 1);
2708 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9},
2709 {partialBucketSplitTimeNs - bucketStartTimeNs}, {0},
2710 {bucketStartTimeNs}, {partialBucketSplitTimeNs});
2711 ASSERT_EQ(1, valueProducer->mSkippedBuckets.size());
2712 ASSERT_EQ(2, valueProducer->mSkippedBuckets[0].dropEvents.size());
2713 EXPECT_EQ(PULL_FAILED, valueProducer->mSkippedBuckets[0].dropEvents[0].reason);
2714 EXPECT_EQ(MULTIPLE_BUCKETS_SKIPPED, valueProducer->mSkippedBuckets[0].dropEvents[1].reason);
2715 EXPECT_EQ(partialBucketSplitTimeNs, valueProducer->mSkippedBuckets[0].bucketStartTimeNs);
2716 EXPECT_EQ(bucket3StartTimeNs, valueProducer->mSkippedBuckets[0].bucketEndTimeNs);
2717 ASSERT_EQ(0UL, valueProducer->mCurrentFullBucket.size());
2718 }
2719
TEST(NumericValueMetricProducerTest,TestBucketBoundariesOnConditionChange)2720 TEST(NumericValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
2721 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2722 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2723 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2724 // Second onConditionChanged.
2725 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2726 vector<std::shared_ptr<LogEvent>>* data) {
2727 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
2728 data->clear();
2729 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
2730 return true;
2731 }))
2732 // Third onConditionChanged.
2733 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2734 vector<std::shared_ptr<LogEvent>>* data) {
2735 EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
2736 data->clear();
2737 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
2738 return true;
2739 }));
2740
2741 sp<NumericValueMetricProducer> valueProducer =
2742 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2743 pullerManager, metric, ConditionState::kUnknown);
2744
2745 valueProducer->onConditionChanged(false, bucketStartTimeNs);
2746 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2747
2748 // End of first bucket
2749 vector<shared_ptr<LogEvent>> allData;
2750 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 4));
2751 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
2752 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2753
2754 valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
2755 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2756 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2757 auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2758 auto curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2759 EXPECT_EQ(true, curBase.has_value());
2760 EXPECT_EQ(5, curBase.value().long_value);
2761 EXPECT_EQ(0, curInterval.sampleSize);
2762
2763 valueProducer->onConditionChanged(false, bucket3StartTimeNs + 10);
2764 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
2765 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
2766
2767 // Bucket should have been completed.
2768 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {bucketSizeNs - 10}, {10},
2769 {bucket2StartTimeNs}, {bucket3StartTimeNs});
2770 }
2771
TEST(NumericValueMetricProducerTest,TestLateOnDataPulledWithoutDiff)2772 TEST(NumericValueMetricProducerTest, TestLateOnDataPulledWithoutDiff) {
2773 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2774 metric.set_use_diff(false);
2775
2776 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2777 sp<NumericValueMetricProducer> valueProducer =
2778 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
2779 metric);
2780
2781 vector<shared_ptr<LogEvent>> allData;
2782 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
2783 valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
2784
2785 allData.clear();
2786 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
2787 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2788
2789 // Bucket should have been completed.
2790 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs}, {0},
2791 {bucketStartTimeNs}, {bucket2StartTimeNs});
2792 }
2793
TEST(NumericValueMetricProducerTest,TestLateOnDataPulledWithDiff)2794 TEST(NumericValueMetricProducerTest, TestLateOnDataPulledWithDiff) {
2795 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2796
2797 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2798 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
2799 // Initialization.
2800 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
2801 vector<std::shared_ptr<LogEvent>>* data) {
2802 data->clear();
2803 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
2804 return true;
2805 }));
2806
2807 sp<NumericValueMetricProducer> valueProducer =
2808 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
2809 metric);
2810
2811 vector<shared_ptr<LogEvent>> allData;
2812 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
2813 valueProducer->onDataPulled(allData, /** succeed */ true, bucketStartTimeNs + 30);
2814
2815 allData.clear();
2816 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
2817 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2818
2819 // Bucket should have been completed.
2820 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {19}, {bucketSizeNs}, {0},
2821 {bucketStartTimeNs}, {bucket2StartTimeNs});
2822 }
2823
TEST_P(NumericValueMetricProducerTest_PartialBucket,TestBucketBoundariesOnPartialBucket)2824 TEST_P(NumericValueMetricProducerTest_PartialBucket, TestBucketBoundariesOnPartialBucket) {
2825 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2826
2827 int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
2828 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2829 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2830 // Initialization.
2831 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2832 vector<std::shared_ptr<LogEvent>>* data) {
2833 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
2834 data->clear();
2835 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
2836 return true;
2837 }))
2838 // notifyAppUpgrade.
2839 .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
2840 const int64_t eventTimeNs,
2841 vector<std::shared_ptr<LogEvent>>* data) {
2842 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
2843 data->clear();
2844 data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
2845 return true;
2846 }));
2847
2848 sp<NumericValueMetricProducer> valueProducer =
2849 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
2850 metric);
2851
2852 switch (GetParam()) {
2853 case APP_UPGRADE:
2854 valueProducer->notifyAppUpgrade(partialBucketSplitTimeNs);
2855 break;
2856 case BOOT_COMPLETE:
2857 valueProducer->onStatsdInitCompleted(partialBucketSplitTimeNs);
2858 break;
2859 }
2860
2861 // Bucket should have been completed.
2862 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {9}, {bucketSizeNs}, {2},
2863 {bucketStartTimeNs}, {bucket2StartTimeNs});
2864 }
2865
TEST(NumericValueMetricProducerTest,TestDataIsNotUpdatedWhenNoConditionChanged)2866 TEST(NumericValueMetricProducerTest, TestDataIsNotUpdatedWhenNoConditionChanged) {
2867 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2868
2869 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2870 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2871 // First on condition changed.
2872 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2873 vector<std::shared_ptr<LogEvent>>* data) {
2874 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
2875 data->clear();
2876 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
2877 return true;
2878 }))
2879 // Second on condition changed.
2880 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2881 vector<std::shared_ptr<LogEvent>>* data) {
2882 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
2883 data->clear();
2884 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
2885 return true;
2886 }));
2887
2888 sp<NumericValueMetricProducer> valueProducer =
2889 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2890 pullerManager, metric, ConditionState::kFalse);
2891
2892 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
2893 valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
2894 valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
2895
2896 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
2897 auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
2898 auto curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
2899 EXPECT_TRUE(curInterval.hasValue());
2900 EXPECT_EQ(2, curInterval.aggregate.long_value);
2901
2902 vector<shared_ptr<LogEvent>> allData;
2903 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 10));
2904 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 1);
2905
2906 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {2}, {2}, {0}, {bucketStartTimeNs},
2907 {bucket2StartTimeNs});
2908 }
2909
2910 // TODO: b/145705635 fix or delete this test
TEST(NumericValueMetricProducerTest,TestBucketInvalidIfGlobalBaseIsNotSet)2911 TEST(NumericValueMetricProducerTest, TestBucketInvalidIfGlobalBaseIsNotSet) {
2912 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
2913
2914 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2915 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
2916 // First condition change.
2917 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2918 vector<std::shared_ptr<LogEvent>>* data) {
2919 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
2920 data->clear();
2921 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
2922 return true;
2923 }))
2924 // 2nd condition change.
2925 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2926 vector<std::shared_ptr<LogEvent>>* data) {
2927 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
2928 data->clear();
2929 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
2930 return true;
2931 }))
2932 // 3rd condition change.
2933 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
2934 vector<std::shared_ptr<LogEvent>>* data) {
2935 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
2936 data->clear();
2937 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
2938 return true;
2939 }));
2940
2941 sp<NumericValueMetricProducer> valueProducer =
2942 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
2943 pullerManager, metric, ConditionState::kFalse);
2944 valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
2945
2946 vector<shared_ptr<LogEvent>> allData;
2947 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 3, 10));
2948 valueProducer->onDataPulled(allData, /** succeed */ false, bucketStartTimeNs + 3);
2949
2950 allData.clear();
2951 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 20));
2952 valueProducer->onDataPulled(allData, /** succeed */ false, bucket2StartTimeNs);
2953
2954 valueProducer->onConditionChanged(false, bucket2StartTimeNs + 8);
2955 valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
2956
2957 allData.clear();
2958 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30));
2959 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2960
2961 // There was not global base available so all buckets are invalid.
2962 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}, {});
2963 }
2964
TEST(NumericValueMetricProducerTest,TestFastDumpWithoutCurrentBucket)2965 TEST(NumericValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
2966 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
2967
2968 sp<EventMatcherWizard> eventMatcherWizard =
2969 createEventMatcherWizard(tagId, logEventMatcherIndex);
2970 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
2971 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
2972 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
2973 // Initial pull.
2974 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
2975 vector<std::shared_ptr<LogEvent>>* data) {
2976 data->clear();
2977 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
2978 return true;
2979 }));
2980
2981 sp<NumericValueMetricProducer> valueProducer =
2982 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
2983 metric);
2984
2985 vector<shared_ptr<LogEvent>> allData;
2986 allData.clear();
2987 allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, tagId, 2, 2));
2988 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
2989
2990 ProtoOutputStream output;
2991 std::set<string> strSet;
2992 valueProducer->onDumpReport(bucket4StartTimeNs, false /* include recent buckets */, true, FAST,
2993 &strSet, &output);
2994
2995 StatsLogReport report = outputStreamToProto(&output);
2996 // Previous bucket is part of the report, and the current bucket is not skipped.
2997 ASSERT_EQ(1, report.value_metrics().data_size());
2998 EXPECT_EQ(0, report.value_metrics().data(0).bucket_info(0).bucket_num());
2999 ASSERT_EQ(0, report.value_metrics().skipped_size());
3000 }
3001
TEST(NumericValueMetricProducerTest,TestPullNeededNoTimeConstraints)3002 TEST(NumericValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
3003 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
3004
3005 sp<EventMatcherWizard> eventMatcherWizard =
3006 createEventMatcherWizard(tagId, logEventMatcherIndex);
3007 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
3008 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3009 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3010 // Initial pull.
3011 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3012 vector<std::shared_ptr<LogEvent>>* data) {
3013 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
3014 data->clear();
3015 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
3016 return true;
3017 }))
3018 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3019 vector<std::shared_ptr<LogEvent>>* data) {
3020 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
3021 data->clear();
3022 data->push_back(
3023 CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10, tagId, 3, 3));
3024 return true;
3025 }));
3026
3027 sp<NumericValueMetricProducer> valueProducer =
3028 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
3029 metric);
3030
3031 ProtoOutputStream output;
3032 std::set<string> strSet;
3033 valueProducer->onDumpReport(bucketStartTimeNs + 10, true /* include recent buckets */, true,
3034 NO_TIME_CONSTRAINTS, &strSet, &output);
3035
3036 StatsLogReport report = outputStreamToProto(&output);
3037 ASSERT_EQ(1, report.value_metrics().data_size());
3038 ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
3039 EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
3040 }
3041
TEST(NumericValueMetricProducerTest,TestPulledData_noDiff_withoutCondition)3042 TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withoutCondition) {
3043 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
3044 metric.set_use_diff(false);
3045
3046 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3047 sp<NumericValueMetricProducer> valueProducer =
3048 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
3049 metric);
3050
3051 vector<shared_ptr<LogEvent>> allData;
3052 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 10));
3053 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 30);
3054
3055 // Bucket should have been completed.
3056 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {10}, {bucketSizeNs}, {30},
3057 {bucketStartTimeNs}, {bucket2StartTimeNs});
3058 ASSERT_EQ(0, valueProducer->mCurrentSlicedBucket.size());
3059 // TODO: mDimInfos is not needed for non-diffed data, but an entry is still created.
3060 ASSERT_EQ(1, valueProducer->mDimInfos.size());
3061 }
3062
TEST(NumericValueMetricProducerTest,TestPulledData_noDiff_withMultipleConditionChanges)3063 TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withMultipleConditionChanges) {
3064 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3065 metric.set_use_diff(false);
3066
3067 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3068 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3069 // condition becomes true
3070 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3071 vector<std::shared_ptr<LogEvent>>* data) {
3072 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
3073 data->clear();
3074 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
3075 return true;
3076 }))
3077 // condition becomes false
3078 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3079 vector<std::shared_ptr<LogEvent>>* data) {
3080 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
3081 data->clear();
3082 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
3083 return true;
3084 }));
3085 sp<NumericValueMetricProducer> valueProducer =
3086 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3087 pullerManager, metric, ConditionState::kFalse);
3088
3089 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
3090 valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
3091 // has one slice
3092 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
3093 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
3094 NumericValueMetricProducer::Interval curInterval =
3095 valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
3096 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
3097 EXPECT_EQ(false, curBase.has_value());
3098 EXPECT_TRUE(curInterval.hasValue());
3099 EXPECT_EQ(20, curInterval.aggregate.long_value);
3100
3101 // Now the alarm is delivered. Condition is off though.
3102 vector<shared_ptr<LogEvent>> allData;
3103 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 110));
3104 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
3105
3106 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8}, {0},
3107 {bucketStartTimeNs}, {bucket2StartTimeNs});
3108 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
3109 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
3110 curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
3111 EXPECT_EQ(false, curBase.has_value());
3112 }
3113
TEST(NumericValueMetricProducerTest,TestPulledData_noDiff_bucketBoundaryTrue)3114 TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryTrue) {
3115 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3116 metric.set_use_diff(false);
3117
3118 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3119 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
3120 // condition becomes true
3121 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
3122 vector<std::shared_ptr<LogEvent>>* data) {
3123 data->clear();
3124 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
3125 return true;
3126 }));
3127 sp<NumericValueMetricProducer> valueProducer =
3128 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3129 pullerManager, metric, ConditionState::kFalse);
3130
3131 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
3132
3133 // Now the alarm is delivered. Condition is on.
3134 vector<shared_ptr<LogEvent>> allData;
3135 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
3136 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
3137
3138 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8}, {0},
3139 {bucketStartTimeNs}, {bucket2StartTimeNs});
3140 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
3141 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
3142 optional<Value> curBase = valueProducer->mDimInfos.begin()->second.dimExtras[0];
3143 EXPECT_EQ(false, curBase.has_value());
3144 }
3145
TEST(NumericValueMetricProducerTest,TestPulledData_noDiff_bucketBoundaryFalse)3146 TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_bucketBoundaryFalse) {
3147 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3148 metric.set_use_diff(false);
3149
3150 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3151 sp<NumericValueMetricProducer> valueProducer =
3152 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3153 pullerManager, metric, ConditionState::kFalse);
3154
3155 // Now the alarm is delivered. Condition is off though.
3156 vector<shared_ptr<LogEvent>> allData;
3157 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
3158 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
3159
3160 // Condition was always false.
3161 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}, {});
3162 }
3163
TEST(NumericValueMetricProducerTest,TestPulledData_noDiff_withFailure)3164 TEST(NumericValueMetricProducerTest, TestPulledData_noDiff_withFailure) {
3165 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3166 metric.set_use_diff(false);
3167
3168 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3169 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3170 // condition becomes true
3171 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3172 vector<std::shared_ptr<LogEvent>>* data) {
3173 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
3174 data->clear();
3175 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
3176 return true;
3177 }))
3178 .WillOnce(Return(false));
3179 sp<NumericValueMetricProducer> valueProducer =
3180 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3181 pullerManager, metric, ConditionState::kFalse);
3182
3183 valueProducer->onConditionChanged(true, bucketStartTimeNs + 8);
3184 valueProducer->onConditionChanged(false, bucketStartTimeNs + 50);
3185 // First event is skipped because the metric is not diffed, so no entry is created in the map
3186 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
3187 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
3188
3189 // Now the alarm is delivered. Condition is off though.
3190 vector<shared_ptr<LogEvent>> allData;
3191 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 30, 30));
3192 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
3193 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
3194 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
3195
3196 // No buckets, we had a failure.
3197 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {}, {}, {}, {}, {});
3198 }
3199
3200 /*
3201 * Test that DUMP_REPORT_REQUESTED dump reason is logged.
3202 *
3203 * For the bucket to be marked invalid during a dump report requested,
3204 * three things must be true:
3205 * - we want to include the current partial bucket
3206 * - we need a pull (metric is pulled and condition is true)
3207 * - the dump latency must be FAST
3208 */
3209
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenDumpReportRequested)3210 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenDumpReportRequested) {
3211 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3212
3213 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3214 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _))
3215 // Condition change to true.
3216 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
3217 vector<std::shared_ptr<LogEvent>>* data) {
3218 data->clear();
3219 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
3220 return true;
3221 }));
3222
3223 sp<NumericValueMetricProducer> valueProducer =
3224 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3225 pullerManager, metric, ConditionState::kFalse);
3226
3227 // Condition change event.
3228 valueProducer->onConditionChanged(true, bucketStartTimeNs + 20);
3229
3230 // Check dump report.
3231 ProtoOutputStream output;
3232 std::set<string> strSet;
3233 valueProducer->onDumpReport(bucketStartTimeNs + 40, true /* include recent buckets */, true,
3234 FAST /* dumpLatency */, &strSet, &output);
3235 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
3236 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
3237
3238 StatsLogReport report = outputStreamToProto(&output);
3239 EXPECT_TRUE(report.has_value_metrics());
3240 ASSERT_EQ(0, report.value_metrics().data_size());
3241 ASSERT_EQ(1, report.value_metrics().skipped_size());
3242
3243 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3244 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3245 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40),
3246 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3247 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3248
3249 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3250 EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
3251 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 40), dropEvent.drop_time_millis());
3252 }
3253
3254 /*
3255 * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late condition
3256 * change event (i.e. the condition change occurs in the wrong bucket).
3257 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenConditionEventWrongBucket)3258 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionEventWrongBucket) {
3259 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3260
3261 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3262 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _))
3263 // Condition change to true.
3264 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
3265 vector<std::shared_ptr<LogEvent>>* data) {
3266 data->clear();
3267 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
3268 return true;
3269 }));
3270
3271 sp<NumericValueMetricProducer> valueProducer =
3272 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3273 pullerManager, metric, ConditionState::kFalse);
3274
3275 // Condition change event.
3276 valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
3277
3278 // Bucket boundary pull.
3279 vector<shared_ptr<LogEvent>> allData;
3280 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
3281 valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
3282
3283 // Late condition change event.
3284 valueProducer->onConditionChanged(false, bucket2StartTimeNs - 100);
3285
3286 // Check dump report.
3287 ProtoOutputStream output;
3288 std::set<string> strSet;
3289 valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
3290 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3291
3292 StatsLogReport report = outputStreamToProto(&output);
3293 EXPECT_TRUE(report.has_value_metrics());
3294 ASSERT_EQ(1, report.value_metrics().data_size());
3295 ASSERT_EQ(1, report.value_metrics().skipped_size());
3296
3297 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
3298 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3299 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
3300 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3301 ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
3302
3303 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3304 EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
3305 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
3306
3307 dropEvent = report.value_metrics().skipped(0).drop_event(1);
3308 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
3309 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100), dropEvent.drop_time_millis());
3310 }
3311
3312 /*
3313 * Test that EVENT_IN_WRONG_BUCKET dump reason is logged for a late accumulate
3314 * event (i.e. the accumulate events call occurs in the wrong bucket).
3315 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenAccumulateEventWrongBucket)3316 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenAccumulateEventWrongBucket) {
3317 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3318
3319 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3320 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3321 // Condition change to true.
3322 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3323 vector<std::shared_ptr<LogEvent>>* data) {
3324 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
3325 data->clear();
3326 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
3327 return true;
3328 }))
3329 // Dump report requested.
3330 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3331 vector<std::shared_ptr<LogEvent>>* data) {
3332 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100);
3333 data->clear();
3334 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
3335 return true;
3336 }));
3337
3338 sp<NumericValueMetricProducer> valueProducer =
3339 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3340 pullerManager, metric, ConditionState::kFalse);
3341
3342 // Condition change event.
3343 valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
3344
3345 // Bucket boundary pull.
3346 vector<shared_ptr<LogEvent>> allData;
3347 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 15));
3348 valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
3349
3350 allData.clear();
3351 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs - 100, 20));
3352
3353 // Late accumulateEvents event.
3354 valueProducer->accumulateEvents(allData, bucket2StartTimeNs - 100, bucket2StartTimeNs - 100);
3355
3356 // Check dump report.
3357 ProtoOutputStream output;
3358 std::set<string> strSet;
3359 valueProducer->onDumpReport(bucket2StartTimeNs + 100, true /* include recent buckets */, true,
3360 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3361
3362 StatsLogReport report = outputStreamToProto(&output);
3363 EXPECT_TRUE(report.has_value_metrics());
3364 ASSERT_EQ(1, report.value_metrics().data_size());
3365 ASSERT_EQ(1, report.value_metrics().skipped_size());
3366
3367 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
3368 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3369 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs + 100),
3370 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3371 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3372
3373 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3374 EXPECT_EQ(BucketDropReason::EVENT_IN_WRONG_BUCKET, dropEvent.drop_reason());
3375 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs - 100), dropEvent.drop_time_millis());
3376 }
3377
3378 /*
3379 * Test that CONDITION_UNKNOWN dump reason is logged due to an unknown condition
3380 * when a metric is initialized.
3381 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenConditionUnknown)3382 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenConditionUnknown) {
3383 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3384
3385 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3386 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3387 // Condition change to true.
3388 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3389 vector<std::shared_ptr<LogEvent>>* data) {
3390 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
3391 data->clear();
3392 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
3393 return true;
3394 }))
3395 // Dump report requested.
3396 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3397 vector<std::shared_ptr<LogEvent>>* data) {
3398 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000);
3399 data->clear();
3400 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
3401 return true;
3402 }));
3403
3404 sp<NumericValueMetricProducer> valueProducer =
3405 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3406 pullerManager, metric, ConditionState::kUnknown);
3407
3408 // Condition change event.
3409 valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
3410
3411 // Check dump report.
3412 ProtoOutputStream output;
3413 std::set<string> strSet;
3414 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
3415 valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
3416 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3417
3418 StatsLogReport report = outputStreamToProto(&output);
3419 EXPECT_TRUE(report.has_value_metrics());
3420 ASSERT_EQ(0, report.value_metrics().data_size());
3421 ASSERT_EQ(1, report.value_metrics().skipped_size());
3422
3423 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3424 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3425 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
3426 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3427 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3428
3429 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3430 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
3431 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
3432 }
3433
3434 /*
3435 * Test that PULL_FAILED dump reason is logged due to a pull failure in
3436 * #pullAndMatchEventsLocked.
3437 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenPullFailed)3438 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenPullFailed) {
3439 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3440
3441 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3442 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3443 // Condition change to true.
3444 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3445 vector<std::shared_ptr<LogEvent>>* data) {
3446 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
3447 data->clear();
3448 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
3449 return true;
3450 }))
3451 // Dump report requested, pull fails.
3452 .WillOnce(Return(false));
3453
3454 sp<NumericValueMetricProducer> valueProducer =
3455 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3456 pullerManager, metric, ConditionState::kFalse);
3457
3458 // Condition change event.
3459 valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
3460
3461 // Check dump report.
3462 ProtoOutputStream output;
3463 std::set<string> strSet;
3464 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000;
3465 valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
3466 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3467
3468 StatsLogReport report = outputStreamToProto(&output);
3469 EXPECT_TRUE(report.has_value_metrics());
3470 ASSERT_EQ(0, report.value_metrics().data_size());
3471 ASSERT_EQ(1, report.value_metrics().skipped_size());
3472
3473 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3474 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3475 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
3476 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3477 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3478
3479 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3480 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3481 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
3482 }
3483
3484 /*
3485 * Test that MULTIPLE_BUCKETS_SKIPPED dump reason is logged when a log event
3486 * skips over more than one bucket.
3487 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestInvalidBucketWhenMultipleBucketsSkipped)3488 TEST(NumericValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenMultipleBucketsSkipped) {
3489 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3490
3491 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3492 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3493 // Condition change to true.
3494 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3495 vector<std::shared_ptr<LogEvent>>* data) {
3496 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
3497 data->clear();
3498 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
3499 return true;
3500 }))
3501 // Dump report requested.
3502 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3503 vector<std::shared_ptr<LogEvent>>* data) {
3504 EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
3505 data->clear();
3506 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
3507 return true;
3508 }));
3509
3510 sp<NumericValueMetricProducer> valueProducer =
3511 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3512 pullerManager, metric, ConditionState::kFalse);
3513
3514 // Condition change event.
3515 valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
3516
3517 // Condition change event that skips forward by three buckets.
3518 valueProducer->onConditionChanged(false, bucket4StartTimeNs + 10);
3519 // Ensure data structures are appropriately trimmed when multiple buckets are skipped.
3520 ASSERT_EQ(valueProducer->mCurrentSlicedBucket.size(), 0);
3521 ASSERT_EQ(valueProducer->mDimInfos.size(), 1);
3522
3523 int64_t dumpTimeNs = bucket4StartTimeNs + 1000;
3524
3525 // Check dump report.
3526 ProtoOutputStream output;
3527 std::set<string> strSet;
3528 valueProducer->onDumpReport(dumpTimeNs, true /* include current buckets */, true,
3529 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3530
3531 StatsLogReport report = outputStreamToProto(&output);
3532 EXPECT_TRUE(report.has_value_metrics());
3533 ASSERT_EQ(0, report.value_metrics().data_size());
3534 ASSERT_EQ(2, report.value_metrics().skipped_size());
3535
3536 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3537 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3538 EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
3539 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3540 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3541
3542 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3543 EXPECT_EQ(BucketDropReason::MULTIPLE_BUCKETS_SKIPPED, dropEvent.drop_reason());
3544 EXPECT_EQ(NanoToMillis(bucket4StartTimeNs + 10), dropEvent.drop_time_millis());
3545
3546 // This bucket is skipped because a dumpReport with include current buckets is called.
3547 // This creates a new bucket from bucket4StartTimeNs to dumpTimeNs in which we have no data
3548 // since the condition is false for the entire bucket interval.
3549 EXPECT_EQ(NanoToMillis(bucket4StartTimeNs),
3550 report.value_metrics().skipped(1).start_bucket_elapsed_millis());
3551 EXPECT_EQ(NanoToMillis(dumpTimeNs),
3552 report.value_metrics().skipped(1).end_bucket_elapsed_millis());
3553 ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
3554
3555 dropEvent = report.value_metrics().skipped(1).drop_event(0);
3556 EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
3557 EXPECT_EQ(NanoToMillis(dumpTimeNs), dropEvent.drop_time_millis());
3558 }
3559
3560 /*
3561 * Test that BUCKET_TOO_SMALL dump reason is logged when a flushed bucket size
3562 * is smaller than the "min_bucket_size_nanos" specified in the metric config.
3563 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestBucketDropWhenBucketTooSmall)3564 TEST(NumericValueMetricProducerTest_BucketDrop, TestBucketDropWhenBucketTooSmall) {
3565 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3566 metric.set_min_bucket_size_nanos(10000000000); // 10 seconds
3567
3568 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3569 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3570 // Condition change to true.
3571 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3572 vector<std::shared_ptr<LogEvent>>* data) {
3573 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
3574 data->clear();
3575 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
3576 return true;
3577 }))
3578 // Dump report requested.
3579 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3580 vector<std::shared_ptr<LogEvent>>* data) {
3581 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
3582 data->clear();
3583 data->push_back(
3584 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 9000000, 15));
3585 return true;
3586 }));
3587
3588 sp<NumericValueMetricProducer> valueProducer =
3589 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3590 pullerManager, metric, ConditionState::kFalse);
3591
3592 // Condition change event.
3593 valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
3594
3595 // Check dump report.
3596 ProtoOutputStream output;
3597 std::set<string> strSet;
3598 int64_t dumpReportTimeNs = bucketStartTimeNs + 9000000;
3599 valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
3600 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3601
3602 StatsLogReport report = outputStreamToProto(&output);
3603 EXPECT_TRUE(report.has_value_metrics());
3604 ASSERT_EQ(0, report.value_metrics().data_size());
3605 ASSERT_EQ(1, report.value_metrics().skipped_size());
3606
3607 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3608 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3609 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
3610 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3611 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3612
3613 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3614 EXPECT_EQ(BucketDropReason::BUCKET_TOO_SMALL, dropEvent.drop_reason());
3615 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
3616 }
3617
3618 /*
3619 * Test that NO_DATA dump reason is logged when a flushed bucket contains no data.
3620 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestBucketDropWhenDataUnavailable)3621 TEST(NumericValueMetricProducerTest_BucketDrop, TestBucketDropWhenDataUnavailable) {
3622 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3623
3624 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3625
3626 sp<NumericValueMetricProducer> valueProducer =
3627 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3628 pullerManager, metric, ConditionState::kFalse);
3629
3630 // Check dump report.
3631 ProtoOutputStream output;
3632 std::set<string> strSet;
3633 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
3634 valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
3635 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3636
3637 StatsLogReport report = outputStreamToProto(&output);
3638 EXPECT_TRUE(report.has_value_metrics());
3639 ASSERT_EQ(0, report.value_metrics().data_size());
3640 ASSERT_EQ(1, report.value_metrics().skipped_size());
3641
3642 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3643 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3644 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
3645 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3646 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3647
3648 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3649 EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
3650 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
3651 }
3652
3653 /*
3654 * Test that all buckets are dropped due to condition unknown until the first onConditionChanged.
3655 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestConditionUnknownMultipleBuckets)3656 TEST(NumericValueMetricProducerTest_BucketDrop, TestConditionUnknownMultipleBuckets) {
3657 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3658
3659 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3660 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3661 // Condition change to true.
3662 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3663 vector<std::shared_ptr<LogEvent>>* data) {
3664 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
3665 data->clear();
3666 data->push_back(CreateRepeatedValueLogEvent(
3667 tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 10));
3668 return true;
3669 }))
3670 // Dump report requested.
3671 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3672 vector<std::shared_ptr<LogEvent>>* data) {
3673 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
3674 data->clear();
3675 data->push_back(CreateRepeatedValueLogEvent(
3676 tagId, bucket2StartTimeNs + 15 * NS_PER_SEC, 15));
3677 return true;
3678 }));
3679
3680 sp<NumericValueMetricProducer> valueProducer =
3681 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3682 pullerManager, metric, ConditionState::kUnknown);
3683
3684 // Bucket should be dropped because of condition unknown.
3685 int64_t appUpgradeTimeNs = bucketStartTimeNs + 5 * NS_PER_SEC;
3686 valueProducer->notifyAppUpgrade(appUpgradeTimeNs);
3687
3688 // Bucket also dropped due to condition unknown
3689 vector<shared_ptr<LogEvent>> allData;
3690 allData.clear();
3691 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 3));
3692 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
3693
3694 // This bucket is also dropped due to condition unknown.
3695 int64_t conditionChangeTimeNs = bucket2StartTimeNs + 10 * NS_PER_SEC;
3696 valueProducer->onConditionChanged(true, conditionChangeTimeNs);
3697
3698 // Check dump report.
3699 ProtoOutputStream output;
3700 std::set<string> strSet;
3701 int64_t dumpReportTimeNs = bucket2StartTimeNs + 15 * NS_PER_SEC; // 15 seconds
3702 valueProducer->onDumpReport(dumpReportTimeNs, true /* include current bucket */, true,
3703 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3704
3705 StatsLogReport report = outputStreamToProto(&output);
3706 EXPECT_TRUE(report.has_value_metrics());
3707 ASSERT_EQ(0, report.value_metrics().data_size());
3708 ASSERT_EQ(3, report.value_metrics().skipped_size());
3709
3710 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3711 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3712 EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
3713 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3714 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3715
3716 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3717 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
3718 EXPECT_EQ(NanoToMillis(appUpgradeTimeNs), dropEvent.drop_time_millis());
3719
3720 EXPECT_EQ(NanoToMillis(appUpgradeTimeNs),
3721 report.value_metrics().skipped(1).start_bucket_elapsed_millis());
3722 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
3723 report.value_metrics().skipped(1).end_bucket_elapsed_millis());
3724 ASSERT_EQ(1, report.value_metrics().skipped(1).drop_event_size());
3725
3726 dropEvent = report.value_metrics().skipped(1).drop_event(0);
3727 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
3728 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs), dropEvent.drop_time_millis());
3729
3730 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
3731 report.value_metrics().skipped(2).start_bucket_elapsed_millis());
3732 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
3733 report.value_metrics().skipped(2).end_bucket_elapsed_millis());
3734 ASSERT_EQ(1, report.value_metrics().skipped(2).drop_event_size());
3735
3736 dropEvent = report.value_metrics().skipped(2).drop_event(0);
3737 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
3738 EXPECT_EQ(NanoToMillis(conditionChangeTimeNs), dropEvent.drop_time_millis());
3739 }
3740
3741 /*
3742 * Test that a skipped bucket is logged when a forced bucket split occurs when the previous bucket
3743 * was not flushed in time.
3744 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestBucketDropWhenForceBucketSplitBeforeBucketFlush)3745 TEST(NumericValueMetricProducerTest_BucketDrop,
3746 TestBucketDropWhenForceBucketSplitBeforeBucketFlush) {
3747 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3748
3749 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3750 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3751 // Condition change to true.
3752 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3753 vector<std::shared_ptr<LogEvent>>* data) {
3754 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
3755 data->clear();
3756 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
3757 return true;
3758 }))
3759 // App Update.
3760 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3761 vector<std::shared_ptr<LogEvent>>* data) {
3762 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000);
3763 data->clear();
3764 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1000, 15));
3765 return true;
3766 }));
3767
3768 sp<NumericValueMetricProducer> valueProducer =
3769 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3770 pullerManager, metric, ConditionState::kFalse);
3771
3772 // Condition changed event
3773 int64_t conditionChangeTimeNs = bucketStartTimeNs + 10;
3774 valueProducer->onConditionChanged(true, conditionChangeTimeNs);
3775
3776 // App update event.
3777 int64_t appUpdateTimeNs = bucket2StartTimeNs + 1000;
3778 valueProducer->notifyAppUpgrade(appUpdateTimeNs);
3779
3780 // Check dump report.
3781 ProtoOutputStream output;
3782 std::set<string> strSet;
3783 int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000; // 10 seconds
3784 valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
3785 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
3786
3787 StatsLogReport report = outputStreamToProto(&output);
3788 EXPECT_TRUE(report.has_value_metrics());
3789 ASSERT_EQ(1, report.value_metrics().data_size());
3790 ASSERT_EQ(1, report.value_metrics().skipped_size());
3791
3792 ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
3793 auto data = report.value_metrics().data(0);
3794 ASSERT_EQ(0, data.bucket_info(0).bucket_num());
3795 EXPECT_EQ(5, data.bucket_info(0).values(0).value_long());
3796
3797 EXPECT_EQ(NanoToMillis(bucket2StartTimeNs),
3798 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3799 EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
3800 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3801 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
3802
3803 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3804 EXPECT_EQ(BucketDropReason::NO_DATA, dropEvent.drop_reason());
3805 EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
3806 }
3807
3808 /*
3809 * Test multiple bucket drop events in the same bucket.
3810 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestMultipleBucketDropEvents)3811 TEST(NumericValueMetricProducerTest_BucketDrop, TestMultipleBucketDropEvents) {
3812 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3813
3814 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3815 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
3816 // Condition change to true.
3817 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
3818 vector<std::shared_ptr<LogEvent>>* data) {
3819 data->clear();
3820 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
3821 return true;
3822 }));
3823
3824 sp<NumericValueMetricProducer> valueProducer =
3825 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3826 pullerManager, metric, ConditionState::kUnknown);
3827
3828 // Condition change event.
3829 valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
3830
3831 // Check dump report.
3832 ProtoOutputStream output;
3833 std::set<string> strSet;
3834 int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
3835 valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
3836 FAST /* dumpLatency */, &strSet, &output);
3837
3838 StatsLogReport report = outputStreamToProto(&output);
3839 EXPECT_TRUE(report.has_value_metrics());
3840 ASSERT_EQ(0, report.value_metrics().data_size());
3841 ASSERT_EQ(1, report.value_metrics().skipped_size());
3842
3843 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3844 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3845 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
3846 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3847 ASSERT_EQ(2, report.value_metrics().skipped(0).drop_event_size());
3848
3849 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3850 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
3851 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis());
3852
3853 dropEvent = report.value_metrics().skipped(0).drop_event(1);
3854 EXPECT_EQ(BucketDropReason::DUMP_REPORT_REQUESTED, dropEvent.drop_reason());
3855 EXPECT_EQ(NanoToMillis(dumpReportTimeNs), dropEvent.drop_time_millis());
3856 }
3857
3858 /*
3859 * Test that the number of logged bucket drop events is capped at the maximum.
3860 * The maximum is currently 10 and is set in MetricProducer::maxDropEventsReached().
3861 */
TEST(NumericValueMetricProducerTest_BucketDrop,TestMaxBucketDropEvents)3862 TEST(NumericValueMetricProducerTest_BucketDrop, TestMaxBucketDropEvents) {
3863 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
3864
3865 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3866 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3867 // First condition change event.
3868 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3869 vector<std::shared_ptr<LogEvent>>* data) {
3870 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
3871 for (int i = 0; i < 2000; i++) {
3872 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
3873 }
3874 return true;
3875 }))
3876 .WillOnce(Return(false))
3877 .WillOnce(Return(false))
3878 .WillOnce(Return(false))
3879 .WillOnce(Return(false))
3880 .WillOnce(Return(false))
3881 .WillOnce(Return(false))
3882 .WillOnce(Return(false))
3883 .WillOnce(Return(false))
3884 .WillOnce(Return(false))
3885 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3886 vector<std::shared_ptr<LogEvent>>* data) {
3887 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
3888 data->clear();
3889 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
3890 return true;
3891 }));
3892
3893 sp<NumericValueMetricProducer> valueProducer =
3894 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
3895 pullerManager, metric, ConditionState::kUnknown);
3896
3897 // First condition change event causes guardrail to be reached.
3898 valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
3899
3900 // 2-10 condition change events result in failed pulls.
3901 valueProducer->onConditionChanged(false, bucketStartTimeNs + 30);
3902 valueProducer->onConditionChanged(true, bucketStartTimeNs + 50);
3903 valueProducer->onConditionChanged(false, bucketStartTimeNs + 70);
3904 valueProducer->onConditionChanged(true, bucketStartTimeNs + 90);
3905 valueProducer->onConditionChanged(false, bucketStartTimeNs + 100);
3906 valueProducer->onConditionChanged(true, bucketStartTimeNs + 150);
3907 valueProducer->onConditionChanged(false, bucketStartTimeNs + 170);
3908 valueProducer->onConditionChanged(true, bucketStartTimeNs + 190);
3909 valueProducer->onConditionChanged(false, bucketStartTimeNs + 200);
3910
3911 // Condition change event 11
3912 valueProducer->onConditionChanged(true, bucketStartTimeNs + 220);
3913
3914 // Check dump report.
3915 ProtoOutputStream output;
3916 std::set<string> strSet;
3917 int64_t dumpReportTimeNs = bucketStartTimeNs + 1000;
3918 // Because we already have 10 dump events in the current bucket,
3919 // this case should not be added to the list of dump events.
3920 valueProducer->onDumpReport(bucketStartTimeNs + 1000, true /* include recent buckets */, true,
3921 FAST /* dumpLatency */, &strSet, &output);
3922
3923 StatsLogReport report = outputStreamToProto(&output);
3924 EXPECT_TRUE(report.has_value_metrics());
3925 ASSERT_EQ(0, report.value_metrics().data_size());
3926 ASSERT_EQ(1, report.value_metrics().skipped_size());
3927
3928 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
3929 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
3930 EXPECT_EQ(NanoToMillis(dumpReportTimeNs),
3931 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
3932 ASSERT_EQ(10, report.value_metrics().skipped(0).drop_event_size());
3933
3934 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
3935 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
3936 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 10), dropEvent.drop_time_millis());
3937
3938 dropEvent = report.value_metrics().skipped(0).drop_event(1);
3939 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3940 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 30), dropEvent.drop_time_millis());
3941
3942 dropEvent = report.value_metrics().skipped(0).drop_event(2);
3943 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3944 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 50), dropEvent.drop_time_millis());
3945
3946 dropEvent = report.value_metrics().skipped(0).drop_event(3);
3947 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3948 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 70), dropEvent.drop_time_millis());
3949
3950 dropEvent = report.value_metrics().skipped(0).drop_event(4);
3951 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3952 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 90), dropEvent.drop_time_millis());
3953
3954 dropEvent = report.value_metrics().skipped(0).drop_event(5);
3955 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3956 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 100), dropEvent.drop_time_millis());
3957
3958 dropEvent = report.value_metrics().skipped(0).drop_event(6);
3959 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3960 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 150), dropEvent.drop_time_millis());
3961
3962 dropEvent = report.value_metrics().skipped(0).drop_event(7);
3963 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3964 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 170), dropEvent.drop_time_millis());
3965
3966 dropEvent = report.value_metrics().skipped(0).drop_event(8);
3967 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3968 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 190), dropEvent.drop_time_millis());
3969
3970 dropEvent = report.value_metrics().skipped(0).drop_event(9);
3971 EXPECT_EQ(BucketDropReason::PULL_FAILED, dropEvent.drop_reason());
3972 EXPECT_EQ(NanoToMillis(bucketStartTimeNs + 200), dropEvent.drop_time_millis());
3973 }
3974
3975 /*
3976 * Test metric with a simple sliced state
3977 * - Increasing values
3978 * - Using diff
3979 * - Second field is value field
3980 */
TEST(NumericValueMetricProducerTest,TestSlicedState)3981 TEST(NumericValueMetricProducerTest, TestSlicedState) {
3982 // Set up NumericValueMetricProducer.
3983 ValueMetric metric =
3984 NumericValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
3985 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
3986 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
3987 // NumericValueMetricProducer initialized.
3988 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3989 vector<std::shared_ptr<LogEvent>>* data) {
3990 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
3991 data->clear();
3992 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
3993 return true;
3994 }))
3995 // Screen state change to ON.
3996 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
3997 vector<std::shared_ptr<LogEvent>>* data) {
3998 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
3999 data->clear();
4000 data->push_back(
4001 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
4002 return true;
4003 }))
4004 // Screen state change to OFF.
4005 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4006 vector<std::shared_ptr<LogEvent>>* data) {
4007 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
4008 data->clear();
4009 data->push_back(
4010 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9));
4011 return true;
4012 }))
4013 // Screen state change to ON.
4014 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4015 vector<std::shared_ptr<LogEvent>>* data) {
4016 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
4017 data->clear();
4018 data->push_back(CreateRepeatedValueLogEvent(
4019 tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
4020 return true;
4021 }))
4022 // Dump report requested.
4023 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4024 vector<std::shared_ptr<LogEvent>>* data) {
4025 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
4026 data->clear();
4027 data->push_back(CreateRepeatedValueLogEvent(
4028 tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
4029 return true;
4030 }));
4031
4032 StateManager::getInstance().clear();
4033 sp<NumericValueMetricProducer> valueProducer =
4034 NumericValueMetricProducerTestHelper::createValueProducerWithState(
4035 pullerManager, metric, {util::SCREEN_STATE_CHANGED}, {});
4036 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
4037
4038 // Set up StateManager and check that StateTrackers are initialized.
4039 StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
4040 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
4041 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
4042
4043 // Bucket status after metric initialized.
4044 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
4045 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4046 // Base for dimension key {
4047 auto it = valueProducer->mCurrentSlicedBucket.begin();
4048 auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4049 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4050 EXPECT_EQ(3, itBase->second.dimExtras[0].value().long_value);
4051 EXPECT_TRUE(itBase->second.hasCurrentState);
4052 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4053 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4054 itBase->second.currentState.getValues()[0].mValue.int_value);
4055 // Value for dimension, state key {{}, kStateUnknown}
4056 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4057 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4058 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4059 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4060 EXPECT_EQ(0, it->second.intervals[0].sampleSize);
4061 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
4062
4063 // Bucket status after screen state change kStateUnknown->ON.
4064 auto screenEvent = CreateScreenStateChangedEvent(
4065 bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
4066 StateManager::getInstance().onLogEvent(*screenEvent);
4067 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4068 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4069 // Base for dimension key {}
4070 it = valueProducer->mCurrentSlicedBucket.begin();
4071 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4072 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4073 EXPECT_EQ(5, itBase->second.dimExtras[0].value().long_value);
4074 EXPECT_TRUE(itBase->second.hasCurrentState);
4075 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4076 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
4077 itBase->second.currentState.getValues()[0].mValue.int_value);
4078 // Value for dimension, state key {{}, ON}
4079 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4080 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4081 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
4082 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4083 EXPECT_EQ(0, it->second.intervals.size());
4084 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
4085 // Value for dimension, state key {{}, kStateUnknown}
4086 it++;
4087 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4088 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4089 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4090 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4091 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4092 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
4093 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4094 bucketStartTimeNs + 5 * NS_PER_SEC);
4095
4096 // Bucket status after screen state change ON->OFF.
4097 screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
4098 android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
4099 StateManager::getInstance().onLogEvent(*screenEvent);
4100 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
4101 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4102 // Base for dimension key {}
4103 it = valueProducer->mCurrentSlicedBucket.begin();
4104 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4105 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4106 EXPECT_EQ(9, itBase->second.dimExtras[0].value().long_value);
4107 EXPECT_TRUE(itBase->second.hasCurrentState);
4108 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
4109 itBase->second.currentState.getValues()[0].mValue.int_value);
4110 // Value for dimension, state key {{}, OFF}
4111 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4112 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4113 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
4114 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4115 EXPECT_EQ(0, it->second.intervals.size());
4116 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
4117 // Value for dimension, state key {{}, ON}
4118 it++;
4119 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4120 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4121 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
4122 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4123 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4124 EXPECT_EQ(4, it->second.intervals[0].aggregate.long_value);
4125 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4126 bucketStartTimeNs + 10 * NS_PER_SEC);
4127 // Value for dimension, state key {{}, kStateUnknown}
4128 it++;
4129 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4130 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4131 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4132 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4133 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4134 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
4135 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4136 bucketStartTimeNs + 5 * NS_PER_SEC);
4137
4138 // Bucket status after screen state change OFF->ON.
4139 screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
4140 android::view::DisplayStateEnum::DISPLAY_STATE_ON);
4141 StateManager::getInstance().onLogEvent(*screenEvent);
4142 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
4143 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4144 // Base for dimension key {}
4145 it = valueProducer->mCurrentSlicedBucket.begin();
4146 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4147 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4148 EXPECT_EQ(21, itBase->second.dimExtras[0].value().long_value);
4149 EXPECT_TRUE(itBase->second.hasCurrentState);
4150 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4151 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
4152 itBase->second.currentState.getValues()[0].mValue.int_value);
4153 // Value for dimension, state key {{}, OFF}
4154 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4155 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4156 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
4157 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4158 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4159 EXPECT_EQ(12, it->second.intervals[0].aggregate.long_value);
4160 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4161 bucketStartTimeNs + 15 * NS_PER_SEC);
4162 // Value for dimension, state key {{}, ON}
4163 it++;
4164 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4165 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4166 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
4167 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4168 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4169 EXPECT_EQ(4, it->second.intervals[0].aggregate.long_value);
4170 assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC,
4171 bucketStartTimeNs + 15 * NS_PER_SEC);
4172 // Value for dimension, state key {{}, kStateUnknown}
4173 it++;
4174 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4175 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4176 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4177 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4178 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4179 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
4180 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4181 bucketStartTimeNs + 5 * NS_PER_SEC);
4182
4183 // Start dump report and check output.
4184 ProtoOutputStream output;
4185 std::set<string> strSet;
4186 valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
4187 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
4188 &strSet, &output);
4189
4190 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
4191 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4192 // Base for dimension key {}
4193 it = valueProducer->mCurrentSlicedBucket.begin();
4194 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4195 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4196 EXPECT_EQ(30, itBase->second.dimExtras[0].value().long_value);
4197 EXPECT_TRUE(itBase->second.hasCurrentState);
4198 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4199 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
4200 itBase->second.currentState.getValues()[0].mValue.int_value);
4201 // Value for dimension, state key {{}, ON}
4202 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4203 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4204 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
4205 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4206 EXPECT_EQ(it->second.intervals[0].sampleSize, 0);
4207 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 50 * NS_PER_SEC);
4208
4209 StatsLogReport report = outputStreamToProto(&output);
4210 EXPECT_TRUE(report.has_value_metrics());
4211 ASSERT_EQ(3, report.value_metrics().data_size());
4212
4213 // {{}, kStateUnknown}
4214 auto data = report.value_metrics().data(0);
4215 ASSERT_EQ(1, data.bucket_info_size());
4216 EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
4217 EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
4218 EXPECT_TRUE(data.slice_by_state(0).has_value());
4219 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
4220 EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
4221
4222 // {{}, ON}
4223 data = report.value_metrics().data(1);
4224 ASSERT_EQ(1, data.bucket_info_size());
4225 EXPECT_EQ(13, data.bucket_info(0).values(0).value_long());
4226 EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
4227 EXPECT_TRUE(data.slice_by_state(0).has_value());
4228 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
4229 EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
4230
4231 // {{}, OFF}
4232 data = report.value_metrics().data(2);
4233 ASSERT_EQ(1, data.bucket_info_size());
4234 EXPECT_EQ(12, data.bucket_info(0).values(0).value_long());
4235 EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
4236 EXPECT_TRUE(data.slice_by_state(0).has_value());
4237 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
4238 EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
4239 }
4240
4241 /*
4242 * Test metric with sliced state with map
4243 * - Increasing values
4244 * - Using diff
4245 * - Second field is value field
4246 */
TEST(NumericValueMetricProducerTest,TestSlicedStateWithMap)4247 TEST(NumericValueMetricProducerTest, TestSlicedStateWithMap) {
4248 // Set up NumericValueMetricProducer.
4249 ValueMetric metric =
4250 NumericValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
4251 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
4252 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
4253 // NumericValueMetricProducer initialized.
4254 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4255 vector<std::shared_ptr<LogEvent>>* data) {
4256 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
4257 data->clear();
4258 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
4259 return true;
4260 }))
4261 // Screen state change to ON.
4262 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4263 vector<std::shared_ptr<LogEvent>>* data) {
4264 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
4265 data->clear();
4266 data->push_back(
4267 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
4268 return true;
4269 }))
4270 // Screen state change to VR has no pull because it is in the same
4271 // state group as ON.
4272
4273 // Screen state change to ON has no pull because it is in the same
4274 // state group as VR.
4275
4276 // Screen state change to OFF.
4277 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4278 vector<std::shared_ptr<LogEvent>>* data) {
4279 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
4280 data->clear();
4281 data->push_back(CreateRepeatedValueLogEvent(
4282 tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
4283 return true;
4284 }))
4285 // Dump report requested.
4286 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4287 vector<std::shared_ptr<LogEvent>>* data) {
4288 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
4289 data->clear();
4290 data->push_back(CreateRepeatedValueLogEvent(
4291 tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
4292 return true;
4293 }));
4294
4295 const StateMap& stateMap =
4296 CreateScreenStateOnOffMap(/*screen on id=*/321, /*screen off id=*/123);
4297 const StateMap_StateGroup screenOnGroup = stateMap.group(0);
4298 const StateMap_StateGroup screenOffGroup = stateMap.group(1);
4299
4300 unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
4301 for (auto group : stateMap.group()) {
4302 for (auto value : group.value()) {
4303 stateGroupMap[SCREEN_STATE_ATOM_ID][value] = group.group_id();
4304 }
4305 }
4306
4307 StateManager::getInstance().clear();
4308 sp<NumericValueMetricProducer> valueProducer =
4309 NumericValueMetricProducerTestHelper::createValueProducerWithState(
4310 pullerManager, metric, {util::SCREEN_STATE_CHANGED}, stateGroupMap);
4311
4312 // Set up StateManager and check that StateTrackers are initialized.
4313 StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
4314 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
4315 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(SCREEN_STATE_ATOM_ID));
4316
4317 // Bucket status after metric initialized.
4318 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
4319 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4320 // Base for dimension key {}
4321 auto it = valueProducer->mCurrentSlicedBucket.begin();
4322 auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4323 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4324 EXPECT_EQ(3, itBase->second.dimExtras[0].value().long_value);
4325 EXPECT_TRUE(itBase->second.hasCurrentState);
4326 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4327 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4328 itBase->second.currentState.getValues()[0].mValue.int_value);
4329 // Value for dimension, state key {{}, {kStateUnknown}}
4330 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4331 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4332 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4333 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4334 EXPECT_EQ(0, it->second.intervals[0].sampleSize);
4335 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
4336
4337 // Bucket status after screen state change kStateUnknown->ON.
4338 auto screenEvent = CreateScreenStateChangedEvent(
4339 bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
4340 StateManager::getInstance().onLogEvent(*screenEvent);
4341 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4342 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4343 // Base for dimension key {}
4344 it = valueProducer->mCurrentSlicedBucket.begin();
4345 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4346 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4347 EXPECT_EQ(5, itBase->second.dimExtras[0].value().long_value);
4348 EXPECT_TRUE(itBase->second.hasCurrentState);
4349 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4350 EXPECT_EQ(screenOnGroup.group_id(),
4351 itBase->second.currentState.getValues()[0].mValue.long_value);
4352 // Value for dimension, state key {{}, ON GROUP}
4353 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4354 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4355 EXPECT_EQ(screenOnGroup.group_id(),
4356 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4357 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
4358 // Value for dimension, state key {{}, kStateUnknown}
4359 it++;
4360 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4361 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4362 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4363 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4364 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4365 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
4366 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4367 bucketStartTimeNs + 5 * NS_PER_SEC);
4368
4369 // Bucket status after screen state change ON->VR.
4370 // Both ON and VR are in the same state group, so the base should not change.
4371 screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
4372 android::view::DisplayStateEnum::DISPLAY_STATE_VR);
4373 StateManager::getInstance().onLogEvent(*screenEvent);
4374 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4375 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4376 // Base for dimension key {}
4377 it = valueProducer->mCurrentSlicedBucket.begin();
4378 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4379 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4380 EXPECT_EQ(5, itBase->second.dimExtras[0].value().long_value);
4381 EXPECT_TRUE(itBase->second.hasCurrentState);
4382 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4383 EXPECT_EQ(screenOnGroup.group_id(),
4384 itBase->second.currentState.getValues()[0].mValue.int_value);
4385 // Value for dimension, state key {{}, ON GROUP}
4386 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4387 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4388 EXPECT_EQ(screenOnGroup.group_id(),
4389 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4390 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
4391 // Value for dimension, state key {{}, kStateUnknown}
4392 it++;
4393 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4394 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4395 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4396 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4397 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4398 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
4399 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4400 bucketStartTimeNs + 5 * NS_PER_SEC);
4401
4402 // Bucket status after screen state change VR->ON.
4403 // Both ON and VR are in the same state group, so the base should not change.
4404 screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC,
4405 android::view::DisplayStateEnum::DISPLAY_STATE_ON);
4406 StateManager::getInstance().onLogEvent(*screenEvent);
4407 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4408 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4409 // Base for dimension key {}
4410 it = valueProducer->mCurrentSlicedBucket.begin();
4411 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4412 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4413 EXPECT_EQ(5, itBase->second.dimExtras[0].value().long_value);
4414 EXPECT_TRUE(itBase->second.hasCurrentState);
4415 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4416 EXPECT_EQ(screenOnGroup.group_id(),
4417 itBase->second.currentState.getValues()[0].mValue.int_value);
4418 // Value for dimension, state key {{}, ON GROUP}
4419 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4420 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4421 EXPECT_EQ(screenOnGroup.group_id(),
4422 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4423 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
4424 // Value for dimension, state key {{}, kStateUnknown}
4425 it++;
4426 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4427 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4428 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4429 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4430 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4431 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
4432 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4433 bucketStartTimeNs + 5 * NS_PER_SEC);
4434
4435 // Bucket status after screen state change VR->OFF.
4436 screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
4437 android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
4438 StateManager::getInstance().onLogEvent(*screenEvent);
4439 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
4440 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4441 // Base for dimension key {}
4442 it = valueProducer->mCurrentSlicedBucket.begin();
4443 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4444 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4445 EXPECT_EQ(21, itBase->second.dimExtras[0].value().long_value);
4446 EXPECT_TRUE(itBase->second.hasCurrentState);
4447 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4448 EXPECT_EQ(screenOffGroup.group_id(),
4449 itBase->second.currentState.getValues()[0].mValue.int_value);
4450 // Value for dimension, state key {{}, OFF GROUP}
4451 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4452 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4453 EXPECT_EQ(screenOffGroup.group_id(),
4454 it->first.getStateValuesKey().getValues()[0].mValue.long_value);
4455 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC);
4456 // Value for dimension, state key {{}, ON GROUP}
4457 it++;
4458 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4459 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4460 EXPECT_EQ(screenOnGroup.group_id(),
4461 it->first.getStateValuesKey().getValues()[0].mValue.long_value);
4462 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4463 EXPECT_EQ(16, it->second.intervals[0].aggregate.long_value);
4464 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
4465 bucketStartTimeNs + 15 * NS_PER_SEC);
4466 // Value for dimension, state key {{}, kStateUnknown}
4467 it++;
4468 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4469 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4470 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4471 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4472 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
4473 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
4474 assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
4475 bucketStartTimeNs + 5 * NS_PER_SEC);
4476
4477 // Start dump report and check output.
4478 ProtoOutputStream output;
4479 std::set<string> strSet;
4480 valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
4481 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
4482 &strSet, &output);
4483
4484 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
4485 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4486 // Base for dimension key {}
4487 it = valueProducer->mCurrentSlicedBucket.begin();
4488 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4489 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
4490 EXPECT_EQ(30, itBase->second.dimExtras[0].value().long_value);
4491 EXPECT_TRUE(itBase->second.hasCurrentState);
4492 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4493 EXPECT_EQ(screenOffGroup.group_id(),
4494 itBase->second.currentState.getValues()[0].mValue.int_value);
4495 // Value for dimension, state key {{}, OFF GROUP}
4496 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4497 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4498 EXPECT_EQ(screenOffGroup.group_id(),
4499 it->first.getStateValuesKey().getValues()[0].mValue.long_value);
4500 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 50 * NS_PER_SEC);
4501
4502 StatsLogReport report = outputStreamToProto(&output);
4503 EXPECT_TRUE(report.has_value_metrics());
4504 ASSERT_EQ(3, report.value_metrics().data_size());
4505
4506 // {{}, kStateUnknown}
4507 auto data = report.value_metrics().data(0);
4508 ASSERT_EQ(1, data.bucket_info_size());
4509 EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
4510 EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
4511 EXPECT_TRUE(data.slice_by_state(0).has_value());
4512 EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
4513 EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
4514
4515 // {{}, ON GROUP}
4516 data = report.value_metrics().data(1);
4517 ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
4518 EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
4519 EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
4520 EXPECT_TRUE(data.slice_by_state(0).has_group_id());
4521 EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
4522 EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
4523
4524 // {{}, OFF GROUP}
4525 data = report.value_metrics().data(2);
4526 ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
4527 EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
4528 EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
4529 EXPECT_TRUE(data.slice_by_state(0).has_group_id());
4530 EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
4531 EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
4532 }
4533
4534 /*
4535 * Test metric that slices by state with a primary field and has dimensions
4536 * - Increasing values
4537 * - Using diff
4538 * - Second field is value field
4539 */
TEST(NumericValueMetricProducerTest,TestSlicedStateWithPrimaryField_WithDimensions)4540 TEST(NumericValueMetricProducerTest, TestSlicedStateWithPrimaryField_WithDimensions) {
4541 // Set up NumericValueMetricProducer.
4542 ValueMetric metric =
4543 NumericValueMetricProducerTestHelper::createMetricWithState("UID_PROCESS_STATE");
4544 metric.mutable_dimensions_in_what()->set_field(tagId);
4545 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
4546 metric.set_condition_correction_threshold_nanos(0);
4547
4548 MetricStateLink* stateLink = metric.add_state_link();
4549 stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
4550 auto fieldsInWhat = stateLink->mutable_fields_in_what();
4551 *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
4552 auto fieldsInState = stateLink->mutable_fields_in_state();
4553 *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
4554
4555 /*
4556 NOTE: "1" denotes uid 1 and "2" denotes uid 2.
4557 bucket # 1 bucket # 2
4558 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
4559 |------------------------------------------|---------------------------------|--
4560
4561 (kStateUnknown)
4562 1
4563 |-------------|
4564 20
4565
4566 2
4567 |----------------------------|
4568 40
4569
4570 (FOREGROUND)
4571 1 1
4572 |----------------------------|-------------| |------|
4573 40 20 10
4574
4575
4576 (BACKGROUND)
4577 1
4578 |------------|
4579 20
4580 2
4581 |-------------|---------------------------------|
4582 20 50
4583 */
4584 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
4585 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
4586 // NumericValueMetricProducer initialized.
4587 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4588 vector<std::shared_ptr<LogEvent>>* data) {
4589 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
4590 data->clear();
4591 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 3));
4592 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
4593 return true;
4594 }))
4595 // Uid 1 process state change from kStateUnknown -> Foreground
4596 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4597 vector<std::shared_ptr<LogEvent>>* data) {
4598 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
4599 data->clear();
4600 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
4601 1 /*uid*/, 6));
4602 // This event should be skipped.
4603 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
4604 2 /*uid*/, 8));
4605 return true;
4606 }))
4607 // Uid 2 process state change from kStateUnknown -> Background
4608 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4609 vector<std::shared_ptr<LogEvent>>* data) {
4610 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
4611 data->clear();
4612 // This event should be skipped.
4613 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
4614 1 /*uid*/, 12));
4615 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
4616 2 /*uid*/, 9));
4617 return true;
4618 }))
4619 // Uid 1 process state change from Foreground -> Background
4620 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4621 vector<std::shared_ptr<LogEvent>>* data) {
4622 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC);
4623 data->clear();
4624 data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
4625 1 /*uid*/, 13));
4626 // This event should be skipped.
4627 data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
4628 2 /*uid*/, 11));
4629 return true;
4630 }))
4631 // Uid 1 process state change from Background -> Foreground
4632 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4633 vector<std::shared_ptr<LogEvent>>* data) {
4634 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC);
4635 data->clear();
4636 data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
4637 1 /*uid*/, 17));
4638
4639 // This event should be skipped.
4640 data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
4641 2 /*uid */, 15));
4642 return true;
4643 }))
4644 // Dump report pull.
4645 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4646 vector<std::shared_ptr<LogEvent>>* data) {
4647 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
4648 data->clear();
4649 data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
4650 1 /*uid*/, 21));
4651 data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
4652 2 /*uid*/, 20));
4653 return true;
4654 }));
4655
4656 StateManager::getInstance().clear();
4657 sp<NumericValueMetricProducer> valueProducer =
4658 NumericValueMetricProducerTestHelper::createValueProducerWithState(
4659 pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {});
4660
4661 // Set up StateManager and check that StateTrackers are initialized.
4662 StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
4663 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
4664 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
4665
4666 // Bucket status after metric initialized.
4667 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4668 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
4669
4670 // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
4671 auto uidProcessEvent =
4672 CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
4673 android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
4674 StateManager::getInstance().onLogEvent(*uidProcessEvent);
4675 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
4676 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
4677
4678 // Bucket status after uid 2 process state change kStateUnknown -> Background.
4679 uidProcessEvent =
4680 CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */,
4681 android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
4682 StateManager::getInstance().onLogEvent(*uidProcessEvent);
4683 ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
4684 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
4685
4686 // Pull at end of first bucket.
4687 vector<shared_ptr<LogEvent>> allData;
4688 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 10));
4689 allData.push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 15));
4690 valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
4691
4692 // Ensure the MetricDimensionKeys for the current state are kept.
4693 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4694 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
4695 auto it = valueProducer->mCurrentSlicedBucket.begin(); // dimension, state key {2, BACKGROUND}
4696 EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
4697 EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
4698 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4699 EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
4700 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4701 it++; // dimension, state key {1, FOREGROUND}
4702 EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
4703 EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
4704 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4705 EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
4706 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4707
4708 // Bucket status after uid 1 process state change from Foreground -> Background.
4709 uidProcessEvent =
4710 CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
4711 android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
4712 StateManager::getInstance().onLogEvent(*uidProcessEvent);
4713 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
4714 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
4715
4716 // Bucket status after uid 1 process state change Background->Foreground.
4717 uidProcessEvent =
4718 CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
4719 android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
4720 StateManager::getInstance().onLogEvent(*uidProcessEvent);
4721 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
4722 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
4723
4724 // Start dump report and check output.
4725 ProtoOutputStream output;
4726 std::set<string> strSet;
4727 int64_t dumpReportTimeNs = bucket2StartTimeNs + 50 * NS_PER_SEC;
4728 valueProducer->onDumpReport(dumpReportTimeNs, true /* include recent buckets */, true,
4729 NO_TIME_CONSTRAINTS, &strSet, &output);
4730
4731 StatsLogReport report = outputStreamToProto(&output);
4732 backfillDimensionPath(&report);
4733 backfillStartEndTimestamp(&report);
4734 EXPECT_TRUE(report.has_value_metrics());
4735 StatsLogReport::ValueMetricDataWrapper valueMetrics;
4736 sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
4737 ASSERT_EQ(5, valueMetrics.data_size());
4738 ASSERT_EQ(0, report.value_metrics().skipped_size());
4739
4740 // {uid 1, kStateUnknown}
4741 ValueMetricData data = valueMetrics.data(0);
4742 ASSERT_EQ(1, data.bucket_info_size());
4743 ValidateUidDimension(data.dimensions_in_what(), tagId, 1);
4744 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
4745 -1 /*StateTracker::kStateUnknown*/);
4746 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {3},
4747 20 * NS_PER_SEC, 0);
4748
4749 // {uid 1, FOREGROUND}
4750 data = valueMetrics.data(1);
4751 ASSERT_EQ(2, data.bucket_info_size());
4752 ValidateUidDimension(data.dimensions_in_what(), tagId, 1);
4753 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
4754 android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
4755 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {4},
4756 40 * NS_PER_SEC, 1);
4757 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, dumpReportTimeNs, {7},
4758 30 * NS_PER_SEC, -1);
4759
4760 // {uid 1, BACKGROUND}
4761 data = valueMetrics.data(2);
4762 ASSERT_EQ(1, data.bucket_info_size());
4763 ValidateUidDimension(data.dimensions_in_what(), tagId, 1);
4764 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
4765 android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
4766 ValidateValueBucket(data.bucket_info(0), bucket2StartTimeNs, dumpReportTimeNs, {4},
4767 20 * NS_PER_SEC, -1);
4768
4769 // {uid 2, kStateUnknown}
4770 data = valueMetrics.data(3);
4771 ASSERT_EQ(1, data.bucket_info_size());
4772 ValidateUidDimension(data.dimensions_in_what(), tagId, 2);
4773 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
4774 -1 /*StateTracker::kStateUnknown*/);
4775 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {2},
4776 40 * NS_PER_SEC, -1);
4777
4778 // {uid 2, BACKGROUND}
4779 data = valueMetrics.data(4);
4780 ASSERT_EQ(2, data.bucket_info_size());
4781 ValidateUidDimension(data.dimensions_in_what(), tagId, 2);
4782 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
4783 android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
4784 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {6},
4785 20 * NS_PER_SEC, 1);
4786 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, dumpReportTimeNs, {5},
4787 50 * NS_PER_SEC, -1);
4788 }
4789
4790 /*
4791 * Test slicing condition_true_nanos by state for metric that slices by state when data is not
4792 * present in pulled data during a state change.
4793 */
TEST(NumericValueMetricProducerTest,TestSlicedStateWithMissingDataInStateChange)4794 TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) {
4795 // Set up NumericValueMetricProducer.
4796 ValueMetric metric =
4797 NumericValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
4798 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
4799 /*
4800 NOTE: "-" means that the data was not present in the pulled data.
4801
4802 bucket # 1
4803 10 20 30 40 50 60 (seconds)
4804 |-------------------------------------------------------|--
4805 x (kStateUnknown)
4806 |-----------|
4807 10
4808
4809 x x (ON)
4810 |---------------------| |-----------|
4811 20 10
4812
4813 - (OFF)
4814 */
4815 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
4816 // NumericValueMetricProducer initialized.
4817 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4818 vector<std::shared_ptr<LogEvent>>* data) {
4819 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
4820 data->clear();
4821 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
4822 return true;
4823 }))
4824 // Battery saver mode state changed to ON.
4825 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4826 vector<std::shared_ptr<LogEvent>>* data) {
4827 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
4828 data->clear();
4829 data->push_back(
4830 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
4831 return true;
4832 }))
4833 // Battery saver mode state changed to OFF but data for dimension key {} is not present
4834 // in the pulled data.
4835 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4836 vector<std::shared_ptr<LogEvent>>* data) {
4837 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
4838 data->clear();
4839 return true;
4840 }))
4841 // Battery saver mode state changed to ON.
4842 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4843 vector<std::shared_ptr<LogEvent>>* data) {
4844 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
4845 data->clear();
4846 data->push_back(
4847 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7));
4848 return true;
4849 }))
4850 // Dump report pull.
4851 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
4852 vector<std::shared_ptr<LogEvent>>* data) {
4853 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
4854 data->clear();
4855 data->push_back(CreateRepeatedValueLogEvent(
4856 tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
4857 return true;
4858 }));
4859
4860 StateManager::getInstance().clear();
4861 sp<NumericValueMetricProducer> valueProducer =
4862 NumericValueMetricProducerTestHelper::createValueProducerWithState(
4863 pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
4864 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
4865
4866 // Set up StateManager and check that StateTrackers are initialized.
4867 StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
4868 valueProducer);
4869 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
4870 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
4871 util::BATTERY_SAVER_MODE_STATE_CHANGED));
4872
4873 // Bucket status after metric initialized.
4874 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
4875 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4876 // Base for dimension key {}
4877 auto it = valueProducer->mCurrentSlicedBucket.begin();
4878 auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4879 EXPECT_TRUE(itBase->second.hasCurrentState);
4880 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4881 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4882 itBase->second.currentState.getValues()[0].mValue.int_value);
4883 // Value for dimension, state key {{}, kStateUnknown}
4884 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4885 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4886 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
4887 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4888 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
4889
4890 // Bucket status after battery saver mode ON event.
4891 unique_ptr<LogEvent> batterySaverOnEvent =
4892 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
4893 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
4894
4895 // Base for dimension key {}
4896
4897 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4898 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4899 it = valueProducer->mCurrentSlicedBucket.begin();
4900 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4901 EXPECT_TRUE(itBase->second.hasCurrentState);
4902 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4903 EXPECT_EQ(BatterySaverModeStateChanged::ON,
4904 itBase->second.currentState.getValues()[0].mValue.int_value);
4905 // Value for key {{}, ON}
4906 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4907 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4908 EXPECT_EQ(BatterySaverModeStateChanged::ON,
4909 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4910 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
4911
4912 // Value for key {{}, -1}
4913 it++;
4914 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4915 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4916 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
4917 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4918 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
4919 bucketStartTimeNs + 10 * NS_PER_SEC);
4920
4921 // Bucket status after battery saver mode OFF event which is not present
4922 // in the pulled data.
4923 unique_ptr<LogEvent> batterySaverOffEvent =
4924 CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
4925 StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
4926
4927 // Base for dimension key {} is cleared.
4928 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
4929 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4930 it = valueProducer->mCurrentSlicedBucket.begin();
4931 // Value for key {{}, ON}
4932 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4933 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4934 EXPECT_EQ(BatterySaverModeStateChanged::ON,
4935 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4936 assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
4937 bucketStartTimeNs + 30 * NS_PER_SEC);
4938
4939 // Value for key {{}, -1}
4940 it++;
4941 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4942 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4943 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
4944 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4945 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
4946 bucketStartTimeNs + 10 * NS_PER_SEC);
4947
4948 // Bucket status after battery saver mode ON event.
4949 batterySaverOnEvent =
4950 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC);
4951 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
4952
4953 // Base for dimension key {}
4954 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
4955 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
4956 it = valueProducer->mCurrentSlicedBucket.begin();
4957 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
4958 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
4959 EXPECT_EQ(BatterySaverModeStateChanged::ON,
4960 itBase->second.currentState.getValues()[0].mValue.int_value);
4961 // Value for key {{}, ON}
4962 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4963 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4964 EXPECT_EQ(BatterySaverModeStateChanged::ON,
4965 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4966 assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
4967 bucketStartTimeNs + 40 * NS_PER_SEC);
4968
4969 // Value for key {{}, -1}
4970 it++;
4971 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
4972 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
4973 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
4974 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
4975 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
4976 bucketStartTimeNs + 10 * NS_PER_SEC);
4977
4978 // Start dump report and check output.
4979 ProtoOutputStream output;
4980 std::set<string> strSet;
4981 valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
4982 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
4983 &strSet, &output);
4984
4985 StatsLogReport report = outputStreamToProto(&output);
4986 backfillDimensionPath(&report);
4987 backfillStartEndTimestamp(&report);
4988 EXPECT_TRUE(report.has_value_metrics());
4989 ASSERT_EQ(2, report.value_metrics().data_size());
4990
4991 // {{}, kStateUnknown}
4992 ValueMetricData data = report.value_metrics().data(0);
4993 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
4994 EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
4995 ASSERT_EQ(1, data.bucket_info_size());
4996 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC,
4997 {2}, 10 * NS_PER_SEC, -1);
4998
4999 // {{}, ON}
5000 data = report.value_metrics().data(1);
5001 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
5002 EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
5003 ASSERT_EQ(1, data.bucket_info_size());
5004 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC,
5005 {8}, 30 * NS_PER_SEC, -1);
5006 }
5007
5008 /*
5009 * Test for metric that slices by state when data is not present in pulled data
5010 * during an event and then a flush occurs for the current bucket. With the new
5011 * condition timer behavior, a "new" MetricDimensionKey is inserted into
5012 * `mCurrentSlicedBucket` before intervals are closed/added to that new
5013 * MetricDimensionKey.
5014 */
TEST(NumericValueMetricProducerTest,TestSlicedStateWithMissingDataThenFlushBucket)5015 TEST(NumericValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) {
5016 // Set up NumericValueMetricProducer.
5017 ValueMetric metric =
5018 NumericValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
5019 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
5020 /*
5021 NOTE: "-" means that the data was not present in the pulled data.
5022
5023 bucket # 1
5024 10 20 30 40 50 60 (seconds)
5025 |-------------------------------------------------------|--
5026 - (kStateUnknown)
5027
5028 - (ON)
5029 */
5030 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
5031 // NumericValueMetricProducer initialized but data for dimension key {} is not present
5032 // in the pulled data..
5033 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5034 vector<std::shared_ptr<LogEvent>>* data) {
5035 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
5036 data->clear();
5037 return true;
5038 }))
5039 // Battery saver mode state changed to ON but data for dimension key {} is not present
5040 // in the pulled data.
5041 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5042 vector<std::shared_ptr<LogEvent>>* data) {
5043 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
5044 data->clear();
5045 return true;
5046 }))
5047 // Dump report pull.
5048 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5049 vector<std::shared_ptr<LogEvent>>* data) {
5050 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
5051 data->clear();
5052 data->push_back(CreateRepeatedValueLogEvent(
5053 tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
5054 return true;
5055 }));
5056
5057 StateManager::getInstance().clear();
5058 sp<NumericValueMetricProducer> valueProducer =
5059 NumericValueMetricProducerTestHelper::createValueProducerWithState(
5060 pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
5061 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
5062
5063 // Set up StateManager and check that StateTrackers are initialized.
5064 StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
5065 valueProducer);
5066 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
5067 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
5068 util::BATTERY_SAVER_MODE_STATE_CHANGED));
5069
5070 // Bucket status after metric initialized.
5071 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
5072 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
5073
5074 // Bucket status after battery saver mode ON event which is not present
5075 // in the pulled data.
5076 unique_ptr<LogEvent> batterySaverOnEvent =
5077 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
5078 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
5079
5080 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
5081 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
5082
5083 // Start dump report and check output.
5084 ProtoOutputStream output;
5085 std::set<string> strSet;
5086 valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
5087 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
5088 &strSet, &output);
5089
5090 StatsLogReport report = outputStreamToProto(&output);
5091 EXPECT_TRUE(report.has_value_metrics());
5092 ASSERT_EQ(0, report.value_metrics().data_size());
5093 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
5094 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5095 }
5096
TEST(NumericValueMetricProducerTest,TestSlicedStateWithNoPullOnBucketBoundary)5097 TEST(NumericValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) {
5098 // Set up NumericValueMetricProducer.
5099 ValueMetric metric =
5100 NumericValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
5101 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
5102 /*
5103 bucket # 1 bucket # 2
5104 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
5105 |------------------------------------|---------------------------|--
5106 x (kStateUnknown)
5107 |-----|
5108 10
5109 x x (ON)
5110 |-----| |-----------|
5111 10 20
5112 x (OFF)
5113 |------------------------|
5114 40
5115 */
5116 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
5117 // NumericValueMetricProducer initialized.
5118 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5119 vector<std::shared_ptr<LogEvent>>* data) {
5120 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
5121 data->clear();
5122 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
5123 return true;
5124 }))
5125 // Battery saver mode state changed to ON.
5126 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5127 vector<std::shared_ptr<LogEvent>>* data) {
5128 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
5129 data->clear();
5130 data->push_back(
5131 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
5132 return true;
5133 }))
5134 // Battery saver mode state changed to OFF.
5135 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5136 vector<std::shared_ptr<LogEvent>>* data) {
5137 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
5138 data->clear();
5139 data->push_back(
5140 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7));
5141 return true;
5142 }))
5143 // Battery saver mode state changed to ON.
5144 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5145 vector<std::shared_ptr<LogEvent>>* data) {
5146 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
5147 data->clear();
5148 data->push_back(CreateRepeatedValueLogEvent(
5149 tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10));
5150 return true;
5151 }))
5152 // Dump report pull.
5153 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5154 vector<std::shared_ptr<LogEvent>>* data) {
5155 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
5156 data->clear();
5157 data->push_back(CreateRepeatedValueLogEvent(
5158 tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15));
5159 return true;
5160 }));
5161
5162 StateManager::getInstance().clear();
5163 sp<NumericValueMetricProducer> valueProducer =
5164 NumericValueMetricProducerTestHelper::createValueProducerWithState(
5165 pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
5166 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
5167
5168 // Set up StateManager and check that StateTrackers are initialized.
5169 StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
5170 valueProducer);
5171 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
5172 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
5173 util::BATTERY_SAVER_MODE_STATE_CHANGED));
5174
5175 // Bucket status after metric initialized.
5176 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
5177 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5178 auto it = valueProducer->mCurrentSlicedBucket.begin();
5179 auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
5180 EXPECT_TRUE(itBase->second.hasCurrentState);
5181 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5182 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
5183 itBase->second.currentState.getValues()[0].mValue.int_value);
5184 // Value for dimension, state key {{}, kStateUnknown}
5185 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5186 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5187 EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
5188 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5189 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
5190
5191 // Bucket status after battery saver mode ON event.
5192 unique_ptr<LogEvent> batterySaverOnEvent =
5193 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
5194 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
5195
5196 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5197 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
5198 it = valueProducer->mCurrentSlicedBucket.begin();
5199 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
5200 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5201 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5202 itBase->second.currentState.getValues()[0].mValue.int_value);
5203 // Value for key {{}, ON}
5204 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5205 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5206 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5207 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5208 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
5209
5210 // Value for key {{}, -1}
5211 it++;
5212 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5213 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5214 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
5215 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5216 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
5217 bucketStartTimeNs + 10 * NS_PER_SEC);
5218
5219 // Bucket status after battery saver mode OFF event.
5220 unique_ptr<LogEvent> batterySaverOffEvent =
5221 CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC);
5222 StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
5223
5224 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5225 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
5226 it = valueProducer->mCurrentSlicedBucket.begin();
5227 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
5228 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5229 EXPECT_EQ(BatterySaverModeStateChanged::OFF,
5230 itBase->second.currentState.getValues()[0].mValue.int_value);
5231 // Value for key {{}, OFF}
5232 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5233 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5234 EXPECT_EQ(BatterySaverModeStateChanged::OFF,
5235 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5236 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
5237
5238 // Value for key {{}, ON}
5239 it++;
5240 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5241 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5242 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5243 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5244 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
5245 bucketStartTimeNs + 20 * NS_PER_SEC);
5246
5247 // Value for key {{}, -1}
5248 it++;
5249 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5250 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5251 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
5252 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5253 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
5254 bucketStartTimeNs + 10 * NS_PER_SEC);
5255
5256 // Bucket status after battery saver mode ON event.
5257 batterySaverOnEvent =
5258 CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
5259 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
5260
5261 // Bucket split. all MetricDimensionKeys other than the current state key are trimmed.
5262 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5263 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
5264 it = valueProducer->mCurrentSlicedBucket.begin();
5265 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
5266 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5267 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5268 itBase->second.currentState.getValues()[0].mValue.int_value);
5269 // Value for key {{}, ON}
5270 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5271 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5272 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5273 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5274 assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
5275
5276 // Start dump report and check output.
5277 ProtoOutputStream output;
5278 std::set<string> strSet;
5279 valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
5280 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
5281 &strSet, &output);
5282 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5283 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
5284
5285 StatsLogReport report = outputStreamToProto(&output);
5286 backfillDimensionPath(&report);
5287 backfillStartEndTimestamp(&report);
5288 EXPECT_TRUE(report.has_value_metrics());
5289 ASSERT_EQ(3, report.value_metrics().data_size());
5290
5291 // {{}, kStateUnknown}
5292 ValueMetricData data = report.value_metrics().data(0);
5293 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
5294 EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
5295 ASSERT_EQ(1, data.bucket_info_size());
5296 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {2},
5297 10 * NS_PER_SEC, -1);
5298
5299 // {{}, ON}
5300 data = report.value_metrics().data(1);
5301 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
5302 EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
5303 ASSERT_EQ(2, data.bucket_info_size());
5304 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {2},
5305 10 * NS_PER_SEC, -1);
5306 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs,
5307 bucket2StartTimeNs + 50 * NS_PER_SEC, {5}, 20 * NS_PER_SEC, -1);
5308
5309 // {{}, OFF}
5310 data = report.value_metrics().data(2);
5311 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
5312 EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
5313 ASSERT_EQ(1, data.bucket_info_size());
5314 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {3},
5315 40 * NS_PER_SEC, -1);
5316 }
5317
5318 /*
5319 * Test slicing condition_true_nanos by state for metric that slices by state when data is not
5320 * present in pulled data during a condition change.
5321 */
TEST(NumericValueMetricProducerTest,TestSlicedStateWithDataMissingInConditionChange)5322 TEST(NumericValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) {
5323 // Set up NumericValueMetricProducer.
5324 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithConditionAndState(
5325 "BATTERY_SAVER_MODE_STATE");
5326 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
5327 /*
5328 NOTE: "-" means that the data was not present in the pulled data.
5329
5330 bucket # 1
5331 10 20 30 40 50 60 (seconds)
5332 |-------------------------------------------------------|--
5333
5334 T F T (Condition)
5335 x x (ON)
5336 |----------------------| - |----|
5337 20 5
5338 x (OFF)
5339 */
5340 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
5341 // Battery saver mode state changed to ON.
5342 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5343 vector<std::shared_ptr<LogEvent>>* data) {
5344 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
5345 data->clear();
5346 data->push_back(
5347 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3));
5348 return true;
5349 }))
5350 // Condition changed to false.
5351 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5352 vector<std::shared_ptr<LogEvent>>* data) {
5353 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
5354 data->clear();
5355 data->push_back(
5356 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
5357 return true;
5358 }))
5359 // Condition changed to true but data for dimension key {} is not present in the
5360 // pulled data.
5361 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5362 vector<std::shared_ptr<LogEvent>>* data) {
5363 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
5364 data->clear();
5365 return true;
5366 }))
5367 // Battery saver mode state changed to ON.
5368 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5369 vector<std::shared_ptr<LogEvent>>* data) {
5370 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
5371 data->clear();
5372 data->push_back(CreateRepeatedValueLogEvent(
5373 tagId, bucketStartTimeNs + 45 * NS_PER_SEC, 14));
5374 return true;
5375 }))
5376 // Dump report pull.
5377 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5378 vector<std::shared_ptr<LogEvent>>* data) {
5379 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
5380 data->clear();
5381 data->push_back(CreateRepeatedValueLogEvent(
5382 tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20));
5383 return true;
5384 }));
5385
5386 StateManager::getInstance().clear();
5387 sp<NumericValueMetricProducer> valueProducer =
5388 NumericValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
5389 pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
5390 ConditionState::kTrue);
5391 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
5392
5393 // Set up StateManager and check that StateTrackers are initialized.
5394 StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
5395 valueProducer);
5396 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
5397 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
5398 util::BATTERY_SAVER_MODE_STATE_CHANGED));
5399
5400 // Bucket status after battery saver mode ON event.
5401 unique_ptr<LogEvent> batterySaverOnEvent =
5402 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
5403 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
5404 // Base for dimension key {}
5405 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5406 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
5407 auto it = valueProducer->mCurrentSlicedBucket.begin();
5408 auto itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
5409 EXPECT_TRUE(itBase->second.hasCurrentState);
5410 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5411 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5412 itBase->second.currentState.getValues()[0].mValue.int_value);
5413 // Value for key {{}, ON}
5414 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5415 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5416 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5417 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5418 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
5419
5420 // Value for key {{}, -1}
5421 it++;
5422 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5423 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5424 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
5425 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5426 assertConditionTimer(it->second.conditionTimer, false, 0, 0);
5427
5428 // Bucket status after condition change to false.
5429 valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC);
5430 // Base for dimension key {}
5431 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5432 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
5433 it = valueProducer->mCurrentSlicedBucket.begin();
5434 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
5435 EXPECT_TRUE(itBase->second.hasCurrentState);
5436 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5437 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5438 itBase->second.currentState.getValues()[0].mValue.int_value);
5439 // Value for key {{}, ON}
5440 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5441 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5442 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5443 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5444 assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
5445 bucketStartTimeNs + 30 * NS_PER_SEC);
5446
5447 // Value for key {{}, -1}
5448 it++;
5449 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5450 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5451 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
5452 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5453 assertConditionTimer(it->second.conditionTimer, false, 0, 0);
5454
5455 unique_ptr<LogEvent> batterySaverOffEvent =
5456 CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 35 * NS_PER_SEC);
5457 StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
5458 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5459 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
5460
5461 // Bucket status after condition change to true.
5462 valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
5463 // Base for dimension key {}. The pull returned no data, so mDimInfos is trimmed.
5464 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
5465 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
5466 it = valueProducer->mCurrentSlicedBucket.begin();
5467 // Value for key {{}, ON}
5468 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5469 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5470 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5471 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5472 assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
5473 bucketStartTimeNs + 30 * NS_PER_SEC);
5474
5475 // Value for key {{}, -1}
5476 it++;
5477 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5478 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5479 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
5480 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5481 assertConditionTimer(it->second.conditionTimer, false, 0, 0);
5482
5483 batterySaverOnEvent =
5484 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 45 * NS_PER_SEC);
5485 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
5486 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5487 itBase = valueProducer->mDimInfos.find(it->first.getDimensionKeyInWhat());
5488 EXPECT_TRUE(itBase->second.hasCurrentState);
5489 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5490 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5491 itBase->second.currentState.getValues()[0].mValue.int_value);
5492 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
5493
5494 // Start dump report and check output.
5495 ProtoOutputStream output;
5496 std::set<string> strSet;
5497 valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
5498 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
5499 &strSet, &output);
5500 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5501 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
5502
5503 StatsLogReport report = outputStreamToProto(&output);
5504 backfillDimensionPath(&report);
5505 backfillStartEndTimestamp(&report);
5506 EXPECT_TRUE(report.has_value_metrics());
5507 ASSERT_EQ(1, report.value_metrics().data_size());
5508
5509 // {{}, ON}
5510 ValueMetricData data = report.value_metrics().data(0);
5511 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
5512 EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
5513 ASSERT_EQ(1, data.bucket_info_size());
5514 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC,
5515 {2 + 6}, 25 * NS_PER_SEC, -1);
5516 }
5517
5518 /*
5519 * Test slicing condition_true_nanos by state for metric that slices by state with a primary field,
5520 * condition, and has multiple dimensions.
5521 */
TEST(NumericValueMetricProducerTest,TestSlicedStateWithMultipleDimensions)5522 TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
5523 // Set up NumericValueMetricProducer.
5524 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithConditionAndState(
5525 "UID_PROCESS_STATE");
5526 metric.mutable_dimensions_in_what()->set_field(tagId);
5527 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
5528 metric.mutable_dimensions_in_what()->add_child()->set_field(3);
5529
5530 MetricStateLink* stateLink = metric.add_state_link();
5531 stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
5532 auto fieldsInWhat = stateLink->mutable_fields_in_what();
5533 *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
5534 auto fieldsInState = stateLink->mutable_fields_in_state();
5535 *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
5536
5537 /*
5538 bucket # 1 bucket # 2
5539 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
5540 |------------------------------------------|---------------------------------|--
5541
5542 T F T (Condition)
5543 (FOREGROUND)
5544 x {1, 14}
5545 |------|
5546 10
5547
5548 x {1, 16}
5549 |------|
5550 10
5551 x {2, 8}
5552 |-------------|
5553 20
5554
5555 (BACKGROUND)
5556 x {1, 14}
5557 |-------------| |----------|---------------------------------|
5558 20 15 50
5559
5560 x {1, 16}
5561 |-------------| |----------|---------------------------------|
5562 20 15 50
5563
5564 x {2, 8}
5565 |----------| |----------|-------------------|
5566 15 15 30
5567 */
5568 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
5569 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
5570 // Uid 1 process state change from kStateUnknown -> Foreground
5571 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5572 vector<std::shared_ptr<LogEvent>>* data) {
5573 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
5574 data->clear();
5575 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
5576 1 /*uid*/, 3, 14 /*tag*/));
5577 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
5578 1 /*uid*/, 3, 16 /*tag*/));
5579
5580 // This event should be skipped.
5581 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
5582 2 /*uid*/, 5, 8 /*tag*/));
5583 return true;
5584 }))
5585 // Uid 1 process state change from Foreground -> Background
5586 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5587 vector<std::shared_ptr<LogEvent>>* data) {
5588 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
5589 data->clear();
5590 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
5591 1 /*uid*/, 5, 14 /*tag*/));
5592 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
5593 1 /*uid*/, 5, 16 /*tag*/));
5594
5595 // This event should be skipped.
5596 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
5597 2 /*uid*/, 7, 8 /*tag*/));
5598
5599 return true;
5600 }))
5601 // Uid 2 process state change from kStateUnknown -> Background
5602 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5603 vector<std::shared_ptr<LogEvent>>* data) {
5604 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
5605 data->clear();
5606 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
5607 2 /*uid*/, 9, 8 /*tag*/));
5608
5609 // This event should be skipped.
5610 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
5611 1 /*uid*/, 9, 14 /* tag */));
5612
5613 // This event should be skipped.
5614 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
5615 1 /*uid*/, 9, 16 /* tag */));
5616
5617 return true;
5618 }))
5619 // Condition changed to false.
5620 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5621 vector<std::shared_ptr<LogEvent>>* data) {
5622 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
5623 data->clear();
5624 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
5625 1 /*uid*/, 11, 14 /* tag */));
5626 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
5627 1 /*uid*/, 11, 16 /* tag */));
5628 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
5629 2 /*uid*/, 11, 8 /*tag*/));
5630
5631 return true;
5632 }))
5633 // Condition changed to true.
5634 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5635 vector<std::shared_ptr<LogEvent>>* data) {
5636 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
5637 data->clear();
5638 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
5639 1 /*uid*/, 13, 14 /* tag */));
5640 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
5641 1 /*uid*/, 13, 16 /* tag */));
5642 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
5643 2 /*uid*/, 13, 8 /*tag*/));
5644 return true;
5645 }))
5646 // Uid 2 process state change from Background -> Foreground
5647 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5648 vector<std::shared_ptr<LogEvent>>* data) {
5649 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
5650 data->clear();
5651 data->push_back(CreateThreeValueLogEvent(
5652 tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
5653
5654 // This event should be skipped.
5655 data->push_back(CreateThreeValueLogEvent(
5656 tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
5657 // This event should be skipped.
5658 data->push_back(CreateThreeValueLogEvent(
5659 tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
5660
5661 return true;
5662 }))
5663 // Dump report pull.
5664 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5665 vector<std::shared_ptr<LogEvent>>* data) {
5666 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
5667 data->clear();
5668 data->push_back(CreateThreeValueLogEvent(
5669 tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */));
5670 data->push_back(CreateThreeValueLogEvent(
5671 tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */));
5672 data->push_back(CreateThreeValueLogEvent(
5673 tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/));
5674 return true;
5675 }));
5676
5677 StateManager::getInstance().clear();
5678 sp<NumericValueMetricProducer> valueProducer =
5679 NumericValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
5680 pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue);
5681 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
5682
5683 // Set up StateManager and check that StateTrackers are initialized.
5684 StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
5685 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
5686 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
5687
5688 // Condition is true.
5689 auto uidProcessEvent =
5690 CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */,
5691 android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
5692 StateManager::getInstance().onLogEvent(*uidProcessEvent);
5693 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
5694 ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
5695
5696 uidProcessEvent =
5697 CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
5698 android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
5699 StateManager::getInstance().onLogEvent(*uidProcessEvent);
5700 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
5701 ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size());
5702
5703 uidProcessEvent =
5704 CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
5705 android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
5706 StateManager::getInstance().onLogEvent(*uidProcessEvent);
5707 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
5708 ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
5709
5710 valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
5711 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
5712 ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
5713
5714 valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC);
5715 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
5716 ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
5717
5718 // Pull at end of first bucket.
5719 vector<shared_ptr<LogEvent>> allData;
5720 allData.push_back(
5721 CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
5722 allData.push_back(
5723 CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
5724 allData.push_back(
5725 CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
5726 valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
5727
5728 // Buckets flushed. MetricDimensionKeys not corresponding to the current state are removed.
5729 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
5730 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
5731 ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
5732
5733 uidProcessEvent =
5734 CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
5735 android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
5736 StateManager::getInstance().onLogEvent(*uidProcessEvent);
5737 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
5738 ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
5739
5740 // Start dump report and check output.
5741 ProtoOutputStream output;
5742 std::set<string> strSet;
5743 valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
5744 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
5745 &strSet, &output);
5746 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
5747 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
5748
5749 StatsLogReport report = outputStreamToProto(&output);
5750 backfillDimensionPath(&report);
5751 backfillStartEndTimestamp(&report);
5752 EXPECT_TRUE(report.has_value_metrics());
5753 StatsLogReport::ValueMetricDataWrapper valueMetrics;
5754 sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
5755 ASSERT_EQ(6, valueMetrics.data_size());
5756 ASSERT_EQ(0, report.value_metrics().skipped_size());
5757
5758 // {{uid 1, tag 14}, FOREGROUND}.
5759 ValueMetricData data = valueMetrics.data(0);
5760 EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
5761 EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
5762 data.slice_by_state(0).value());
5763 ASSERT_EQ(1, data.bucket_info_size());
5764 EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
5765
5766 // {{uid 1, tag 16}, BACKGROUND}.
5767 data = valueMetrics.data(1);
5768 EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
5769 EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
5770 data.slice_by_state(0).value());
5771 ASSERT_EQ(2, data.bucket_info_size());
5772 EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
5773 EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
5774
5775 // {{uid 1, tag 16}, FOREGROUND}.
5776 data = valueMetrics.data(2);
5777 EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
5778 EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
5779 data.slice_by_state(0).value());
5780 ASSERT_EQ(1, data.bucket_info_size());
5781 EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
5782
5783 // {{uid 1, tag 14}, BACKGROUND}.
5784 data = valueMetrics.data(3);
5785 EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
5786 EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
5787 data.slice_by_state(0).value());
5788 ASSERT_EQ(2, data.bucket_info_size());
5789 EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
5790 EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
5791
5792 // {{uid 2, tag 8}, FOREGROUND}.
5793 data = valueMetrics.data(4);
5794 EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
5795 EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
5796 data.slice_by_state(0).value());
5797 ASSERT_EQ(1, data.bucket_info_size());
5798 EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
5799
5800 // {{uid 2, tag 8}, BACKGROUND}.
5801 data = valueMetrics.data(5);
5802 EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
5803 EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
5804 data.slice_by_state(0).value());
5805 ASSERT_EQ(2, data.bucket_info_size());
5806 EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
5807 EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
5808 }
5809
TEST(NumericValueMetricProducerTest,TestSlicedStateWithCondition)5810 TEST(NumericValueMetricProducerTest, TestSlicedStateWithCondition) {
5811 // Set up NumericValueMetricProducer.
5812 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithConditionAndState(
5813 "BATTERY_SAVER_MODE_STATE");
5814 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
5815 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
5816 // Condition changed to true.
5817 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5818 vector<std::shared_ptr<LogEvent>>* data) {
5819 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
5820 data->clear();
5821 data->push_back(
5822 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3));
5823 return true;
5824 }))
5825 // Battery saver mode state changed to OFF.
5826 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5827 vector<std::shared_ptr<LogEvent>>* data) {
5828 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
5829 data->clear();
5830 data->push_back(
5831 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
5832 return true;
5833 }))
5834 // Condition changed to false.
5835 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
5836 vector<std::shared_ptr<LogEvent>>* data) {
5837 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
5838 data->clear();
5839 data->push_back(CreateRepeatedValueLogEvent(
5840 tagId, bucket2StartTimeNs + 10 * NS_PER_SEC, 15));
5841 return true;
5842 }));
5843
5844 StateManager::getInstance().clear();
5845 sp<NumericValueMetricProducer> valueProducer =
5846 NumericValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
5847 pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
5848 ConditionState::kFalse);
5849 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
5850
5851 // Set up StateManager and check that StateTrackers are initialized.
5852 StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
5853 valueProducer);
5854 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
5855 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
5856 util::BATTERY_SAVER_MODE_STATE_CHANGED));
5857
5858 // Bucket status after battery saver mode ON event.
5859 // Condition is false so we do nothing.
5860 unique_ptr<LogEvent> batterySaverOnEvent =
5861 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
5862 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
5863 EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
5864 EXPECT_EQ(0UL, valueProducer->mDimInfos.size());
5865
5866 // Bucket status after condition change to true.
5867 valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
5868 // Base for dimension key {}
5869 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5870 std::unordered_map<HashableDimensionKey,
5871 NumericValueMetricProducer::DimensionsInWhatInfo>::iterator itBase =
5872 valueProducer->mDimInfos.find(DEFAULT_DIMENSION_KEY);
5873 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
5874 EXPECT_EQ(3, itBase->second.dimExtras[0].value().long_value);
5875 EXPECT_TRUE(itBase->second.hasCurrentState);
5876 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5877 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5878 itBase->second.currentState.getValues()[0].mValue.int_value);
5879 // Value for key {{}, ON}
5880 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
5881 std::unordered_map<MetricDimensionKey, NumericValueMetricProducer::CurrentBucket>::iterator it =
5882 valueProducer->mCurrentSlicedBucket.begin();
5883 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5884 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5885 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5886 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5887 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
5888 // Value for key {{}, -1}
5889 it++;
5890 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5891 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5892 EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
5893 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5894 EXPECT_EQ(0, it->second.intervals[0].sampleSize);
5895 assertConditionTimer(it->second.conditionTimer, false, 0, 0);
5896
5897 // Bucket status after battery saver mode OFF event.
5898 unique_ptr<LogEvent> batterySaverOffEvent =
5899 CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
5900 StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
5901 // Base for dimension key {}
5902 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5903 itBase = valueProducer->mDimInfos.find(DEFAULT_DIMENSION_KEY);
5904 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
5905 EXPECT_EQ(5, itBase->second.dimExtras[0].value().long_value);
5906 EXPECT_TRUE(itBase->second.hasCurrentState);
5907 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5908 EXPECT_EQ(BatterySaverModeStateChanged::OFF,
5909 itBase->second.currentState.getValues()[0].mValue.int_value);
5910 // Value for key {{}, OFF}
5911 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
5912 it = valueProducer->mCurrentSlicedBucket.begin();
5913 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5914 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5915 EXPECT_EQ(BatterySaverModeStateChanged::OFF,
5916 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5917 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
5918 // Value for key {{}, ON}
5919 it++;
5920 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5921 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5922 EXPECT_EQ(BatterySaverModeStateChanged::ON,
5923 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5924 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
5925 EXPECT_EQ(2, it->second.intervals[0].aggregate.long_value);
5926 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
5927 bucketStartTimeNs + 30 * NS_PER_SEC);
5928 // Value for key {{}, -1}
5929 it++;
5930 assertConditionTimer(it->second.conditionTimer, false, 0, 0);
5931
5932 // Pull at end of first bucket.
5933 vector<shared_ptr<LogEvent>> allData;
5934 allData.clear();
5935 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11));
5936 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
5937
5938 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
5939 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
5940 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5941 // Base for dimension key {}
5942 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5943 itBase = valueProducer->mDimInfos.find(DEFAULT_DIMENSION_KEY);
5944 EXPECT_TRUE(itBase->second.dimExtras[0].has_value());
5945 EXPECT_EQ(11, itBase->second.dimExtras[0].value().long_value);
5946 EXPECT_TRUE(itBase->second.hasCurrentState);
5947 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5948 EXPECT_EQ(BatterySaverModeStateChanged::OFF,
5949 itBase->second.currentState.getValues()[0].mValue.int_value);
5950 // Value for key {{}, OFF}
5951 it = valueProducer->mCurrentSlicedBucket.begin();
5952 assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
5953
5954 // Bucket 2 status after condition change to false.
5955 valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
5956 // Base for dimension key {}
5957 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
5958 itBase = valueProducer->mDimInfos.find(DEFAULT_DIMENSION_KEY);
5959 EXPECT_FALSE(itBase->second.dimExtras[0].has_value());
5960 EXPECT_TRUE(itBase->second.hasCurrentState);
5961 ASSERT_EQ(1, itBase->second.currentState.getValues().size());
5962 EXPECT_EQ(BatterySaverModeStateChanged::OFF,
5963 itBase->second.currentState.getValues()[0].mValue.int_value);
5964 // Value for key {{}, OFF}
5965 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
5966 it = valueProducer->mCurrentSlicedBucket.begin();
5967 EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
5968 ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
5969 EXPECT_EQ(BatterySaverModeStateChanged::OFF,
5970 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
5971 EXPECT_GT(it->second.intervals[0].sampleSize, 0);
5972 EXPECT_EQ(4, it->second.intervals[0].aggregate.long_value);
5973 assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
5974 bucket2StartTimeNs + 10 * NS_PER_SEC);
5975
5976 // Start dump report and check output.
5977 ProtoOutputStream output;
5978 std::set<string> strSet;
5979 valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
5980 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
5981 &strSet, &output);
5982
5983 StatsLogReport report = outputStreamToProto(&output);
5984 EXPECT_TRUE(report.has_value_metrics());
5985 ASSERT_EQ(2, report.value_metrics().data_size());
5986
5987 ValueMetricData data = report.value_metrics().data(0);
5988 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
5989 EXPECT_TRUE(data.slice_by_state(0).has_value());
5990 EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
5991 ASSERT_EQ(1, data.bucket_info_size());
5992 EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
5993 EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
5994
5995 data = report.value_metrics().data(1);
5996 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
5997 EXPECT_TRUE(data.slice_by_state(0).has_value());
5998 EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
5999 ASSERT_EQ(2, data.bucket_info_size());
6000 EXPECT_EQ(6, data.bucket_info(0).values(0).value_long());
6001 EXPECT_EQ(4, data.bucket_info(1).values(0).value_long());
6002 EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
6003 EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
6004 }
6005
TEST(NumericValueMetricProducerTest,TestSlicedStateWithConditionFalseMultipleBuckets)6006 TEST(NumericValueMetricProducerTest, TestSlicedStateWithConditionFalseMultipleBuckets) {
6007 // Set up NumericValueMetricProducer.
6008 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithConditionAndState(
6009 "BATTERY_SAVER_MODE_STATE");
6010 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6011 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6012 // Condition changed to true.
6013 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6014 vector<std::shared_ptr<LogEvent>>* data) {
6015 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
6016 data->clear();
6017 data->push_back(
6018 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 3));
6019 return true;
6020 }))
6021 // Battery saver mode state changed to OFF.
6022 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6023 vector<std::shared_ptr<LogEvent>>* data) {
6024 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
6025 data->clear();
6026 data->push_back(
6027 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
6028 return true;
6029 }))
6030 // Condition changed to false.
6031 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6032 vector<std::shared_ptr<LogEvent>>* data) {
6033 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
6034 data->clear();
6035 data->push_back(
6036 CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 9));
6037 return true;
6038 }))
6039 // Condition changed to true.
6040 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6041 vector<std::shared_ptr<LogEvent>>* data) {
6042 EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10 * NS_PER_SEC);
6043 data->clear();
6044 data->push_back(CreateRepeatedValueLogEvent(
6045 tagId, bucket3StartTimeNs + 10 * NS_PER_SEC, 35));
6046 return true;
6047 }))
6048 // Dump report pull.
6049 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6050 vector<std::shared_ptr<LogEvent>>* data) {
6051 EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 30 * NS_PER_SEC);
6052 data->clear();
6053 data->push_back(CreateRepeatedValueLogEvent(
6054 tagId, bucket3StartTimeNs + 30 * NS_PER_SEC, 53));
6055 return true;
6056 }));
6057
6058 StateManager::getInstance().clear();
6059 sp<NumericValueMetricProducer> valueProducer =
6060 NumericValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
6061 pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
6062 ConditionState::kFalse);
6063 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
6064
6065 // Set up StateManager and check that StateTrackers are initialized.
6066 StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
6067 valueProducer);
6068 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
6069 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
6070 util::BATTERY_SAVER_MODE_STATE_CHANGED));
6071
6072 // Bucket status after battery saver mode ON event.
6073 // Condition is false so we do nothing.
6074 unique_ptr<LogEvent> batterySaverOnEvent =
6075 CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
6076 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
6077 EXPECT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
6078 EXPECT_EQ(0UL, valueProducer->mDimInfos.size());
6079
6080 // Bucket status after condition change to true.
6081 valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
6082 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
6083 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
6084
6085 // Bucket status after battery saver mode OFF event.
6086 unique_ptr<LogEvent> batterySaverOffEvent =
6087 CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
6088 StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
6089 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
6090 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
6091
6092 // Bucket status after condition change to false.
6093 valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
6094 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
6095 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
6096
6097 // Pull at end of first bucket.
6098 vector<shared_ptr<LogEvent>> allData;
6099 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 11));
6100 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
6101 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
6102 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
6103 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
6104
6105 // Battery saver mode ON event. Nothing change since the condition is false.
6106 batterySaverOnEvent =
6107 CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
6108 StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
6109
6110 // Pull at end of second bucket. Since no new data is seen, mDimInfos will be cleared.
6111 allData.clear();
6112 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15));
6113 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
6114 ASSERT_EQ(2UL, valueProducer->mPastBuckets.size());
6115 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
6116 ASSERT_EQ(0UL, valueProducer->mDimInfos.size());
6117
6118 // Bucket2 status after condition change to true.
6119 valueProducer->onConditionChanged(true, bucket3StartTimeNs + 10 * NS_PER_SEC);
6120 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
6121 // This currently keys into the old state key, which is unknown since mDimInfos was cleared.
6122 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
6123
6124 // Start dump report and check output.
6125 ProtoOutputStream output;
6126 std::set<string> strSet;
6127 valueProducer->onDumpReport(bucket3StartTimeNs + 30 * NS_PER_SEC,
6128 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
6129 &strSet, &output);
6130
6131 StatsLogReport report = outputStreamToProto(&output);
6132 backfillDimensionPath(&report);
6133 backfillStartEndTimestamp(&report);
6134 EXPECT_TRUE(report.has_value_metrics());
6135 ASSERT_EQ(2, report.value_metrics().data_size());
6136
6137 ValueMetricData data = report.value_metrics().data(0);
6138 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
6139 EXPECT_TRUE(data.slice_by_state(0).has_value());
6140 EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
6141 ASSERT_EQ(2, data.bucket_info_size());
6142 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {2},
6143 10 * NS_PER_SEC, -1);
6144 ValidateValueBucket(data.bucket_info(1), bucket3StartTimeNs,
6145 bucket3StartTimeNs + 30 * NS_PER_SEC, {18}, 20 * NS_PER_SEC, -1);
6146
6147 data = report.value_metrics().data(1);
6148 EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
6149 EXPECT_TRUE(data.slice_by_state(0).has_value());
6150 EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
6151 ASSERT_EQ(1, data.bucket_info_size());
6152 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {4},
6153 10 * NS_PER_SEC, -1);
6154 }
6155
6156 /*
6157 * Test slicing by state for metric that slices by state with a primary field,
6158 * has multiple dimensions, and a pull that returns incomplete data.
6159 */
TEST(NumericValueMetricProducerTest,TestSlicedStateWithMultipleDimensionsMissingDataInPull)6160 TEST(NumericValueMetricProducerTest, TestSlicedStateWithMultipleDimensionsMissingDataInPull) {
6161 // Set up NumericValueMetricProducer.
6162 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithConditionAndState(
6163 "UID_PROCESS_STATE");
6164 metric.mutable_dimensions_in_what()->set_field(tagId);
6165 metric.mutable_dimensions_in_what()->add_child()->set_field(1);
6166 metric.mutable_dimensions_in_what()->add_child()->set_field(3);
6167
6168 MetricStateLink* stateLink = metric.add_state_link();
6169 stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
6170 auto fieldsInWhat = stateLink->mutable_fields_in_what();
6171 *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
6172 auto fieldsInState = stateLink->mutable_fields_in_state();
6173 *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
6174 /*
6175 bucket # 1 bucket # 2
6176 10 20 30 40 50 60 70 80 90 100 110 120 (seconds)
6177 |------------------------------------------|---------------------------------|--
6178 (kUnknown)
6179 x {1, 14}
6180 |-------------|
6181 20
6182 x - {1, 16}
6183 |-------------|
6184 20
6185 x {2, 8}
6186 |-----------------|
6187 25
6188 {FOREGROUND}
6189 x {2, 8}
6190 |-------------|
6191 20
6192 (BACKGROUND)
6193 x {1, 14}
6194 |----------------------------|---------------------------------|
6195 40 50
6196 - {1, 16}
6197 |---------------------------------|
6198 50
6199 x - {2, 8}
6200 |-------------------------|
6201 45
6202 */
6203 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6204 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6205 // Initial Pull
6206 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6207 vector<std::shared_ptr<LogEvent>>* data) {
6208 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
6209 data->clear();
6210 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 1,
6211 14 /*tag*/));
6212 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, 1 /*uid*/, 1,
6213 16 /*tag*/));
6214 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 1,
6215 8 /*tag*/));
6216 return true;
6217 }))
6218 // Uid 1 process state change from kStateUnknown -> Background. Tag 16 is missing.
6219 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6220 vector<std::shared_ptr<LogEvent>>* data) {
6221 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
6222 data->clear();
6223 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
6224 1 /*uid*/, 5, 14 /*tag*/));
6225 // This event should be skipped.
6226 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
6227 2 /*uid*/, 7, 8 /*tag*/));
6228 return true;
6229 }))
6230 // Uid 2 process state change from kStateUnknown -> Background
6231 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6232 vector<std::shared_ptr<LogEvent>>* data) {
6233 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
6234 data->clear();
6235 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
6236 2 /*uid*/, 8, 8 /*tag*/));
6237 // This event should be skipped.
6238 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
6239 1 /*uid*/, 8, 14 /* tag */));
6240 // This event should be skipped.
6241 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
6242 1 /*uid*/, 8, 16 /* tag */));
6243 return true;
6244 }))
6245 // Uid 2 process state change from Background -> Foreground
6246 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6247 vector<std::shared_ptr<LogEvent>>* data) {
6248 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
6249 data->clear();
6250 data->push_back(CreateThreeValueLogEvent(
6251 tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
6252 // This event should be skipped.
6253 data->push_back(CreateThreeValueLogEvent(
6254 tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
6255 // This event should be skipped.
6256 data->push_back(CreateThreeValueLogEvent(
6257 tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
6258 return true;
6259 }))
6260 // Dump report pull.
6261 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6262 vector<std::shared_ptr<LogEvent>>* data) {
6263 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
6264 data->clear();
6265 data->push_back(CreateThreeValueLogEvent(
6266 tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 22, 14 /* tag */));
6267 data->push_back(CreateThreeValueLogEvent(
6268 tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 22, 16 /* tag */));
6269 data->push_back(CreateThreeValueLogEvent(
6270 tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 22, 8 /*tag*/));
6271 return true;
6272 }));
6273
6274 StateManager::getInstance().clear();
6275 sp<NumericValueMetricProducer> valueProducer =
6276 NumericValueMetricProducerTestHelper::createValueProducerWithState(
6277 pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {});
6278 EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
6279
6280 // Set up StateManager and check that StateTrackers are initialized.
6281 StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
6282 EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
6283 EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
6284
6285 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
6286 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
6287
6288 // Tag 16 is missing and gets trimmed from mDimInfos
6289 auto uidProcessEvent =
6290 CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
6291 android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
6292 StateManager::getInstance().onLogEvent(*uidProcessEvent);
6293 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
6294 ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
6295
6296 uidProcessEvent =
6297 CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
6298 android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
6299 StateManager::getInstance().onLogEvent(*uidProcessEvent);
6300 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
6301 ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
6302
6303 // Pull at end of first bucket. Uid 2 is missing and gets trimmed from mDimInfos
6304 vector<shared_ptr<LogEvent>> allData;
6305 allData.push_back(
6306 CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
6307 allData.push_back(
6308 CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
6309 valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
6310
6311 // Buckets flushed. MetricDimensionKeys not corresponding to the current state are removed.
6312 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
6313 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
6314 // {1, 16, kUnknown}, {2, 8, BACKGROUND} aren't present since the pulls were missing the dims.
6315 ASSERT_EQ(3UL, valueProducer->mPastBuckets.size());
6316
6317 uidProcessEvent =
6318 CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
6319 android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
6320 StateManager::getInstance().onLogEvent(*uidProcessEvent);
6321 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
6322 ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
6323
6324 // Start dump report and check output.
6325 ProtoOutputStream output;
6326 std::set<string> strSet;
6327 valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
6328 true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
6329 &strSet, &output);
6330 ASSERT_EQ(3UL, valueProducer->mDimInfos.size());
6331 ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
6332
6333 StatsLogReport report = outputStreamToProto(&output);
6334 backfillDimensionPath(&report);
6335 backfillStartEndTimestamp(&report);
6336 EXPECT_TRUE(report.has_value_metrics());
6337 StatsLogReport::ValueMetricDataWrapper valueMetrics;
6338 sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
6339
6340 // {1, 16, kUnknown}, {2, 8, BACKGROUND} aren't present since the pulls were missing the dims.
6341 ASSERT_EQ(5, valueMetrics.data_size());
6342 ASSERT_EQ(0, report.value_metrics().skipped_size());
6343
6344 // {{uid 1, tag 14}, kStateUnknown}.
6345 ValueMetricData data = valueMetrics.data(0);
6346 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
6347 -1 /*StateTracker::kStateUnknown*/);
6348 EXPECT_EQ(data.dimensions_in_what().field(), tagId);
6349 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
6350 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
6351 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 1);
6352 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
6353 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 14);
6354 ASSERT_EQ(1, data.bucket_info_size());
6355 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {4},
6356 20 * NS_PER_SEC, -1);
6357
6358 // {{uid 1, tag 14}, BACKGROUND}.
6359 data = valueMetrics.data(1);
6360 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
6361 android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
6362 EXPECT_EQ(data.dimensions_in_what().field(), tagId);
6363 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
6364 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
6365 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 1);
6366 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
6367 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 14);
6368 ASSERT_EQ(2, data.bucket_info_size());
6369 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {8},
6370 40 * NS_PER_SEC, -1);
6371 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs,
6372 bucket2StartTimeNs + 50 * NS_PER_SEC, {9}, 50 * NS_PER_SEC, -1);
6373
6374 // {{uid 1, tag 16}, BACKGROUND}.
6375 data = valueMetrics.data(2);
6376 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
6377 android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
6378 EXPECT_EQ(data.dimensions_in_what().field(), tagId);
6379 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
6380 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
6381 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 1);
6382 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
6383 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 16);
6384 ASSERT_EQ(1, data.bucket_info_size());
6385 ValidateValueBucket(data.bucket_info(0), bucket2StartTimeNs,
6386 bucket2StartTimeNs + 50 * NS_PER_SEC, {9}, 50 * NS_PER_SEC, -1);
6387
6388 // {{uid 2, tag 8}, kStateUnknown}.
6389 data = valueMetrics.data(3);
6390 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
6391 -1 /*StateTracker::kStateUnknown*/);
6392 EXPECT_EQ(data.dimensions_in_what().field(), tagId);
6393 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
6394 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
6395 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 2);
6396 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
6397 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 8);
6398 ASSERT_EQ(1, data.bucket_info_size());
6399 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {7},
6400 25 * NS_PER_SEC, -1);
6401
6402 // {{uid 2, tag 8}, FOREGROUND}.
6403 data = valueMetrics.data(4);
6404 ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
6405 android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
6406 EXPECT_EQ(data.dimensions_in_what().field(), tagId);
6407 ASSERT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 2);
6408 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
6409 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 2);
6410 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).field(), 3);
6411 EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(1).value_int(), 8);
6412 ASSERT_EQ(1, data.bucket_info_size());
6413 ValidateValueBucket(data.bucket_info(0), bucket2StartTimeNs,
6414 bucket2StartTimeNs + 50 * NS_PER_SEC, {4}, 20 * NS_PER_SEC, -1);
6415 }
6416
6417 /*
6418 * Test bucket splits when condition is unknown.
6419 */
TEST(NumericValueMetricProducerTest,TestForcedBucketSplitWhenConditionUnknownSkipsBucket)6420 TEST(NumericValueMetricProducerTest, TestForcedBucketSplitWhenConditionUnknownSkipsBucket) {
6421 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
6422
6423 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6424
6425 sp<NumericValueMetricProducer> valueProducer =
6426 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
6427 pullerManager, metric, ConditionState::kUnknown);
6428
6429 // App update event.
6430 int64_t appUpdateTimeNs = bucketStartTimeNs + 1000;
6431 valueProducer->notifyAppUpgrade(appUpdateTimeNs);
6432
6433 // Check dump report.
6434 ProtoOutputStream output;
6435 std::set<string> strSet;
6436 int64_t dumpReportTimeNs = bucketStartTimeNs + 10000000000; // 10 seconds
6437 valueProducer->onDumpReport(dumpReportTimeNs, false /* include current buckets */, true,
6438 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
6439
6440 StatsLogReport report = outputStreamToProto(&output);
6441 EXPECT_TRUE(report.has_value_metrics());
6442 ASSERT_EQ(0, report.value_metrics().data_size());
6443 ASSERT_EQ(1, report.value_metrics().skipped_size());
6444
6445 EXPECT_EQ(NanoToMillis(bucketStartTimeNs),
6446 report.value_metrics().skipped(0).start_bucket_elapsed_millis());
6447 EXPECT_EQ(NanoToMillis(appUpdateTimeNs),
6448 report.value_metrics().skipped(0).end_bucket_elapsed_millis());
6449 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
6450
6451 auto dropEvent = report.value_metrics().skipped(0).drop_event(0);
6452 EXPECT_EQ(BucketDropReason::CONDITION_UNKNOWN, dropEvent.drop_reason());
6453 EXPECT_EQ(NanoToMillis(appUpdateTimeNs), dropEvent.drop_time_millis());
6454 }
6455
TEST(NumericValueMetricProducerTest,TestUploadThreshold)6456 TEST(NumericValueMetricProducerTest, TestUploadThreshold) {
6457 // Create metric with upload threshold and two value fields.
6458 int64_t thresholdValue = 15;
6459 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
6460 metric.mutable_value_field()->add_child()->set_field(3);
6461 metric.mutable_threshold()->set_gt_int(thresholdValue);
6462 *metric.mutable_dimensions_in_what() = CreateDimensions(tagId, {1 /*uid*/});
6463
6464 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6465
6466 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6467 // First bucket pull.
6468 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6469 vector<std::shared_ptr<LogEvent>>* data) {
6470 data->clear();
6471 data->push_back(
6472 CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 1, 1 /*uid*/, 5, 5));
6473 data->push_back(
6474 CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 1, 2 /*uid*/, 5, 5));
6475 return true;
6476 }))
6477 // Dump report.
6478 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6479 vector<std::shared_ptr<LogEvent>>* data) {
6480 data->clear();
6481 data->push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 10000000000,
6482 1 /*uid*/, 22, 21));
6483 data->push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 10000000000,
6484 2 /*uid*/, 30, 10));
6485 return true;
6486 }));
6487
6488 sp<NumericValueMetricProducer> valueProducer =
6489 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
6490 metric);
6491
6492 // Bucket 2 start.
6493 vector<shared_ptr<LogEvent>> allData;
6494 allData.clear();
6495 allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 1 /*uid*/, 21, 21));
6496 allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 20, 5));
6497 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
6498
6499 // Check dump report.
6500 ProtoOutputStream output;
6501 std::set<string> strSet;
6502 int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000;
6503 valueProducer->onDumpReport(dumpReportTimeNs, true /* include current buckets */, true,
6504 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
6505
6506 StatsLogReport report = outputStreamToProto(&output);
6507 backfillDimensionPath(&report);
6508 backfillStartEndTimestamp(&report);
6509 EXPECT_TRUE(report.has_value_metrics());
6510 StatsLogReport::ValueMetricDataWrapper valueMetrics;
6511 sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
6512 ASSERT_EQ(1, valueMetrics.data_size());
6513 ASSERT_EQ(1, report.value_metrics().skipped_size());
6514
6515 // Check data keyed to uid 1.
6516 ValueMetricData data = valueMetrics.data(0);
6517 ValidateUidDimension(data.dimensions_in_what(), tagId, 1);
6518 ASSERT_EQ(1, data.bucket_info_size());
6519 // First bucket.
6520 // Values pass threshold.
6521 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {16, 16}, -1,
6522 0);
6523 // Second bucket is dropped because values do not pass threshold.
6524
6525 // Check data keyed to uid 2.
6526 // First bucket and second bucket are dropped because values do not pass threshold.
6527
6528 // Check that second bucket has NO_DATA drop reason.
6529 EXPECT_EQ(bucket2StartTimeNs, report.value_metrics().skipped(0).start_bucket_elapsed_nanos());
6530 EXPECT_EQ(dumpReportTimeNs, report.value_metrics().skipped(0).end_bucket_elapsed_nanos());
6531 ASSERT_EQ(1, report.value_metrics().skipped(0).drop_event_size());
6532 EXPECT_EQ(BucketDropReason::NO_DATA,
6533 report.value_metrics().skipped(0).drop_event(0).drop_reason());
6534 }
6535
6536 /**
6537 * Tests pulled atoms with conditions and delayed pull on the bucket boundary in respect to
6538 * late alarm and condition is true during the pull
6539 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestAlarmLatePullWhileConditionTrue)6540 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWhileConditionTrue) {
6541 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
6542
6543 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
6544
6545 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6546 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6547 // Pull on the initial onConditionChanged
6548 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6549 vector<std::shared_ptr<LogEvent>>* data) {
6550 data->clear();
6551 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
6552 return true;
6553 }));
6554
6555 sp<NumericValueMetricProducer> valueProducer =
6556 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
6557 pullerManager, metric, ConditionState::kFalse);
6558
6559 valueProducer->onConditionChanged(true, bucketStartTimeNs);
6560
6561 vector<shared_ptr<LogEvent>> allData;
6562
6563 // first delayed pull on the bucket #1 edge
6564 allData.clear();
6565 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
6566 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
6567
6568 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
6569 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
6570 {bucketStartTimeNs}, {bucket2StartTimeNs});
6571
6572 // second pull on the bucket #2 boundary on time
6573 allData.clear();
6574 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15));
6575 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
6576
6577 // the second pull did close the second bucket with condition duration == bucketSizeNs
6578 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5},
6579 {bucketSizeNs, bucketSizeNs}, {pullDelayNs, -pullDelayNs},
6580 {bucketStartTimeNs, bucket2StartTimeNs},
6581 {bucket2StartTimeNs, bucket3StartTimeNs});
6582 }
6583
6584 /**
6585 * Tests pulled atoms with conditions and delayed pull on the bucket boundary in respect to
6586 * late alarm and condition is false during the pull
6587 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestAlarmLatePullWhileConditionFalse)6588 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWhileConditionFalse) {
6589 const int64_t delayNs = NS_PER_SEC; // 1 sec
6590 const int64_t conditionDurationNs = NS_PER_SEC; // 1 sec
6591
6592 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
6593
6594 int increasedValue = 5;
6595 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6596 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6597 .Times(4)
6598 .WillRepeatedly(Invoke([&increasedValue](int tagId, const ConfigKey&,
6599 const int64_t eventTimeNs,
6600 vector<std::shared_ptr<LogEvent>>* data) {
6601 data->clear();
6602 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, increasedValue));
6603 increasedValue += 5;
6604 return true;
6605 }));
6606
6607 sp<NumericValueMetricProducer> valueProducer =
6608 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
6609 pullerManager, metric, ConditionState::kFalse);
6610
6611 valueProducer->onConditionChanged(true, bucketStartTimeNs);
6612 valueProducer->onConditionChanged(false, bucketStartTimeNs + conditionDurationNs);
6613
6614 vector<shared_ptr<LogEvent>> allData;
6615 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + delayNs, 10));
6616 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + delayNs);
6617
6618 // first delayed pull on the bucket #1 edge
6619 // the delayed pull did close the first bucket with condition duration == conditionDurationNs
6620 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {conditionDurationNs}, {0},
6621 {bucketStartTimeNs}, {bucket2StartTimeNs});
6622
6623 valueProducer->onConditionChanged(true, bucket2StartTimeNs + 2 * delayNs);
6624
6625 valueProducer->onConditionChanged(false,
6626 bucket2StartTimeNs + 2 * delayNs + conditionDurationNs);
6627
6628 allData.clear();
6629 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 10));
6630 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
6631
6632 // second pull on the bucket #2 edge is on time
6633 assertPastBucketValuesSingleKey(
6634 valueProducer->mPastBuckets, {5, 5}, {conditionDurationNs, conditionDurationNs}, {0, 0},
6635 {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
6636 }
6637
6638 /**
6639 * Tests pulled atoms with conditions and delayed pull on the bucket boundary in respect to
6640 * onConditionChanged true to false
6641 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestLatePullOnConditionChangeFalse)6642 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLatePullOnConditionChangeFalse) {
6643 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
6644 const int64_t arbitraryIntervalNs = 5 * NS_PER_SEC; // 5 sec interval
6645 const int64_t conditionDurationNs = 1 * NS_PER_SEC; // 1 sec
6646
6647 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
6648
6649 int increasedValue = 5;
6650 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6651 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6652 .Times(4)
6653 .WillRepeatedly(Invoke([&increasedValue](int tagId, const ConfigKey&,
6654 const int64_t eventTimeNs,
6655 vector<std::shared_ptr<LogEvent>>* data) {
6656 data->clear();
6657 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, increasedValue));
6658 increasedValue += 5;
6659 return true;
6660 }));
6661
6662 sp<NumericValueMetricProducer> valueProducer =
6663 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
6664 pullerManager, metric, ConditionState::kFalse);
6665
6666 valueProducer->onConditionChanged(true, bucketStartTimeNs);
6667
6668 // will force delayed pull & bucket close
6669 valueProducer->onConditionChanged(false, bucket2StartTimeNs + pullDelayNs);
6670
6671 // first delayed pull on the bucket #1 edge
6672 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
6673 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
6674 {bucketStartTimeNs}, {bucket2StartTimeNs});
6675
6676 // here arbitraryIntervalNs just an arbitrary interval after the delayed pull &
6677 // before the sequence of condition change events
6678 valueProducer->onConditionChanged(true, bucket2StartTimeNs + pullDelayNs + arbitraryIntervalNs);
6679
6680 valueProducer->onConditionChanged(
6681 false, bucket2StartTimeNs + pullDelayNs + arbitraryIntervalNs + conditionDurationNs);
6682
6683 vector<shared_ptr<LogEvent>> allData;
6684 allData.clear();
6685 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 30));
6686 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
6687
6688 // second pull on the bucket #2 edge is on time
6689 // the pull did close the second bucket with condition where
6690 // duration == conditionDurationNs + carryover from first bucket due to delayed pull
6691 assertPastBucketValuesSingleKey(
6692 valueProducer->mPastBuckets, {5, 5}, {bucketSizeNs, pullDelayNs + conditionDurationNs},
6693 {pullDelayNs, -pullDelayNs}, {bucketStartTimeNs, bucket2StartTimeNs},
6694 {bucket2StartTimeNs, bucket3StartTimeNs});
6695 }
6696
6697 /**
6698 * Tests pulled atoms with conditions and delayed pull on the bucket boundary in respect to
6699 * onConditionChanged false to true
6700 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestLatePullOnConditionChangeTrue)6701 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLatePullOnConditionChangeTrue) {
6702 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
6703 const int64_t conditionSwitchIntervalNs = 10 * NS_PER_SEC; // 10 sec
6704 const int64_t conditionDurationNs = 1 * NS_PER_SEC; // 1 sec
6705
6706 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
6707
6708 int increasedValue = 5;
6709 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6710 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6711 .Times(5)
6712 .WillRepeatedly(Invoke([&increasedValue](int tagId, const ConfigKey&,
6713 const int64_t eventTimeNs,
6714 vector<std::shared_ptr<LogEvent>>* data) {
6715 data->clear();
6716 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, increasedValue));
6717 increasedValue += 5;
6718 return true;
6719 }));
6720
6721 sp<NumericValueMetricProducer> valueProducer =
6722 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
6723 pullerManager, metric, ConditionState::kFalse);
6724
6725 valueProducer->onConditionChanged(true, bucketStartTimeNs);
6726
6727 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
6728
6729 valueProducer->onConditionChanged(false, bucketStartTimeNs + conditionDurationNs);
6730
6731 // will force delayed pull & bucket close
6732 valueProducer->onConditionChanged(true, bucket2StartTimeNs + pullDelayNs);
6733
6734 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
6735 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {conditionDurationNs}, {0},
6736 {bucketStartTimeNs}, {bucket2StartTimeNs});
6737
6738 valueProducer->onConditionChanged(false,
6739 bucket2StartTimeNs + pullDelayNs + conditionDurationNs);
6740
6741 // will force delayed pull & bucket close
6742 valueProducer->onConditionChanged(true, bucket3StartTimeNs + pullDelayNs);
6743
6744 // the delayed pull did close the second bucket with condition duration == conditionDurationNs
6745 assertPastBucketValuesSingleKey(
6746 valueProducer->mPastBuckets, {5, 5}, {conditionDurationNs, conditionDurationNs}, {0, 0},
6747 {bucketStartTimeNs, bucket2StartTimeNs}, {bucket2StartTimeNs, bucket3StartTimeNs});
6748 }
6749
6750 /**
6751 * Tests pulled atoms with conditions and delayed pull on the bucket boundary in respect to
6752 * late alarms. Condition is true during the pull
6753 * With a following events in the middle of the bucket
6754 * 1) onConditionChanged true to false
6755 * 2) onConditionChanged false to true
6756 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestAlarmLatePullWithConditionChanged)6757 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullWithConditionChanged) {
6758 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
6759 const int64_t conditionSwitchIntervalNs = 10 * NS_PER_SEC; // 10 sec
6760 const int64_t bucket2DelayNs = 5 * NS_PER_SEC; // 1 sec
6761 const int64_t bucket1LatePullNs = bucket2StartTimeNs + pullDelayNs; // 71 sec
6762 const int64_t bucket2LatePullNs = bucket3StartTimeNs + bucket2DelayNs; // 145 sec
6763
6764 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithCondition();
6765
6766 int increasedValue = 5;
6767 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6768 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6769 .Times(5)
6770 .WillRepeatedly(Invoke([&increasedValue](int tagId, const ConfigKey&,
6771 const int64_t eventTimeNs,
6772 vector<std::shared_ptr<LogEvent>>* data) {
6773 data->clear();
6774 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, increasedValue));
6775 increasedValue += 5;
6776 return true;
6777 }));
6778
6779 sp<NumericValueMetricProducer> valueProducer =
6780 NumericValueMetricProducerTestHelper::createValueProducerWithCondition(
6781 pullerManager, metric, ConditionState::kFalse);
6782
6783 valueProducer->onConditionChanged(true, bucketStartTimeNs);
6784
6785 // will force delayed pull & bucket #1 close
6786 vector<shared_ptr<LogEvent>> allData;
6787 allData.clear();
6788 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket1LatePullNs, 10));
6789 valueProducer->onDataPulled(allData, /** succeed */ true, bucket1LatePullNs);
6790
6791 // first delayed pull on the bucket #1 edge
6792 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
6793 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
6794 {bucketStartTimeNs}, {bucket2StartTimeNs});
6795
6796 valueProducer->onConditionChanged(false, bucket1LatePullNs + conditionSwitchIntervalNs);
6797
6798 valueProducer->onConditionChanged(true, bucket1LatePullNs + 2 * conditionSwitchIntervalNs);
6799
6800 // will force delayed pull & bucket #2 close
6801 allData.clear();
6802 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2LatePullNs, 25));
6803 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2LatePullNs);
6804
6805 // second delayed pull on the bucket #2 edge
6806 // the pull did close the second bucket with condition true
6807 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 10},
6808 {bucketSizeNs, bucketSizeNs - conditionSwitchIntervalNs},
6809 {pullDelayNs, -pullDelayNs + bucket2DelayNs},
6810 {bucketStartTimeNs, bucket2StartTimeNs},
6811 {bucket2StartTimeNs, bucket3StartTimeNs});
6812
6813 valueProducer->onConditionChanged(false, bucket2LatePullNs + conditionSwitchIntervalNs);
6814
6815 valueProducer->onConditionChanged(true, bucket2LatePullNs + 3 * conditionSwitchIntervalNs);
6816
6817 // will force pull on time & bucket #3 close
6818 allData.clear();
6819 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs, 40));
6820 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
6821
6822 // the pull did close the third bucket with condition true
6823 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 10, 15},
6824 {bucketSizeNs, bucketSizeNs - conditionSwitchIntervalNs,
6825 bucketSizeNs - 2 * conditionSwitchIntervalNs},
6826 {pullDelayNs, -pullDelayNs + bucket2DelayNs, -bucket2DelayNs},
6827 {bucketStartTimeNs, bucket2StartTimeNs, bucket3StartTimeNs},
6828 {bucket2StartTimeNs, bucket3StartTimeNs, bucket4StartTimeNs});
6829 }
6830
6831 /**
6832 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
6833 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestAlarmLatePullNoCondition)6834 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoCondition) {
6835 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
6836
6837 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
6838
6839 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6840 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6841 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6842 vector<std::shared_ptr<LogEvent>>* data) {
6843 data->clear();
6844 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
6845 return true;
6846 }));
6847
6848 sp<NumericValueMetricProducer> valueProducer =
6849 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
6850 metric);
6851
6852 vector<shared_ptr<LogEvent>> allData;
6853
6854 // first delayed pull on the bucket #1 edge
6855 allData.clear();
6856 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
6857 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
6858
6859 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
6860 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
6861 {bucketStartTimeNs}, {bucket2StartTimeNs});
6862
6863 // second pull on the bucket #2 boundary on time
6864 allData.clear();
6865 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15));
6866 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
6867
6868 // the second pull did close the second bucket with condition duration == bucketSizeNs
6869 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5},
6870 {bucketSizeNs, bucketSizeNs}, {pullDelayNs, -pullDelayNs},
6871 {bucketStartTimeNs, bucket2StartTimeNs},
6872 {bucket2StartTimeNs, bucket3StartTimeNs});
6873
6874 // third pull on the bucket #3 boundary on time
6875 allData.clear();
6876 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs, 20));
6877 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
6878
6879 // the third pull did close the third bucket with condition duration == bucketSizeNs
6880 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5, 5},
6881 {bucketSizeNs, bucketSizeNs, bucketSizeNs},
6882 {pullDelayNs, -pullDelayNs, 0},
6883 {bucketStartTimeNs, bucket2StartTimeNs, bucket3StartTimeNs},
6884 {bucket2StartTimeNs, bucket3StartTimeNs, bucket4StartTimeNs});
6885 }
6886
6887 /**
6888 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
6889 * The skipped bucket is introduced prior delayed pull
6890 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestAlarmLatePullNoConditionWithSkipped)6891 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestAlarmLatePullNoConditionWithSkipped) {
6892 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
6893
6894 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
6895
6896 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6897 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
6898 .WillOnce(Return(true));
6899
6900 sp<NumericValueMetricProducer> valueProducer =
6901 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
6902 metric);
6903
6904 vector<shared_ptr<LogEvent>> allData;
6905
6906 // first delayed pull on the bucket #1 edge with delay
6907 allData.clear();
6908 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
6909 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
6910
6911 // the delayed pull did close the first bucket which is skipped
6912 // skipped due to bucket does not contains any value
6913 ASSERT_EQ(0UL, valueProducer->mPastBuckets.size());
6914 ASSERT_EQ(1UL, valueProducer->mSkippedBuckets.size());
6915
6916 // second pull on the bucket #2 boundary on time
6917 allData.clear();
6918 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15));
6919 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
6920
6921 // the second pull did close the second bucket with condition duration == bucketSizeNs
6922 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs},
6923 {-pullDelayNs}, {bucket2StartTimeNs}, {bucket3StartTimeNs});
6924
6925 // third pull on the bucket #3 boundary on time
6926 allData.clear();
6927 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs, 20));
6928 valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
6929
6930 // the third pull did close the third bucket with condition duration == bucketSizeNs
6931 assertPastBucketValuesSingleKey(
6932 valueProducer->mPastBuckets, {5, 5}, {bucketSizeNs, bucketSizeNs}, {-pullDelayNs, 0},
6933 {bucket2StartTimeNs, bucket3StartTimeNs}, {bucket3StartTimeNs, bucket4StartTimeNs});
6934 }
6935
6936 /**
6937 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
6938 * The threshold is not defined - correction upload should be skipped
6939 * Metric population scenario mimics the
6940 * NumericValueMetricProducerTest_ConditionCorrection.TestAlarmLatePullNoCondition test
6941 * to extent of a single bucket with correction value due to pull delay
6942 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestThresholdNotDefinedNoUpload)6943 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdNotDefinedNoUpload) {
6944 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
6945
6946 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
6947 ASSERT_FALSE(metric.has_condition_correction_threshold_nanos());
6948
6949 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
6950 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
6951 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
6952 vector<std::shared_ptr<LogEvent>>* data) {
6953 data->clear();
6954 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
6955 return true;
6956 }));
6957
6958 sp<NumericValueMetricProducer> valueProducer =
6959 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
6960 metric);
6961
6962 ASSERT_FALSE(valueProducer->mConditionCorrectionThresholdNs.has_value());
6963
6964 vector<shared_ptr<LogEvent>> allData;
6965
6966 // first delayed pull on the bucket #1 edge
6967 allData.clear();
6968 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
6969 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
6970
6971 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
6972 // and the condition correction == pull delay
6973 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
6974 {bucketStartTimeNs}, {bucket2StartTimeNs});
6975
6976 // generate dump report and validate correction value in the reported buckets
6977 ProtoOutputStream output;
6978 std::set<string> strSet;
6979 valueProducer->onDumpReport(bucket3StartTimeNs, false /* include partial bucket */, true,
6980 FAST /* dumpLatency */, &strSet, &output);
6981
6982 StatsLogReport report = outputStreamToProto(&output);
6983
6984 EXPECT_TRUE(report.has_value_metrics());
6985 ASSERT_EQ(1, report.value_metrics().data_size());
6986 ASSERT_EQ(0, report.value_metrics().skipped_size());
6987 ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
6988 EXPECT_FALSE(report.value_metrics().data(0).bucket_info(0).has_condition_correction_nanos());
6989 }
6990
6991 /**
6992 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
6993 * The threshold set to zero - correction should be performed
6994 * Metric population scenario mimics the
6995 * NumericValueMetricProducerTest_ConditionCorrection.TestAlarmLatePullNoCondition test
6996 * to extent of a single bucket with correction value due to pull delay
6997 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestThresholdDefinedZero)6998 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdDefinedZero) {
6999 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
7000 const int64_t correctionThresholdNs = 0; // 0 sec
7001
7002 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
7003 metric.set_condition_correction_threshold_nanos(correctionThresholdNs);
7004
7005 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
7006 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
7007 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7008 vector<std::shared_ptr<LogEvent>>* data) {
7009 data->clear();
7010 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
7011 return true;
7012 }));
7013
7014 sp<NumericValueMetricProducer> valueProducer =
7015 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
7016 metric);
7017
7018 ASSERT_EQ(correctionThresholdNs, valueProducer->mConditionCorrectionThresholdNs);
7019
7020 vector<shared_ptr<LogEvent>> allData;
7021
7022 // first delayed pull on the bucket #1 edge
7023 allData.clear();
7024 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
7025 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
7026
7027 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
7028 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
7029 {bucketStartTimeNs}, {bucket2StartTimeNs});
7030
7031 // generate dump report and validate correction value in the reported buckets
7032 ProtoOutputStream output;
7033 std::set<string> strSet;
7034 valueProducer->onDumpReport(bucket3StartTimeNs, false /* include partial bucket */, true,
7035 FAST /* dumpLatency */, &strSet, &output);
7036
7037 StatsLogReport report = outputStreamToProto(&output);
7038
7039 EXPECT_TRUE(report.has_value_metrics());
7040 ASSERT_EQ(1, report.value_metrics().data_size());
7041 ASSERT_EQ(0, report.value_metrics().skipped_size());
7042 ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
7043 EXPECT_EQ(pullDelayNs,
7044 report.value_metrics().data(0).bucket_info(0).condition_correction_nanos());
7045 }
7046
7047 /**
7048 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
7049 * The threshold is equal to the pullDelayNs - correction should be performed
7050 * Metric population scenario mimics the
7051 * NumericValueMetricProducerTest_ConditionCorrection.TestAlarmLatePullNoCondition test
7052 * to extent of a 2 bucket with correction value due to pull delay
7053 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestThresholdUploadPassWhenEqual)7054 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadPassWhenEqual) {
7055 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
7056 const int64_t correctionThresholdNs = pullDelayNs; // 1 sec
7057
7058 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
7059 metric.set_condition_correction_threshold_nanos(pullDelayNs);
7060 ASSERT_EQ(pullDelayNs, metric.condition_correction_threshold_nanos());
7061
7062 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
7063 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
7064 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7065 vector<std::shared_ptr<LogEvent>>* data) {
7066 data->clear();
7067 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
7068 return true;
7069 }));
7070
7071 sp<NumericValueMetricProducer> valueProducer =
7072 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
7073 metric);
7074
7075 ASSERT_EQ(correctionThresholdNs, valueProducer->mConditionCorrectionThresholdNs);
7076
7077 vector<shared_ptr<LogEvent>> allData;
7078
7079 // first delayed pull on the bucket #1 edge
7080 allData.clear();
7081 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
7082 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
7083
7084 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
7085 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
7086 {bucketStartTimeNs}, {bucket2StartTimeNs});
7087
7088 // second pull on the bucket #2 boundary on time
7089 allData.clear();
7090 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs, 15));
7091 valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
7092
7093 // the second pull did close the second bucket with condition duration == bucketSizeNs
7094 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5, 5},
7095 {bucketSizeNs, bucketSizeNs}, {pullDelayNs, -pullDelayNs},
7096 {bucketStartTimeNs, bucket2StartTimeNs},
7097 {bucket2StartTimeNs, bucket3StartTimeNs});
7098
7099 // generate dump report and validate correction value in the reported buckets
7100 ProtoOutputStream output;
7101 std::set<string> strSet;
7102 valueProducer->onDumpReport(bucket3StartTimeNs, false /* include partial bucket */, true,
7103 FAST /* dumpLatency */, &strSet, &output);
7104
7105 StatsLogReport report = outputStreamToProto(&output);
7106
7107 EXPECT_TRUE(report.has_value_metrics());
7108 ASSERT_EQ(1, report.value_metrics().data_size());
7109 ASSERT_EQ(0, report.value_metrics().skipped_size());
7110 ASSERT_EQ(2, report.value_metrics().data(0).bucket_info_size());
7111 EXPECT_EQ(pullDelayNs,
7112 report.value_metrics().data(0).bucket_info(0).condition_correction_nanos());
7113 EXPECT_EQ(-pullDelayNs,
7114 report.value_metrics().data(0).bucket_info(1).condition_correction_nanos());
7115 }
7116
7117 /**
7118 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
7119 * The threshold is smaller thant pullDelayNs - correction should be performed
7120 * Metric population scenario mimics the
7121 * NumericValueMetricProducerTest_ConditionCorrection.TestAlarmLatePullNoCondition test
7122 * to extent of a single bucket with correction value due to pull delay
7123 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestThresholdUploadPassWhenGreater)7124 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadPassWhenGreater) {
7125 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
7126 const int64_t correctionThresholdNs = NS_PER_SEC - 1; // less than 1 sec
7127
7128 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
7129 metric.set_condition_correction_threshold_nanos(correctionThresholdNs);
7130 ASSERT_EQ(correctionThresholdNs, metric.condition_correction_threshold_nanos());
7131
7132 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
7133 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
7134 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7135 vector<std::shared_ptr<LogEvent>>* data) {
7136 data->clear();
7137 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
7138 return true;
7139 }));
7140
7141 sp<NumericValueMetricProducer> valueProducer =
7142 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
7143 metric);
7144
7145 ASSERT_EQ(correctionThresholdNs, valueProducer->mConditionCorrectionThresholdNs);
7146
7147 vector<shared_ptr<LogEvent>> allData;
7148
7149 // first delayed pull on the bucket #1 edge
7150 allData.clear();
7151 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
7152 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
7153
7154 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
7155 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
7156 {bucketStartTimeNs}, {bucket2StartTimeNs});
7157
7158 // generate dump report and validate correction value in the reported buckets
7159 ProtoOutputStream output;
7160 std::set<string> strSet;
7161 valueProducer->onDumpReport(bucket3StartTimeNs, false /* include partial bucket */, true,
7162 FAST /* dumpLatency */, &strSet, &output);
7163
7164 StatsLogReport report = outputStreamToProto(&output);
7165
7166 EXPECT_TRUE(report.has_value_metrics());
7167 ASSERT_EQ(1, report.value_metrics().data_size());
7168 ASSERT_EQ(0, report.value_metrics().skipped_size());
7169 ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
7170 EXPECT_EQ(pullDelayNs,
7171 report.value_metrics().data(0).bucket_info(0).condition_correction_nanos());
7172 }
7173
7174 /**
7175 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
7176 * The threshold is greater than pullDelayNs - correction upload should be skipped
7177 * Metric population scenario mimics the
7178 * NumericValueMetricProducerTest_ConditionCorrection.TestAlarmLatePullNoCondition test
7179 * to extent of a single bucket with correction value due to pull delay
7180 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestThresholdUploadSkip)7181 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestThresholdUploadSkip) {
7182 const int64_t pullDelayNs = 1 * NS_PER_SEC; // 1 sec
7183 const int64_t correctionThresholdNs = NS_PER_SEC + 1; // greater than 1 sec
7184
7185 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
7186 metric.set_condition_correction_threshold_nanos(correctionThresholdNs);
7187 ASSERT_EQ(correctionThresholdNs, metric.condition_correction_threshold_nanos());
7188
7189 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
7190 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
7191 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7192 vector<std::shared_ptr<LogEvent>>* data) {
7193 data->clear();
7194 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
7195 return true;
7196 }));
7197
7198 sp<NumericValueMetricProducer> valueProducer =
7199 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
7200 metric);
7201
7202 ASSERT_EQ(correctionThresholdNs, valueProducer->mConditionCorrectionThresholdNs);
7203
7204 vector<shared_ptr<LogEvent>> allData;
7205
7206 // first delayed pull on the bucket #1 edge
7207 allData.clear();
7208 allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + pullDelayNs, 10));
7209 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + pullDelayNs);
7210
7211 // the delayed pull did close the first bucket with condition duration == bucketSizeNs
7212 assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {5}, {bucketSizeNs}, {pullDelayNs},
7213 {bucketStartTimeNs}, {bucket2StartTimeNs});
7214
7215 // generate dump report and validate correction value in the reported buckets
7216 ProtoOutputStream output;
7217 std::set<string> strSet;
7218 valueProducer->onDumpReport(bucket3StartTimeNs, false /* include partial bucket */, true,
7219 FAST /* dumpLatency */, &strSet, &output);
7220
7221 StatsLogReport report = outputStreamToProto(&output);
7222
7223 EXPECT_TRUE(report.has_value_metrics());
7224 ASSERT_EQ(1, report.value_metrics().data_size());
7225 ASSERT_EQ(0, report.value_metrics().skipped_size());
7226 ASSERT_EQ(1, report.value_metrics().data(0).bucket_info_size());
7227 EXPECT_FALSE(report.value_metrics().data(0).bucket_info(0).has_condition_correction_nanos());
7228 }
7229
7230 /**
7231 * Tests pulled atoms with no conditions and delayed pull on the bucket boundary
7232 * for the atoms sliced by state. Delayed pull occures due to delayed onStateChange event
7233 * First bucket ends with delayed OFF -> ON transition, correction is applied only to OFF state
7234 * Second and third buckets pulled ontime
7235 */
TEST(NumericValueMetricProducerTest_ConditionCorrection,TestLateStateChangeSlicedAtoms)7236 TEST(NumericValueMetricProducerTest_ConditionCorrection, TestLateStateChangeSlicedAtoms) {
7237 // Set up NumericValueMetricProducer.
7238 ValueMetric metric =
7239 NumericValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
7240 metric.set_condition_correction_threshold_nanos(0);
7241 int increasedValue = 1;
7242 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
7243 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
7244 .Times(5)
7245 .WillRepeatedly(Invoke([&increasedValue](int tagId, const ConfigKey&,
7246 const int64_t eventTimeNs,
7247 vector<std::shared_ptr<LogEvent>>* data) {
7248 data->clear();
7249 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, increasedValue++));
7250 return true;
7251 }));
7252
7253 StateManager::getInstance().clear();
7254 sp<NumericValueMetricProducer> valueProducer =
7255 NumericValueMetricProducerTestHelper::createValueProducerWithState(
7256 pullerManager, metric, {util::SCREEN_STATE_CHANGED}, {});
7257
7258 // Set up StateManager and check that StateTrackers are initialized.
7259 StateManager::getInstance().registerListener(SCREEN_STATE_ATOM_ID, valueProducer);
7260
7261 // Bucket status after screen state change kStateUnknown->OFF
7262 auto screenEvent = CreateScreenStateChangedEvent(
7263 bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
7264 StateManager::getInstance().onLogEvent(*screenEvent);
7265 ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
7266
7267 // Value for dimension, state key {{}, OFF}
7268 auto it = valueProducer->mCurrentSlicedBucket.begin();
7269 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
7270 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
7271 assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
7272
7273 // Bucket status after screen state change OFF->ON, forces bucket flush and new bucket start
7274 // with 10 seconds delay
7275 screenEvent = CreateScreenStateChangedEvent(bucket2StartTimeNs + 10 * NS_PER_SEC,
7276 android::view::DisplayStateEnum::DISPLAY_STATE_ON);
7277 StateManager::getInstance().onLogEvent(*screenEvent);
7278 // Bucket flush will trim all MetricDimensionKeys besides the current state key.
7279 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
7280 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
7281
7282 // mCurrentSlicedBucket represents second bucket
7283 // Value for dimension, state key {{}, ON}
7284 it = valueProducer->mCurrentSlicedBucket.begin();
7285 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
7286 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
7287 assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 10 * NS_PER_SEC);
7288
7289 // Bucket status after screen state change ON->OFF, forces bucket flush and new bucket start
7290 screenEvent = CreateScreenStateChangedEvent(bucket3StartTimeNs,
7291 android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
7292 StateManager::getInstance().onLogEvent(*screenEvent);
7293 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
7294 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
7295
7296 // mCurrentSlicedBucket represents third bucket
7297 // Value for dimension, state key {{}, OFF}
7298 it = valueProducer->mCurrentSlicedBucket.begin();
7299 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
7300 it->first.getStateValuesKey().getValues()[0].mValue.int_value);
7301 assertConditionTimer(it->second.conditionTimer, true, 0, bucket3StartTimeNs, 0);
7302
7303 // Bucket status after screen state change OFF->ON, forces bucket flush and new bucket start
7304 screenEvent = CreateScreenStateChangedEvent(bucket4StartTimeNs,
7305 android::view::DisplayStateEnum::DISPLAY_STATE_ON);
7306 StateManager::getInstance().onLogEvent(*screenEvent);
7307 ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
7308 ASSERT_EQ(1UL, valueProducer->mDimInfos.size());
7309
7310 // Start dump report and check output.
7311 ProtoOutputStream output;
7312 std::set<string> strSet;
7313 valueProducer->onDumpReport(bucket4StartTimeNs + 10, false /* do not include partial buckets */,
7314 true, NO_TIME_CONSTRAINTS, &strSet, &output);
7315
7316 StatsLogReport report = outputStreamToProto(&output);
7317 backfillStartEndTimestamp(&report);
7318 EXPECT_TRUE(report.has_value_metrics());
7319 ASSERT_EQ(3, report.value_metrics().data_size());
7320
7321 // {{}, ON} - delayed start finish on time - no correction
7322 auto data = report.value_metrics().data(0);
7323 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
7324 ValidateValueBucket(data.bucket_info(0), bucket2StartTimeNs, bucket3StartTimeNs, {1},
7325 50 * NS_PER_SEC, 0);
7326
7327 // {{}, Unknown}
7328 data = report.value_metrics().data(1);
7329 EXPECT_EQ(-1, data.slice_by_state(0).value());
7330 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {1},
7331 5 * NS_PER_SEC, 0);
7332
7333 // {{}, OFF}
7334 data = report.value_metrics().data(2);
7335 EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
7336 ASSERT_EQ(2, data.bucket_info_size());
7337 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {1},
7338 55 * NS_PER_SEC, 10 * NS_PER_SEC);
7339 ValidateValueBucket(data.bucket_info(1), bucket3StartTimeNs, bucket4StartTimeNs, {1},
7340 60 * NS_PER_SEC, 0);
7341 }
7342
TEST(NumericValueMetricProducerTest,TestSubsetDimensions)7343 TEST(NumericValueMetricProducerTest, TestSubsetDimensions) {
7344 // Create metric with subset of dimensions.
7345 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetric();
7346 *metric.mutable_dimensions_in_what() = CreateDimensions(tagId, {1 /*uid*/});
7347
7348 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
7349
7350 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
7351 // First and third fields are dimension fields. Second field is the value field.
7352 // First bucket pull.
7353 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7354 vector<std::shared_ptr<LogEvent>>* data) {
7355 data->clear();
7356 data->push_back(
7357 CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 1, 1 /*uid*/, 5, 5));
7358 data->push_back(
7359 CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 1, 1 /*uid*/, 5, 7));
7360 data->push_back(
7361 CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 1, 2 /*uid*/, 6, 5));
7362 data->push_back(
7363 CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 1, 2 /*uid*/, 6, 7));
7364 return true;
7365 }))
7366 // Dump report.
7367 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7368 vector<std::shared_ptr<LogEvent>>* data) {
7369 data->clear();
7370 data->push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 10000000000,
7371 1 /*uid*/, 13, 5));
7372 data->push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 10000000000,
7373 1 /*uid*/, 15, 7));
7374 data->push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 10000000000,
7375 2 /*uid*/, 21, 5));
7376 data->push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 10000000000,
7377 2 /*uid*/, 22, 7));
7378 return true;
7379 }));
7380
7381 sp<NumericValueMetricProducer> valueProducer =
7382 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
7383 metric);
7384
7385 // Bucket 2 start.
7386 vector<shared_ptr<LogEvent>> allData;
7387 allData.clear();
7388 allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 1 /*uid*/, 10, 5));
7389 allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 1 /*uid*/, 11, 7));
7390 allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 8, 5));
7391 allData.push_back(CreateThreeValueLogEvent(tagId, bucket2StartTimeNs + 1, 2 /*uid*/, 9, 7));
7392 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
7393 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
7394 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
7395
7396 // Check dump report.
7397 ProtoOutputStream output;
7398 std::set<string> strSet;
7399 int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000;
7400 valueProducer->onDumpReport(dumpReportTimeNs, true /* include current buckets */, true,
7401 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
7402 ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
7403 ASSERT_EQ(2UL, valueProducer->mDimInfos.size());
7404
7405 StatsLogReport report = outputStreamToProto(&output);
7406 backfillDimensionPath(&report);
7407 backfillStartEndTimestamp(&report);
7408 EXPECT_TRUE(report.has_value_metrics());
7409 StatsLogReport::ValueMetricDataWrapper valueMetrics;
7410 sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
7411 ASSERT_EQ(2, valueMetrics.data_size());
7412 EXPECT_EQ(0, report.value_metrics().skipped_size());
7413
7414 // Check data keyed to uid 1.
7415 ValueMetricData data = valueMetrics.data(0);
7416 ValidateUidDimension(data.dimensions_in_what(), tagId, 1);
7417 ASSERT_EQ(2, data.bucket_info_size());
7418 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {11}, -1, 0);
7419 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, dumpReportTimeNs, {7}, -1, 0);
7420
7421 // Check data keyed to uid 2.
7422 data = valueMetrics.data(1);
7423 ValidateUidDimension(data.dimensions_in_what(), tagId, 2);
7424 ASSERT_EQ(2, data.bucket_info_size());
7425 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {5}, -1, 0);
7426 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, dumpReportTimeNs, {26}, -1, 0);
7427 }
7428
TEST(NumericValueMetricProducerTest,TestRepeatedValueFieldAndDimensions)7429 TEST(NumericValueMetricProducerTest, TestRepeatedValueFieldAndDimensions) {
7430 ValueMetric metric = NumericValueMetricProducerTestHelper::createMetricWithRepeatedValueField();
7431 metric.mutable_dimensions_in_what()->set_field(tagId);
7432 FieldMatcher* valueChild = metric.mutable_dimensions_in_what()->add_child();
7433 valueChild->set_field(1);
7434 valueChild->set_position(Position::FIRST);
7435
7436 sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
7437
7438 EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
7439 // First field is a dimension field (repeated, position FIRST).
7440 // Third field is the value field (repeated, position FIRST).
7441 // NumericValueMetricProducer initialized.
7442 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7443 vector<std::shared_ptr<LogEvent>>* data) {
7444 data->clear();
7445 data->push_back(
7446 makeRepeatedUidLogEvent(tagId, bucketStartTimeNs + 1, {1, 10}, 5, {2, 3}));
7447 data->push_back(
7448 makeRepeatedUidLogEvent(tagId, bucketStartTimeNs + 1, {2, 10}, 5, {3, 4}));
7449 return true;
7450 }))
7451 // Dump report pull.
7452 .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
7453 vector<std::shared_ptr<LogEvent>>* data) {
7454 data->clear();
7455 data->push_back(makeRepeatedUidLogEvent(tagId, bucket2StartTimeNs + 10000000000,
7456 {1, 10}, 5, {10, 3}));
7457 data->push_back(makeRepeatedUidLogEvent(tagId, bucket2StartTimeNs + 10000000000,
7458 {2, 10}, 5, {14, 4}));
7459 return true;
7460 }));
7461
7462 sp<NumericValueMetricProducer> valueProducer =
7463 NumericValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager,
7464 metric);
7465
7466 // Bucket 2 start.
7467 vector<shared_ptr<LogEvent>> allData;
7468 allData.clear();
7469 allData.push_back(makeRepeatedUidLogEvent(tagId, bucket2StartTimeNs + 1, {1, 10}, 5, {5, 7}));
7470 allData.push_back(makeRepeatedUidLogEvent(tagId, bucket2StartTimeNs + 1, {2, 10}, 5, {7, 5}));
7471 valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
7472
7473 // Check dump report.
7474 ProtoOutputStream output;
7475 std::set<string> strSet;
7476 int64_t dumpReportTimeNs = bucket2StartTimeNs + 10000000000;
7477 valueProducer->onDumpReport(dumpReportTimeNs, true /* include current buckets */, true,
7478 NO_TIME_CONSTRAINTS /* dumpLatency */, &strSet, &output);
7479
7480 StatsLogReport report = outputStreamToProto(&output);
7481 backfillDimensionPath(&report);
7482 backfillStartEndTimestamp(&report);
7483 EXPECT_TRUE(report.has_value_metrics());
7484 StatsLogReport::ValueMetricDataWrapper valueMetrics;
7485 sortMetricDataByDimensionsValue(report.value_metrics(), &valueMetrics);
7486 ASSERT_EQ(2, valueMetrics.data_size());
7487 EXPECT_EQ(0, report.value_metrics().skipped_size());
7488
7489 // Check data keyed to uid 1.
7490 ValueMetricData data = valueMetrics.data(0);
7491 ValidateUidDimension(data.dimensions_in_what(), tagId, 1);
7492 ASSERT_EQ(2, data.bucket_info_size());
7493 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {3}, -1,
7494 0); // Summed diffs of 2, 5
7495 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, dumpReportTimeNs, {5}, -1,
7496 0); // Summed diffs of 5, 10
7497
7498 // Check data keyed to uid 2.
7499 data = valueMetrics.data(1);
7500 ValidateUidDimension(data.dimensions_in_what(), tagId, 2);
7501 ASSERT_EQ(2, data.bucket_info_size());
7502 ValidateValueBucket(data.bucket_info(0), bucketStartTimeNs, bucket2StartTimeNs, {4}, -1,
7503 0); // Summed diffs of 3, 7
7504 ValidateValueBucket(data.bucket_info(1), bucket2StartTimeNs, dumpReportTimeNs, {7}, -1,
7505 0); // Summed diffs of 7, 14
7506 }
7507
7508 } // namespace statsd
7509 } // namespace os
7510 } // namespace android
7511 #else
7512 GTEST_LOG_(INFO) << "This test does nothing.\n";
7513 #endif
7514