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