ValueMetricProducer_test.cpp revision 330af58f2b8582b855085655fae553cdfaf44e6c
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/ValueMetricProducer.h"
16#include "src/stats_log_util.h"
17#include "metrics_test_helper.h"
18#include "tests/statsd_test_util.h"
19
20#include <gmock/gmock.h>
21#include <gtest/gtest.h>
22#include <stdio.h>
23#include <vector>
24
25using namespace testing;
26using android::sp;
27using std::make_shared;
28using std::set;
29using std::shared_ptr;
30using std::unordered_map;
31using std::vector;
32
33#ifdef __ANDROID__
34
35namespace android {
36namespace os {
37namespace statsd {
38
39const ConfigKey kConfigKey(0, 12345);
40const int tagId = 1;
41const int64_t metricId = 123;
42const int64_t bucketStartTimeNs = 10000000000;
43const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
44const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
45const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
46const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs;
47const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC;
48
49/*
50 * Tests pulled atoms with no conditions
51 */
52TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
53    ValueMetric metric;
54    metric.set_id(metricId);
55    metric.set_bucket(ONE_MINUTE);
56    metric.mutable_value_field()->set_field(tagId);
57    metric.mutable_value_field()->add_child()->set_field(2);
58
59    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
60    // TODO: pending refactor of StatsPullerManager
61    // For now we still need this so that it doesn't do real pulling.
62    shared_ptr<MockStatsPullerManager> pullerManager =
63            make_shared<StrictMock<MockStatsPullerManager>>();
64    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
65    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
66
67    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
68                                      tagId, bucketStartTimeNs, pullerManager);
69
70    vector<shared_ptr<LogEvent>> allData;
71    allData.clear();
72    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
73    event->write(tagId);
74    event->write(11);
75    event->init();
76    allData.push_back(event);
77
78    valueProducer.onDataPulled(allData);
79    // has one slice
80    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
81    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
82    // startUpdated:true tainted:0 sum:0 start:11
83    EXPECT_EQ(true, curInterval.startUpdated);
84    EXPECT_EQ(0, curInterval.tainted);
85    EXPECT_EQ(0, curInterval.sum);
86    EXPECT_EQ(11, curInterval.start);
87    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
88    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
89
90    allData.clear();
91    event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
92    event->write(tagId);
93    event->write(23);
94    event->init();
95    allData.push_back(event);
96    valueProducer.onDataPulled(allData);
97    // has one slice
98    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
99    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
100    // tartUpdated:false tainted:0 sum:12
101    EXPECT_EQ(true, curInterval.startUpdated);
102    EXPECT_EQ(0, curInterval.tainted);
103    EXPECT_EQ(0, curInterval.sum);
104    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
105    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
106    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
107
108    allData.clear();
109    event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
110    event->write(tagId);
111    event->write(36);
112    event->init();
113    allData.push_back(event);
114    valueProducer.onDataPulled(allData);
115    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
116    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
117    // startUpdated:false tainted:0 sum:12
118    EXPECT_EQ(true, curInterval.startUpdated);
119    EXPECT_EQ(0, curInterval.tainted);
120    EXPECT_EQ(0, curInterval.sum);
121    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
122    EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
123    EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue);
124}
125
126/*
127 * Test pulled event with non sliced condition.
128 */
129TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
130    ValueMetric metric;
131    metric.set_id(metricId);
132    metric.set_bucket(ONE_MINUTE);
133    metric.mutable_value_field()->set_field(tagId);
134    metric.mutable_value_field()->add_child()->set_field(2);
135    metric.set_condition(StringToId("SCREEN_ON"));
136
137    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
138    shared_ptr<MockStatsPullerManager> pullerManager =
139            make_shared<StrictMock<MockStatsPullerManager>>();
140    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
141    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
142
143    EXPECT_CALL(*pullerManager, Pull(tagId, _))
144            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
145                data->clear();
146                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
147                event->write(tagId);
148                event->write(100);
149                event->init();
150                data->push_back(event);
151                return true;
152            }))
153            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
154                data->clear();
155                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
156                event->write(tagId);
157                event->write(120);
158                event->init();
159                data->push_back(event);
160                return true;
161            }));
162
163    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
164                                      pullerManager);
165
166    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
167
168    // has one slice
169    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
170    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
171    // startUpdated:false tainted:0 sum:0 start:100
172    EXPECT_EQ(100, curInterval.start);
173    EXPECT_EQ(true, curInterval.startUpdated);
174    EXPECT_EQ(0, curInterval.tainted);
175    EXPECT_EQ(0, curInterval.sum);
176    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
177
178    vector<shared_ptr<LogEvent>> allData;
179    allData.clear();
180    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
181    event->write(1);
182    event->write(110);
183    event->init();
184    allData.push_back(event);
185    valueProducer.onDataPulled(allData);
186
187    // has one slice
188    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
189    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
190    // startUpdated:false tainted:0 sum:0 start:110
191    EXPECT_EQ(110, curInterval.start);
192    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
193    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
194    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
195
196    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
197
198    // has one slice
199    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
200    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
201    // startUpdated:false tainted:0 sum:0 start:110
202    EXPECT_EQ(10, curInterval.sum);
203    EXPECT_EQ(false, curInterval.startUpdated);
204}
205
206TEST(ValueMetricProducerTest, TestPushedEventsWithUpgrade) {
207    ValueMetric metric;
208    metric.set_id(metricId);
209    metric.set_bucket(ONE_MINUTE);
210    metric.mutable_value_field()->set_field(tagId);
211    metric.mutable_value_field()->add_child()->set_field(2);
212
213    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
214    shared_ptr<MockStatsPullerManager> pullerManager =
215            make_shared<StrictMock<MockStatsPullerManager>>();
216    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
217                                      pullerManager);
218
219    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
220    event1->write(1);
221    event1->write(10);
222    event1->init();
223    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
224    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
225
226    valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
227    EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
228    EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
229
230    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 59 * NS_PER_SEC);
231    event2->write(1);
232    event2->write(10);
233    event2->init();
234    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
235    EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
236    EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
237
238    // Next value should create a new bucket.
239    shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 65 * NS_PER_SEC);
240    event3->write(1);
241    event3->write(10);
242    event3->init();
243    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
244    EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
245    EXPECT_EQ((uint64_t)bucketStartTimeNs + bucketSizeNs, valueProducer.mCurrentBucketStartTimeNs);
246}
247
248TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade) {
249    ValueMetric metric;
250    metric.set_id(metricId);
251    metric.set_bucket(ONE_MINUTE);
252    metric.mutable_value_field()->set_field(tagId);
253    metric.mutable_value_field()->add_child()->set_field(2);
254
255    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
256    shared_ptr<MockStatsPullerManager> pullerManager =
257            make_shared<StrictMock<MockStatsPullerManager>>();
258    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
259    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
260    EXPECT_CALL(*pullerManager, Pull(tagId, _))
261            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
262                data->clear();
263                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
264                event->write(tagId);
265                event->write(120);
266                event->init();
267                data->push_back(event);
268                return true;
269            }));
270    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, tagId, bucketStartTimeNs,
271                                      pullerManager);
272
273    vector<shared_ptr<LogEvent>> allData;
274    allData.clear();
275    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
276    event->write(tagId);
277    event->write(100);
278    event->init();
279    allData.push_back(event);
280
281    valueProducer.onDataPulled(allData);
282    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
283
284    valueProducer.notifyAppUpgrade(eventUpgradeTimeNs, "ANY.APP", 1, 1);
285    EXPECT_EQ(1UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
286    EXPECT_EQ((uint64_t)eventUpgradeTimeNs, valueProducer.mCurrentBucketStartTimeNs);
287    EXPECT_EQ(20L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][0].mValue);
288
289    allData.clear();
290    event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
291    event->write(tagId);
292    event->write(150);
293    event->init();
294    allData.push_back(event);
295    valueProducer.onDataPulled(allData);
296    EXPECT_EQ(2UL, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
297    EXPECT_EQ((uint64_t)bucket2StartTimeNs, valueProducer.mCurrentBucketStartTimeNs);
298    EXPECT_EQ(30L, valueProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1].mValue);
299}
300
301TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
302    ValueMetric metric;
303    metric.set_id(metricId);
304    metric.set_bucket(ONE_MINUTE);
305    metric.mutable_value_field()->set_field(tagId);
306    metric.mutable_value_field()->add_child()->set_field(2);
307
308    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
309    shared_ptr<MockStatsPullerManager> pullerManager =
310            make_shared<StrictMock<MockStatsPullerManager>>();
311
312    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
313                                      pullerManager);
314
315    shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
316    event1->write(1);
317    event1->write(10);
318    event1->init();
319    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
320    event2->write(1);
321    event2->write(20);
322    event2->init();
323    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
324    // has one slice
325    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
326    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
327    EXPECT_EQ(10, curInterval.sum);
328
329    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
330
331    // has one slice
332    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
333    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
334    EXPECT_EQ(30, curInterval.sum);
335
336    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
337    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
338    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
339    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
340}
341
342TEST(ValueMetricProducerTest, TestAnomalyDetection) {
343    Alert alert;
344    alert.set_id(101);
345    alert.set_metric_id(metricId);
346    alert.set_trigger_if_sum_gt(130);
347    alert.set_num_buckets(2);
348    const int32_t refPeriodSec = 3;
349    alert.set_refractory_period_secs(refPeriodSec);
350
351    ValueMetric metric;
352    metric.set_id(metricId);
353    metric.set_bucket(ONE_MINUTE);
354    metric.mutable_value_field()->set_field(tagId);
355    metric.mutable_value_field()->add_child()->set_field(2);
356
357    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
358    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
359                                      -1 /*not pulled*/, bucketStartTimeNs);
360    sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert);
361
362
363    shared_ptr<LogEvent> event1
364            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC);
365    event1->write(161);
366    event1->write(10); // value of interest
367    event1->init();
368    shared_ptr<LogEvent> event2
369            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC);
370    event2->write(162);
371    event2->write(20); // value of interest
372    event2->init();
373    shared_ptr<LogEvent> event3
374            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC);
375    event3->write(163);
376    event3->write(130); // value of interest
377    event3->init();
378    shared_ptr<LogEvent> event4
379            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC);
380    event4->write(35);
381    event4->write(1); // value of interest
382    event4->init();
383    shared_ptr<LogEvent> event5
384            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
385    event5->write(45);
386    event5->write(150); // value of interest
387    event5->init();
388    shared_ptr<LogEvent> event6
389            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
390    event6->write(25);
391    event6->write(160); // value of interest
392    event6->init();
393
394    // Two events in bucket #0.
395    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
396    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
397    // Value sum == 30 <= 130.
398    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
399
400    // One event in bucket #2. No alarm as bucket #0 is trashed out.
401    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
402    // Value sum == 130 <= 130.
403    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
404
405    // Three events in bucket #3.
406    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
407    // Anomaly at event 4 since Value sum == 131 > 130!
408    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
409            event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec);
410    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
411    // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
412    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
413            event4->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec);
414
415    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
416    // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
417    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
418            event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec);
419}
420
421}  // namespace statsd
422}  // namespace os
423}  // namespace android
424#else
425GTEST_LOG_(INFO) << "This test does nothing.\n";
426#endif
427