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