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 
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 
20 #include <chrono>
21 
22 #include "stats_buffer_writer_queue_impl.h"
23 #include "stats_event.h"
24 #include "utils.h"
25 
26 using testing::_;
27 using testing::AnyNumber;
28 using testing::DoAll;
29 using testing::Return;
30 using testing::StrictMock;
31 
32 namespace {
33 
34 constexpr static int WAIT_MS = 100;
35 
generateTestEvent()36 static AStatsEvent* generateTestEvent() {
37     AStatsEvent* event = AStatsEvent_obtain();
38     AStatsEvent_setAtomId(event, 100);
39     AStatsEvent_writeInt32(event, 5);
40     AStatsEvent_write(event);
41     return event;
42 }
43 
44 class BasicBufferWriterQueueMock : public BufferWriterQueue {
45 public:
46     BasicBufferWriterQueueMock() = default;
47     MOCK_METHOD(bool, handleCommand, (const BufferWriterQueue::Cmd& cmd), (const override));
48 };
49 
50 typedef StrictMock<BasicBufferWriterQueueMock> BufferWriterQueueMock;
51 
52 }  // namespace
53 
TEST(StatsBufferWriterQueueTest,TestWriteSuccess)54 TEST(StatsBufferWriterQueueTest, TestWriteSuccess) {
55     AStatsEvent* event = generateTestEvent();
56 
57     size_t eventBufferSize = 0;
58     const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
59     EXPECT_GE(eventBufferSize, 0);
60     EXPECT_TRUE(buffer != nullptr);
61 
62     const uint32_t atomId = AStatsEvent_getAtomId(event);
63 
64     BufferWriterQueueMock queue;
65     EXPECT_CALL(queue, handleCommand(_)).WillOnce(Return(true));
66     // simulate failed write to stats socket
67     const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
68     AStatsEvent_release(event);
69     EXPECT_TRUE(addedToQueue);
70     // to yeld to the queue worker thread
71     std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MS));
72 
73     queue.drainQueue();
74     EXPECT_EQ(queue.getQueueSize(), 0);
75 }
76 
TEST(StatsBufferWriterQueueTest,TestWriteOverflow)77 TEST(StatsBufferWriterQueueTest, TestWriteOverflow) {
78     AStatsEvent* event = generateTestEvent();
79 
80     size_t eventBufferSize = 0;
81     const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
82     EXPECT_GE(eventBufferSize, 0);
83     EXPECT_TRUE(buffer != nullptr);
84 
85     const uint32_t atomId = AStatsEvent_getAtomId(event);
86 
87     BufferWriterQueueMock queue;
88     EXPECT_CALL(queue, handleCommand(_)).WillRepeatedly(Return(false));
89     // simulate failed write to stats socket
90     for (int i = 0; i < BufferWriterQueueMock::kQueueMaxSizeLimit; i++) {
91         const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
92         EXPECT_TRUE(addedToQueue);
93     }
94 
95     const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
96     AStatsEvent_release(event);
97     EXPECT_FALSE(addedToQueue);
98 
99     EXPECT_EQ(queue.getQueueSize(), BufferWriterQueueMock::kQueueMaxSizeLimit);
100 
101     queue.drainQueue();
102     EXPECT_EQ(queue.getQueueSize(), 0);
103 }
104 
TEST(StatsBufferWriterQueueTest,TestSleepOnOverflow)105 TEST(StatsBufferWriterQueueTest, TestSleepOnOverflow) {
106     AStatsEvent* event = generateTestEvent();
107 
108     size_t eventBufferSize = 0;
109     const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
110     EXPECT_GE(eventBufferSize, 0);
111     EXPECT_TRUE(buffer != nullptr);
112 
113     const uint32_t atomId = AStatsEvent_getAtomId(event);
114 
115     std::vector<int64_t> attemptsTs;
116 
117     BufferWriterQueueMock queue;
118     ON_CALL(queue, handleCommand(_))
119             .WillByDefault(DoAll(
120                     [&attemptsTs](const BufferWriterQueue::Cmd&) {
121                         // store timestamp for command handler invocations
122                         attemptsTs.push_back(get_elapsed_realtime_ns());
123                         return false;
124                     },
125                     Return(false)));
126 
127     EXPECT_CALL(queue, handleCommand(_)).Times(AnyNumber());
128 
129     // simulate failed write to stats socket to fill the queue
130     for (int i = 0; i < BufferWriterQueueMock::kQueueMaxSizeLimit; i++) {
131         const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
132         EXPECT_TRUE(addedToQueue);
133     }
134     AStatsEvent_release(event);
135     // to yeld to the queue worker thread
136     std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_MS));
137 
138     // to eliminate extra commands handling on the worker thread
139     queue.drainQueue();
140     EXPECT_EQ(queue.getQueueSize(), 0);
141 
142     EXPECT_GE(attemptsTs.size(), 2);
143     for (int i = 0; i < attemptsTs.size() - 1; i++) {
144         EXPECT_GE(attemptsTs[i + 1] - attemptsTs[i],
145                   BufferWriterQueueMock::kDelayOnFailedWriteMs * 1000000);
146     }
147 }
148 
TEST(StatsBufferWriterQueueTest,TestTerminateNonEmptyQueue)149 TEST(StatsBufferWriterQueueTest, TestTerminateNonEmptyQueue) {
150     AStatsEvent* event = generateTestEvent();
151 
152     size_t eventBufferSize = 0;
153     const uint8_t* buffer = AStatsEvent_getBuffer(event, &eventBufferSize);
154     EXPECT_GE(eventBufferSize, 0);
155     EXPECT_TRUE(buffer != nullptr);
156 
157     const uint32_t atomId = AStatsEvent_getAtomId(event);
158 
159     BufferWriterQueueMock queue;
160     EXPECT_CALL(queue, handleCommand(_)).WillRepeatedly(Return(false));
161     // simulate failed write to stats socket
162     for (int i = 0; i < BufferWriterQueueMock::kQueueMaxSizeLimit; i++) {
163         const bool addedToQueue = queue.write(buffer, eventBufferSize, atomId);
164         EXPECT_TRUE(addedToQueue);
165     }
166     AStatsEvent_release(event);
167     EXPECT_EQ(queue.getQueueSize(), BufferWriterQueueMock::kQueueMaxSizeLimit);
168     queue.drainQueue();
169     EXPECT_EQ(queue.getQueueSize(), 0);
170 }
171