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