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/EventMetricProducer.h"
16
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <stdio.h>
20
21 #include <vector>
22
23 #include "metrics_test_helper.h"
24 #include "stats_event.h"
25 #include "tests/statsd_test_util.h"
26
27 using namespace testing;
28 using android::sp;
29 using std::set;
30 using std::unordered_map;
31 using std::vector;
32
33 #ifdef __ANDROID__
34
35 namespace android {
36 namespace os {
37 namespace statsd {
38
39
40 namespace {
41 const ConfigKey kConfigKey(0, 12345);
42 const uint64_t protoHash = 0x1234567890;
43
makeLogEvent(LogEvent * logEvent,int32_t atomId,int64_t timestampNs,string str,vector<uint8_t> * bytesField=nullptr)44 void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str,
45 vector<uint8_t>* bytesField = nullptr) {
46 AStatsEvent* statsEvent = AStatsEvent_obtain();
47 AStatsEvent_setAtomId(statsEvent, atomId);
48 AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
49 AStatsEvent_writeString(statsEvent, str.c_str());
50 if (bytesField != nullptr) {
51 AStatsEvent_writeByteArray(statsEvent, bytesField->data(), bytesField->size());
52 }
53
54 parseStatsEventToLogEvent(statsEvent, logEvent);
55 }
56 } // anonymous namespace
57
58 class EventMetricProducerTest : public ::testing::Test {
SetUp()59 void SetUp() override {
60 FlagProvider::getInstance().overrideFuncs(&isAtLeastSFuncTrue);
61 }
62
TearDown()63 void TearDown() override {
64 FlagProvider::getInstance().resetOverrides();
65 }
66 };
67
TEST_F(EventMetricProducerTest,TestNoCondition)68 TEST_F(EventMetricProducerTest, TestNoCondition) {
69 int64_t bucketStartTimeNs = 10000000000;
70 int64_t eventStartTimeNs = bucketStartTimeNs + 1;
71 int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
72
73 EventMetric metric;
74 metric.set_id(1);
75
76 LogEvent event1(/*uid=*/0, /*pid=*/0);
77 CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
78
79 LogEvent event2(/*uid=*/0, /*pid=*/0);
80 CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 2);
81
82 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
83
84 EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
85 wizard, protoHash, bucketStartTimeNs);
86
87 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
88 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
89
90 // Check dump report content.
91 ProtoOutputStream output;
92 std::set<string> strSet;
93 eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
94 true /*erase data*/, FAST, &strSet, &output);
95
96 StatsLogReport report = outputStreamToProto(&output);
97 backfillAggregatedAtoms(&report);
98 EXPECT_TRUE(report.has_event_metrics());
99 ASSERT_EQ(2, report.event_metrics().data_size());
100 EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
101 EXPECT_EQ(bucketStartTimeNs + 2, report.event_metrics().data(1).elapsed_timestamp_nanos());
102 }
103
TEST_F(EventMetricProducerTest,TestEventsWithNonSlicedCondition)104 TEST_F(EventMetricProducerTest, TestEventsWithNonSlicedCondition) {
105 int64_t bucketStartTimeNs = 10000000000;
106 int64_t eventStartTimeNs = bucketStartTimeNs + 1;
107 int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
108
109 EventMetric metric;
110 metric.set_id(1);
111 metric.set_condition(StringToId("SCREEN_ON"));
112
113 LogEvent event1(/*uid=*/0, /*pid=*/0);
114 CreateNoValuesLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1);
115
116 LogEvent event2(/*uid=*/0, /*pid=*/0);
117 CreateNoValuesLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10);
118
119 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
120
121 EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
122 {ConditionState::kUnknown}, wizard, protoHash,
123 bucketStartTimeNs);
124
125 eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
126 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
127
128 eventProducer.onConditionChanged(false /*condition*/, bucketStartTimeNs + 2);
129
130 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
131
132 // Check dump report content.
133 ProtoOutputStream output;
134 std::set<string> strSet;
135 eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
136 true /*erase data*/, FAST, &strSet, &output);
137
138 StatsLogReport report = outputStreamToProto(&output);
139 EXPECT_TRUE(report.has_event_metrics());
140 backfillAggregatedAtoms(&report);
141 ASSERT_EQ(1, report.event_metrics().data_size());
142 EXPECT_EQ(bucketStartTimeNs + 1, report.event_metrics().data(0).elapsed_timestamp_nanos());
143 }
144
TEST_F(EventMetricProducerTest,TestEventsWithSlicedCondition)145 TEST_F(EventMetricProducerTest, TestEventsWithSlicedCondition) {
146 int64_t bucketStartTimeNs = 10000000000;
147 int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
148
149 int tagId = 1;
150 int conditionTagId = 2;
151
152 EventMetric metric;
153 metric.set_id(1);
154 metric.set_condition(StringToId("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON"));
155 MetricConditionLink* link = metric.add_links();
156 link->set_condition(StringToId("APP_IN_BACKGROUND_PER_UID"));
157 buildSimpleAtomFieldMatcher(tagId, 1, link->mutable_fields_in_what());
158 buildSimpleAtomFieldMatcher(conditionTagId, 2, link->mutable_fields_in_condition());
159
160 LogEvent event1(/*uid=*/0, /*pid=*/0);
161 makeLogEvent(&event1, 1 /*tagId*/, bucketStartTimeNs + 1, "111");
162 ConditionKey key1;
163 key1[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
164 getMockedDimensionKey(conditionTagId, 2, "111")};
165
166 LogEvent event2(/*uid=*/0, /*pid=*/0);
167 makeLogEvent(&event2, 1 /*tagId*/, bucketStartTimeNs + 10, "222");
168 ConditionKey key2;
169 key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {
170 getMockedDimensionKey(conditionTagId, 2, "222")};
171
172 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
173 // Condition is false for first event.
174 EXPECT_CALL(*wizard, query(_, key1, _)).WillOnce(Return(ConditionState::kFalse));
175 // Condition is true for second event.
176 EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
177
178 EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
179 {ConditionState::kUnknown}, wizard, protoHash,
180 bucketStartTimeNs);
181
182 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
183 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
184
185 // Check dump report content.
186 ProtoOutputStream output;
187 std::set<string> strSet;
188 eventProducer.onDumpReport(bucketStartTimeNs + 20, true /*include current partial bucket*/,
189 true /*erase data*/, FAST, &strSet, &output);
190
191 StatsLogReport report = outputStreamToProto(&output);
192 backfillAggregatedAtoms(&report);
193 EXPECT_TRUE(report.has_event_metrics());
194 ASSERT_EQ(1, report.event_metrics().data_size());
195 EXPECT_EQ(bucketStartTimeNs + 10, report.event_metrics().data(0).elapsed_timestamp_nanos());
196 }
197
TEST_F(EventMetricProducerTest,TestOneAtomTagAggregatedEvents)198 TEST_F(EventMetricProducerTest, TestOneAtomTagAggregatedEvents) {
199 int64_t bucketStartTimeNs = 10000000000;
200 int tagId = 1;
201
202 EventMetric metric;
203 metric.set_id(1);
204
205 LogEvent event1(/*uid=*/0, /*pid=*/0);
206 makeLogEvent(&event1, tagId, bucketStartTimeNs + 10, "111");
207 LogEvent event2(/*uid=*/0, /*pid=*/0);
208 makeLogEvent(&event2, tagId, bucketStartTimeNs + 20, "111");
209 LogEvent event3(/*uid=*/0, /*pid=*/0);
210 makeLogEvent(&event3, tagId, bucketStartTimeNs + 30, "111");
211
212 LogEvent event4(/*uid=*/0, /*pid=*/0);
213 makeLogEvent(&event4, tagId, bucketStartTimeNs + 40, "222");
214
215 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
216 EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
217 wizard, protoHash, bucketStartTimeNs);
218
219 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
220 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
221 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event3);
222 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event4);
223
224 // Check dump report content.
225 ProtoOutputStream output;
226 std::set<string> strSet;
227 eventProducer.onDumpReport(bucketStartTimeNs + 50, true /*include current partial bucket*/,
228 true /*erase data*/, FAST, &strSet, &output);
229
230 StatsLogReport report = outputStreamToProto(&output);
231 EXPECT_TRUE(report.has_event_metrics());
232 ASSERT_EQ(2, report.event_metrics().data_size());
233
234 for (EventMetricData metricData : report.event_metrics().data()) {
235 AggregatedAtomInfo atomInfo = metricData.aggregated_atom_info();
236 if (atomInfo.elapsed_timestamp_nanos_size() == 1) {
237 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 40);
238 } else if (atomInfo.elapsed_timestamp_nanos_size() == 3) {
239 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 10);
240 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(1), bucketStartTimeNs + 20);
241 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(2), bucketStartTimeNs + 30);
242 } else {
243 FAIL();
244 }
245 }
246 }
247
TEST_F(EventMetricProducerTest,TestBytesFieldAggregatedEvents)248 TEST_F(EventMetricProducerTest, TestBytesFieldAggregatedEvents) {
249 int64_t bucketStartTimeNs = 10000000000;
250 int tagId = 1;
251
252 EventMetric metric;
253 metric.set_id(1);
254
255 vector<uint8_t> bytesField1{10, 20, 30};
256 vector<uint8_t> bytesField2{10, 20, 30, 40};
257 LogEvent event1(/*uid=*/0, /*pid=*/0);
258 makeLogEvent(&event1, tagId, bucketStartTimeNs + 10, "111", &bytesField1);
259 LogEvent event2(/*uid=*/0, /*pid=*/0);
260 makeLogEvent(&event2, tagId, bucketStartTimeNs + 20, "111", &bytesField1);
261 LogEvent event3(/*uid=*/0, /*pid=*/0);
262 makeLogEvent(&event3, tagId, bucketStartTimeNs + 30, "111", &bytesField1);
263
264 LogEvent event4(/*uid=*/0, /*pid=*/0);
265 makeLogEvent(&event4, tagId, bucketStartTimeNs + 40, "111", &bytesField2);
266
267 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
268 EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
269 wizard, protoHash, bucketStartTimeNs);
270
271 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
272 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
273 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event3);
274 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event4);
275
276 // Check dump report content.
277 ProtoOutputStream output;
278 std::set<string> strSet;
279 eventProducer.onDumpReport(bucketStartTimeNs + 50, true /*include current partial bucket*/,
280 true /*erase data*/, FAST, &strSet, &output);
281
282 StatsLogReport report = outputStreamToProto(&output);
283 EXPECT_TRUE(report.has_event_metrics());
284 ASSERT_EQ(2, report.event_metrics().data_size());
285
286 for (EventMetricData metricData : report.event_metrics().data()) {
287 AggregatedAtomInfo atomInfo = metricData.aggregated_atom_info();
288 if (atomInfo.elapsed_timestamp_nanos_size() == 1) {
289 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 40);
290 } else if (atomInfo.elapsed_timestamp_nanos_size() == 3) {
291 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 10);
292 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(1), bucketStartTimeNs + 20);
293 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(2), bucketStartTimeNs + 30);
294 } else {
295 FAIL();
296 }
297 }
298 }
299
TEST_F(EventMetricProducerTest,TestTwoAtomTagAggregatedEvents)300 TEST_F(EventMetricProducerTest, TestTwoAtomTagAggregatedEvents) {
301 int64_t bucketStartTimeNs = 10000000000;
302 int tagId = 1;
303 int tagId2 = 0;
304
305 EventMetric metric;
306 metric.set_id(1);
307
308 LogEvent event1(/*uid=*/0, /*pid=*/0);
309 makeLogEvent(&event1, tagId, bucketStartTimeNs + 10, "111");
310 LogEvent event2(/*uid=*/0, /*pid=*/0);
311 makeLogEvent(&event2, tagId, bucketStartTimeNs + 20, "111");
312
313 LogEvent event3(/*uid=*/0, /*pid=*/0);
314 makeLogEvent(&event3, tagId2, bucketStartTimeNs + 40, "222");
315
316 sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
317 EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
318 wizard, protoHash, bucketStartTimeNs);
319
320 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
321 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
322 eventProducer.onMatchedLogEvent(1 /*matcher index*/, event3);
323
324 // Check dump report content.
325 ProtoOutputStream output;
326 std::set<string> strSet;
327 eventProducer.onDumpReport(bucketStartTimeNs + 50, true /*include current partial bucket*/,
328 true /*erase data*/, FAST, &strSet, &output);
329
330 StatsLogReport report = outputStreamToProto(&output);
331 EXPECT_TRUE(report.has_event_metrics());
332 ASSERT_EQ(2, report.event_metrics().data_size());
333
334 for (EventMetricData metricData : report.event_metrics().data()) {
335 AggregatedAtomInfo atomInfo = metricData.aggregated_atom_info();
336 if (atomInfo.elapsed_timestamp_nanos_size() == 1) {
337 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 40);
338 } else if (atomInfo.elapsed_timestamp_nanos_size() == 2) {
339 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(0), bucketStartTimeNs + 10);
340 EXPECT_EQ(atomInfo.elapsed_timestamp_nanos(1), bucketStartTimeNs + 20);
341 } else {
342 FAIL();
343 }
344 }
345 }
346 } // namespace statsd
347 } // namespace os
348 } // namespace android
349 #else
350 GTEST_LOG_(INFO) << "This test does nothing.\n";
351 #endif
352