ValueMetricProducer_test.cpp revision 9369446f0b04945d6674550728ae81196d6fb5c2
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;
47
48/*
49 * Tests pulled atoms with no conditions
50 */
51TEST(ValueMetricProducerTest, TestNonDimensionalEvents) {
52    ValueMetric metric;
53    metric.set_id(metricId);
54    metric.set_bucket(ONE_MINUTE);
55    metric.mutable_value_field()->set_field(tagId);
56    metric.mutable_value_field()->add_child()->set_field(2);
57
58    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
59    // TODO: pending refactor of StatsPullerManager
60    // For now we still need this so that it doesn't do real pulling.
61    shared_ptr<MockStatsPullerManager> pullerManager =
62            make_shared<StrictMock<MockStatsPullerManager>>();
63    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
64    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
65
66    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
67                                      tagId, bucketStartTimeNs, pullerManager);
68
69    vector<shared_ptr<LogEvent>> allData;
70    allData.clear();
71    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
72    event->write(tagId);
73    event->write(11);
74    event->init();
75    allData.push_back(event);
76
77    valueProducer.onDataPulled(allData);
78    // has one slice
79    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
80    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
81    // startUpdated:true tainted:0 sum:0 start:11
82    EXPECT_EQ(true, curInterval.startUpdated);
83    EXPECT_EQ(0, curInterval.tainted);
84    EXPECT_EQ(0, curInterval.sum);
85    EXPECT_EQ(11, curInterval.start);
86    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
87    EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue);
88
89    allData.clear();
90    event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1);
91    event->write(tagId);
92    event->write(23);
93    event->init();
94    allData.push_back(event);
95    valueProducer.onDataPulled(allData);
96    // has one slice
97    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
98    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
99    // tartUpdated:false tainted:0 sum:12
100    EXPECT_EQ(true, curInterval.startUpdated);
101    EXPECT_EQ(0, curInterval.tainted);
102    EXPECT_EQ(0, curInterval.sum);
103    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
104    EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size());
105    EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue);
106
107    allData.clear();
108    event = make_shared<LogEvent>(tagId, bucket4StartTimeNs + 1);
109    event->write(tagId);
110    event->write(36);
111    event->init();
112    allData.push_back(event);
113    valueProducer.onDataPulled(allData);
114    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
115    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
116    // startUpdated:false tainted:0 sum:12
117    EXPECT_EQ(true, curInterval.startUpdated);
118    EXPECT_EQ(0, curInterval.tainted);
119    EXPECT_EQ(0, curInterval.sum);
120    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
121    EXPECT_EQ(3UL, valueProducer.mPastBuckets.begin()->second.size());
122    EXPECT_EQ(13, valueProducer.mPastBuckets.begin()->second.back().mValue);
123}
124
125/*
126 * Test pulled event with non sliced condition.
127 */
128TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition) {
129    ValueMetric metric;
130    metric.set_id(metricId);
131    metric.set_bucket(ONE_MINUTE);
132    metric.mutable_value_field()->set_field(tagId);
133    metric.mutable_value_field()->add_child()->set_field(2);
134    metric.set_condition(StringToId("SCREEN_ON"));
135
136    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
137    shared_ptr<MockStatsPullerManager> pullerManager =
138            make_shared<StrictMock<MockStatsPullerManager>>();
139    EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
140    EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
141
142    EXPECT_CALL(*pullerManager, Pull(tagId, _))
143            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
144                data->clear();
145                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
146                event->write(tagId);
147                event->write(100);
148                event->init();
149                data->push_back(event);
150                return true;
151            }))
152            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
153                data->clear();
154                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
155                event->write(tagId);
156                event->write(120);
157                event->init();
158                data->push_back(event);
159                return true;
160            }));
161
162    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
163                                      pullerManager);
164
165    valueProducer.onConditionChanged(true, bucketStartTimeNs + 8);
166
167    // has one slice
168    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
169    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
170    // startUpdated:false tainted:0 sum:0 start:100
171    EXPECT_EQ(100, curInterval.start);
172    EXPECT_EQ(true, curInterval.startUpdated);
173    EXPECT_EQ(0, curInterval.tainted);
174    EXPECT_EQ(0, curInterval.sum);
175    EXPECT_EQ(0UL, valueProducer.mPastBuckets.size());
176
177    vector<shared_ptr<LogEvent>> allData;
178    allData.clear();
179    shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
180    event->write(1);
181    event->write(110);
182    event->init();
183    allData.push_back(event);
184    valueProducer.onDataPulled(allData);
185
186    // has one slice
187    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
188    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
189    // startUpdated:false tainted:0 sum:0 start:110
190    EXPECT_EQ(110, curInterval.start);
191    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
192    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
193    EXPECT_EQ(10, valueProducer.mPastBuckets.begin()->second.back().mValue);
194
195    valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1);
196
197    // has one slice
198    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
199    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
200    // startUpdated:false tainted:0 sum:0 start:110
201    EXPECT_EQ(10, curInterval.sum);
202    EXPECT_EQ(false, curInterval.startUpdated);
203}
204
205TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
206    ValueMetric metric;
207    metric.set_id(metricId);
208    metric.set_bucket(ONE_MINUTE);
209    metric.mutable_value_field()->set_field(tagId);
210    metric.mutable_value_field()->add_child()->set_field(2);
211
212    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
213    shared_ptr<MockStatsPullerManager> pullerManager =
214            make_shared<StrictMock<MockStatsPullerManager>>();
215
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    shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 20);
224    event2->write(1);
225    event2->write(20);
226    event2->init();
227    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
228    // has one slice
229    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
230    ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
231    EXPECT_EQ(10, curInterval.sum);
232
233    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
234
235    // has one slice
236    EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
237    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second;
238    EXPECT_EQ(30, curInterval.sum);
239
240    valueProducer.flushIfNeededLocked(bucket3StartTimeNs);
241    EXPECT_EQ(1UL, valueProducer.mPastBuckets.size());
242    EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size());
243    EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
244}
245
246TEST(ValueMetricProducerTest, TestAnomalyDetection) {
247    Alert alert;
248    alert.set_id(101);
249    alert.set_metric_id(metricId);
250    alert.set_trigger_if_sum_gt(130);
251    alert.set_num_buckets(2);
252    const int32_t refPeriodSec = 3;
253    alert.set_refractory_period_secs(refPeriodSec);
254
255    ValueMetric metric;
256    metric.set_id(metricId);
257    metric.set_bucket(ONE_MINUTE);
258    metric.mutable_value_field()->set_field(tagId);
259    metric.mutable_value_field()->add_child()->set_field(2);
260
261    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
262    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
263                                      -1 /*not pulled*/, bucketStartTimeNs);
264    sp<AnomalyTracker> anomalyTracker = valueProducer.addAnomalyTracker(alert);
265
266
267    shared_ptr<LogEvent> event1
268            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC);
269    event1->write(161);
270    event1->write(10); // value of interest
271    event1->init();
272    shared_ptr<LogEvent> event2
273            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC);
274    event2->write(162);
275    event2->write(20); // value of interest
276    event2->init();
277    shared_ptr<LogEvent> event3
278            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC);
279    event3->write(163);
280    event3->write(130); // value of interest
281    event3->init();
282    shared_ptr<LogEvent> event4
283            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC);
284    event4->write(35);
285    event4->write(1); // value of interest
286    event4->init();
287    shared_ptr<LogEvent> event5
288            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
289    event5->write(45);
290    event5->write(150); // value of interest
291    event5->init();
292    shared_ptr<LogEvent> event6
293            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
294    event6->write(25);
295    event6->write(160); // value of interest
296    event6->init();
297
298    // Two events in bucket #0.
299    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
300    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
301    // Value sum == 30 <= 130.
302    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
303
304    // One event in bucket #2. No alarm as bucket #0 is trashed out.
305    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
306    // Value sum == 130 <= 130.
307    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
308
309    // Three events in bucket #3.
310    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
311    // Anomaly at event 4 since Value sum == 131 > 130!
312    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
313            event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
314    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
315    // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
316    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
317            event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
318
319    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
320    // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
321    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
322            event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
323}
324
325}  // namespace statsd
326}  // namespace os
327}  // namespace android
328#else
329GTEST_LOG_(INFO) << "This test does nothing.\n";
330#endif
331