1 /*
2 * Copyright (C) 2023, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include <gtest/gtest.h>
17
18 #include "socket/StatsSocketListener.h"
19 #include "tests/statsd_test_util.h"
20
21 #ifdef __ANDROID__
22
23 namespace android {
24 namespace os {
25 namespace statsd {
26
27 namespace {
28
29 constexpr uint32_t kTestUid = 1001;
30 constexpr uint32_t kTestPid = 1002;
31 constexpr int kEventCount = 1000;
32 constexpr int kEventFilteredCount = 500;
33 constexpr int kAtomId = 1000;
34
35 class AStatsEventWrapper final {
36 AStatsEvent* statsEvent = nullptr;
37
38 public:
AStatsEventWrapper(int atomId)39 AStatsEventWrapper(int atomId) {
40 statsEvent = AStatsEvent_obtain();
41 createStatsEvent(statsEvent, INT64_TYPE, /*atomId=*/atomId);
42 AStatsEvent_build(statsEvent);
43 }
44
getBuffer() const45 std::pair<const uint8_t*, size_t> getBuffer() const {
46 size_t size;
47 const uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
48 return std::make_pair(buf, size);
49 }
50
~AStatsEventWrapper()51 ~AStatsEventWrapper() {
52 AStatsEvent_release(statsEvent);
53 }
54 };
55
56 } // namespace
57
generateAtomLogging(LogEventQueue & queue,const LogEventFilter & filter,int eventCount,int startAtomId)58 void generateAtomLogging(LogEventQueue& queue, const LogEventFilter& filter, int eventCount,
59 int startAtomId) {
60 // create number of AStatsEvent
61 for (int i = 0; i < eventCount; i++) {
62 AStatsEventWrapper event(startAtomId + i);
63 auto [buf, size] = event.getBuffer();
64 StatsSocketListener::processStatsEventBuffer(buf, size, kTestUid, kTestPid, queue, filter);
65 }
66 }
67
68 class SocketParseMessageTest : public testing::TestWithParam<bool> {
69 protected:
70 LogEventQueue mEventQueue;
71 LogEventFilter mLogEventFilter;
72
73 public:
SocketParseMessageTest()74 SocketParseMessageTest() : mEventQueue(kEventCount /*buffer limit*/) {
75 mLogEventFilter.setFilteringEnabled(GetParam());
76 }
77
ToString(testing::TestParamInfo<bool> info)78 static std::string ToString(testing::TestParamInfo<bool> info) {
79 return info.param ? "WithEventFilter" : "NoEventFilter";
80 }
81 };
82
83 INSTANTIATE_TEST_SUITE_P(SocketParseMessageTest, SocketParseMessageTest, testing::Bool(),
84 SocketParseMessageTest::ToString);
85
TEST_P(SocketParseMessageTest,TestProcessMessage)86 TEST_P(SocketParseMessageTest, TestProcessMessage) {
87 StatsdStats::getInstance().reset();
88
89 generateAtomLogging(mEventQueue, mLogEventFilter, kEventCount, kAtomId);
90
91 int64_t lastEventTs = 0;
92 // check content of the queue
93 EXPECT_EQ(kEventCount, mEventQueue.mQueue.size());
94 for (int i = 0; i < kEventCount; i++) {
95 auto logEvent = mEventQueue.waitPop();
96 EXPECT_TRUE(logEvent->isValid());
97 EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
98 EXPECT_EQ(logEvent->isParsedHeaderOnly(), GetParam());
99 lastEventTs = logEvent->GetElapsedTimestampNs();
100 }
101
102 EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, kEventCount);
103 EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos, lastEventTs);
104 }
105
TEST_P(SocketParseMessageTest,TestProcessMessageEmptySetExplicitSet)106 TEST_P(SocketParseMessageTest, TestProcessMessageEmptySetExplicitSet) {
107 LogEventFilter::AtomIdSet idsList;
108 mLogEventFilter.setAtomIds(idsList, nullptr);
109 generateAtomLogging(mEventQueue, mLogEventFilter, kEventCount, kAtomId);
110
111 // check content of the queue
112 EXPECT_EQ(kEventCount, mEventQueue.mQueue.size());
113 for (int i = 0; i < kEventCount; i++) {
114 auto logEvent = mEventQueue.waitPop();
115 EXPECT_TRUE(logEvent->isValid());
116 EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
117 EXPECT_EQ(logEvent->isParsedHeaderOnly(), GetParam());
118 }
119 }
120
TEST(SocketParseMessageTest,TestProcessMessageFilterCompleteSet)121 TEST(SocketParseMessageTest, TestProcessMessageFilterCompleteSet) {
122 LogEventQueue eventQueue(kEventCount /*buffer limit*/);
123
124 LogEventFilter logEventFilter;
125
126 LogEventFilter::AtomIdSet idsList;
127 for (int i = 0; i < kEventCount; i++) {
128 idsList.insert(kAtomId + i);
129 }
130 logEventFilter.setAtomIds(idsList, nullptr);
131
132 generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
133
134 // check content of the queue
135 EXPECT_EQ(kEventCount, eventQueue.mQueue.size());
136 for (int i = 0; i < kEventCount; i++) {
137 auto logEvent = eventQueue.waitPop();
138 EXPECT_TRUE(logEvent->isValid());
139 EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
140 EXPECT_FALSE(logEvent->isParsedHeaderOnly());
141 }
142 }
143
TEST(SocketParseMessageTest,TestProcessMessageFilterPartialSet)144 TEST(SocketParseMessageTest, TestProcessMessageFilterPartialSet) {
145 LogEventQueue eventQueue(kEventCount /*buffer limit*/);
146
147 LogEventFilter logEventFilter;
148 logEventFilter.setFilteringEnabled(true);
149
150 LogEventFilter::AtomIdSet idsList;
151 for (int i = 0; i < kEventFilteredCount; i++) {
152 idsList.insert(kAtomId + i);
153 }
154 logEventFilter.setAtomIds(idsList, nullptr);
155
156 generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
157
158 // check content of the queue
159 EXPECT_EQ(kEventCount, eventQueue.mQueue.size());
160 for (int i = 0; i < kEventFilteredCount; i++) {
161 auto logEvent = eventQueue.waitPop();
162 EXPECT_TRUE(logEvent->isValid());
163 EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
164 EXPECT_FALSE(logEvent->isParsedHeaderOnly());
165 }
166
167 for (int i = kEventFilteredCount; i < kEventCount; i++) {
168 auto logEvent = eventQueue.waitPop();
169 EXPECT_TRUE(logEvent->isValid());
170 EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
171 EXPECT_TRUE(logEvent->isParsedHeaderOnly());
172 }
173 }
174
TEST(SocketParseMessageTest,TestProcessMessageFilterToggle)175 TEST(SocketParseMessageTest, TestProcessMessageFilterToggle) {
176 LogEventQueue eventQueue(kEventCount * 3 /*buffer limit*/);
177
178 LogEventFilter logEventFilter;
179 logEventFilter.setFilteringEnabled(true);
180
181 LogEventFilter::AtomIdSet idsList;
182 for (int i = 0; i < kEventFilteredCount; i++) {
183 idsList.insert(kAtomId + i);
184 }
185 // events with ids from kAtomId to kAtomId + kEventFilteredCount should not be skipped
186 logEventFilter.setAtomIds(idsList, nullptr);
187
188 generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId);
189
190 logEventFilter.setFilteringEnabled(false);
191 // since filtering is disabled - events with any ids should not be skipped
192 // will generate events with ids [kAtomId + kEventCount, kAtomId + kEventCount * 2]
193 generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId + kEventCount);
194
195 logEventFilter.setFilteringEnabled(true);
196 LogEventFilter::AtomIdSet idsList2;
197 for (int i = kEventFilteredCount; i < kEventCount; i++) {
198 idsList2.insert(kAtomId + kEventCount * 2 + i);
199 }
200 // events with idsList2 ids should not be skipped
201 logEventFilter.setAtomIds(idsList2, nullptr);
202
203 // will generate events with ids [kAtomId + kEventCount * 2, kAtomId + kEventCount * 3]
204 generateAtomLogging(eventQueue, logEventFilter, kEventCount, kAtomId + kEventCount * 2);
205
206 // check content of the queue
207 EXPECT_EQ(kEventCount * 3, eventQueue.mQueue.size());
208 // events with ids from kAtomId to kAtomId + kEventFilteredCount should not be skipped
209 for (int i = 0; i < kEventFilteredCount; i++) {
210 auto logEvent = eventQueue.waitPop();
211 EXPECT_TRUE(logEvent->isValid());
212 EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
213 EXPECT_FALSE(logEvent->isParsedHeaderOnly());
214 }
215
216 // all events above kAtomId + kEventFilteredCount to kAtomId + kEventCount should be skipped
217 for (int i = kEventFilteredCount; i < kEventCount; i++) {
218 auto logEvent = eventQueue.waitPop();
219 EXPECT_TRUE(logEvent->isValid());
220 EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
221 EXPECT_TRUE(logEvent->isParsedHeaderOnly());
222 }
223
224 // events with ids [kAtomId + kEventCount, kAtomId + kEventCount * 2] should not be skipped
225 // since wiltering was disabled at that time
226 for (int i = 0; i < kEventCount; i++) {
227 auto logEvent = eventQueue.waitPop();
228 EXPECT_TRUE(logEvent->isValid());
229 EXPECT_EQ(kAtomId + kEventCount + i, logEvent->GetTagId());
230 EXPECT_FALSE(logEvent->isParsedHeaderOnly());
231 }
232
233 // first half events with ids [kAtomId + kEventCount * 2, kAtomId + kEventCount * 3]
234 // should be skipped
235 for (int i = 0; i < kEventFilteredCount; i++) {
236 auto logEvent = eventQueue.waitPop();
237 EXPECT_TRUE(logEvent->isValid());
238 EXPECT_EQ(kAtomId + kEventCount * 2 + i, logEvent->GetTagId());
239 EXPECT_TRUE(logEvent->isParsedHeaderOnly());
240 }
241
242 // second half events with ids [kAtomId + kEventCount * 2, kAtomId + kEventCount * 3]
243 // should be processed
244 for (int i = kEventFilteredCount; i < kEventCount; i++) {
245 auto logEvent = eventQueue.waitPop();
246 EXPECT_TRUE(logEvent->isValid());
247 EXPECT_EQ(kAtomId + kEventCount * 2 + i, logEvent->GetTagId());
248 EXPECT_FALSE(logEvent->isParsedHeaderOnly());
249 }
250 }
251
252 // TODO: tests for setAtomIds() with multiple consumers
253 // TODO: use MockLogEventFilter to test different sets from different consumers
254
255 } // namespace statsd
256 } // namespace os
257 } // namespace android
258 #else
259 GTEST_LOG_(INFO) << "This test does nothing.\n";
260 #endif
261