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