1 /*
2 * Copyright (C) 2021 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 "PendingRequestPool.h"
18
19 #include <VehicleHalTypes.h>
20 #include <VehicleUtils.h>
21
22 #include <gmock/gmock.h>
23 #include <gtest/gtest.h>
24
25 #include <unordered_set>
26 #include <vector>
27
28 namespace android {
29 namespace hardware {
30 namespace automotive {
31 namespace vehicle {
32
33 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
34
35 using ::testing::ElementsAre;
36 using ::testing::UnorderedElementsAre;
37 using ::testing::WhenSorted;
38
39 class PendingRequestPoolTest : public ::testing::Test {
40 public:
SetUp()41 void SetUp() override { mPool = std::make_unique<PendingRequestPool>(TEST_TIMEOUT); }
42
TearDown()43 void TearDown() override {
44 if (mPool != nullptr) {
45 ASSERT_EQ(mPool->countPendingRequests(getTestClientId()), static_cast<size_t>(0))
46 << "at least one pending request still exists in the pool when finish";
47 }
48 }
49
getPool()50 PendingRequestPool* getPool() { return mPool.get(); }
51
destroyPool()52 void destroyPool() { mPool.reset(); }
53
getTimeout()54 int64_t getTimeout() { return TEST_TIMEOUT; }
55
getTestClientId()56 const void* getTestClientId() { return reinterpret_cast<const void*>(0); }
57
58 private:
59 // Test timeout is 0.1s.
60 static const int64_t TEST_TIMEOUT = 100000000;
61
62 std::unique_ptr<PendingRequestPool> mPool;
63 };
64
TEST_F(PendingRequestPoolTest,testFinishAllRequests)65 TEST_F(PendingRequestPoolTest, testFinishAllRequests) {
66 std::mutex lock;
67 std::vector<int64_t> timeoutRequestIds;
68
69 std::unordered_set<int64_t> requestIds;
70 for (int64_t i = 0; i < 10; i++) {
71 requestIds.insert(i);
72 }
73
74 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
75 [&lock, &timeoutRequestIds](const std::unordered_set<int64_t>& requests) {
76 std::scoped_lock<std::mutex> lockGuard(lock);
77 for (int64_t request : requests) {
78 timeoutRequestIds.push_back(request);
79 }
80 });
81
82 ASSERT_RESULT_OK(getPool()->addRequests(getTestClientId(), requestIds, callback));
83
84 for (int64_t i = 0; i < 10; i++) {
85 ASSERT_TRUE(getPool()->isRequestPending(getTestClientId(), i));
86 }
87
88 for (int64_t i = 0; i < 10; i++) {
89 ASSERT_THAT(getPool()->tryFinishRequests(getTestClientId(), {i}), UnorderedElementsAre(i));
90 }
91
92 for (int64_t i = 0; i < 10; i++) {
93 ASSERT_FALSE(getPool()->isRequestPending(getTestClientId(), i));
94 }
95 }
96
TEST_F(PendingRequestPoolTest,testFinishHalfOfRequest)97 TEST_F(PendingRequestPoolTest, testFinishHalfOfRequest) {
98 int64_t timeout = getTimeout();
99 std::mutex lock;
100 std::vector<int64_t> timeoutRequestIds;
101
102 std::unordered_set<int64_t> requestIds;
103 for (int64_t i = 0; i < 10; i++) {
104 requestIds.insert(i);
105 }
106
107 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
108 [&lock, &timeoutRequestIds](const std::unordered_set<int64_t>& requests) {
109 std::scoped_lock<std::mutex> lockGuard(lock);
110 for (int64_t request : requests) {
111 timeoutRequestIds.push_back(request);
112 }
113 });
114
115 ASSERT_RESULT_OK(getPool()->addRequests(getTestClientId(), requestIds, callback));
116
117 for (int64_t i = 0; i < 10; i++) {
118 ASSERT_TRUE(getPool()->isRequestPending(getTestClientId(), i));
119 }
120
121 // Finish half of the requests.
122 requestIds.clear();
123 for (int64_t i = 0; i < 5; i++) {
124 requestIds.insert(i);
125 }
126
127 ASSERT_EQ(getPool()->tryFinishRequests(getTestClientId(), requestIds), requestIds);
128
129 for (int64_t i = 0; i < 5; i++) {
130 ASSERT_FALSE(getPool()->isRequestPending(getTestClientId(), i));
131 }
132 for (int64_t i = 5; i < 10; i++) {
133 ASSERT_TRUE(getPool()->isRequestPending(getTestClientId(), i));
134 }
135
136 // Wait until the unfinished requests timeout. The check interval is timeout, so at max we
137 // would wait an additional interval, which is 2 * timeout until the callback is called.
138 std::this_thread::sleep_for(2 * std::chrono::nanoseconds(timeout));
139
140 ASSERT_THAT(timeoutRequestIds, WhenSorted(ElementsAre(5, 6, 7, 8, 9)));
141 }
142
TEST_F(PendingRequestPoolTest,testFinishRequestTwice)143 TEST_F(PendingRequestPoolTest, testFinishRequestTwice) {
144 std::mutex lock;
145 std::vector<int64_t> timeoutRequestIds;
146
147 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
148 [&lock, &timeoutRequestIds](const std::unordered_set<int64_t>& requests) {
149 std::scoped_lock<std::mutex> lockGuard(lock);
150 for (int64_t request : requests) {
151 timeoutRequestIds.push_back(request);
152 }
153 });
154
155 ASSERT_RESULT_OK(getPool()->addRequests(getTestClientId(), {0}, callback));
156
157 ASSERT_THAT(getPool()->tryFinishRequests(getTestClientId(), {0}), UnorderedElementsAre(0))
158 << "failed to finish an added request";
159 ASSERT_TRUE(getPool()->tryFinishRequests(getTestClientId(), {0}).empty())
160 << "finish a request second time must return empty result";
161 }
162
TEST_F(PendingRequestPoolTest,testFinishRequestNonExistingId)163 TEST_F(PendingRequestPoolTest, testFinishRequestNonExistingId) {
164 std::mutex lock;
165 std::vector<int64_t> timeoutRequestIds;
166
167 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
168 [&lock, &timeoutRequestIds](const std::unordered_set<int64_t>& requests) {
169 std::scoped_lock<std::mutex> lockGuard(lock);
170 for (int64_t request : requests) {
171 timeoutRequestIds.push_back(request);
172 }
173 });
174
175 ASSERT_RESULT_OK(getPool()->addRequests(getTestClientId(), {0, 1, 2}, callback));
176
177 ASSERT_THAT(getPool()->tryFinishRequests(getTestClientId(), {0, 1, 2, 3}),
178 UnorderedElementsAre(0, 1, 2))
179 << "finished request IDs must not contain non-existing request ID";
180 // Even though one of the request to finish does not exist, the rest of the requests should be
181 // finished.
182 ASSERT_EQ(getPool()->countPendingRequests(getTestClientId()), static_cast<size_t>(0))
183 << "requests not being finished correctly";
184 }
185
TEST_F(PendingRequestPoolTest,testFinishAfterTimeout)186 TEST_F(PendingRequestPoolTest, testFinishAfterTimeout) {
187 std::mutex lock;
188 std::vector<int64_t> timeoutRequestIds;
189
190 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
191 [&lock, &timeoutRequestIds](const std::unordered_set<int64_t>& requests) {
192 std::scoped_lock<std::mutex> lockGuard(lock);
193 for (int64_t request : requests) {
194 timeoutRequestIds.push_back(request);
195 }
196 });
197
198 ASSERT_RESULT_OK(getPool()->addRequests(getTestClientId(), {0}, callback));
199
200 std::this_thread::sleep_for(2 * std::chrono::nanoseconds(getTimeout()));
201
202 ASSERT_TRUE(getPool()->tryFinishRequests(getTestClientId(), {0}).empty())
203 << "finish a request after timeout must do nothing";
204 }
205
TEST_F(PendingRequestPoolTest,testDestroyWithPendingRequests)206 TEST_F(PendingRequestPoolTest, testDestroyWithPendingRequests) {
207 std::mutex lock;
208 std::vector<int64_t> timeoutRequestIds;
209
210 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
211 [&lock, &timeoutRequestIds](const std::unordered_set<int64_t>& requests) {
212 std::scoped_lock<std::mutex> lockGuard(lock);
213 for (int64_t request : requests) {
214 timeoutRequestIds.push_back(request);
215 }
216 });
217
218 ASSERT_RESULT_OK(getPool()->addRequests(getTestClientId(), {0}, callback));
219
220 destroyPool();
221
222 // Before the pool is destroyed, the pending requests should be notified as timeout.
223 ASSERT_THAT(timeoutRequestIds, UnorderedElementsAre(0))
224 << "timeout not triggered when the pool is destroyed";
225 }
226
TEST_F(PendingRequestPoolTest,testDuplicateRequestId)227 TEST_F(PendingRequestPoolTest, testDuplicateRequestId) {
228 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
229 [](std::unordered_set<int64_t>) {});
230
231 ASSERT_RESULT_OK(getPool()->addRequests(getTestClientId(), {0}, callback));
232 ASSERT_FALSE(getPool()->addRequests(getTestClientId(), {1, 2, 0}, callback).ok())
233 << "adding duplicate request IDs must fail";
234
235 ASSERT_THAT(getPool()->tryFinishRequests(getTestClientId(), {0}), UnorderedElementsAre(0));
236 }
237
TEST_F(PendingRequestPoolTest,testSameRequestIdForDifferentClient)238 TEST_F(PendingRequestPoolTest, testSameRequestIdForDifferentClient) {
239 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
240 [](std::unordered_set<int64_t>) {});
241
242 ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<const void*>(0), {0}, callback));
243 ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<const void*>(1), {1, 2, 0}, callback));
244
245 ASSERT_THAT(getPool()->tryFinishRequests(reinterpret_cast<const void*>(0), {0}),
246 UnorderedElementsAre(0));
247 ASSERT_THAT(getPool()->tryFinishRequests(reinterpret_cast<const void*>(1), {1, 2, 0}),
248 UnorderedElementsAre(0, 1, 2));
249 }
250
TEST_F(PendingRequestPoolTest,testPendingRequestCountLimit)251 TEST_F(PendingRequestPoolTest, testPendingRequestCountLimit) {
252 auto callback = std::make_shared<PendingRequestPool::TimeoutCallbackFunc>(
253 [](std::unordered_set<int64_t>) {});
254
255 std::unordered_set<int64_t> requests;
256
257 // MAX_PENDING_REQUEST_PER_CLIENT = 10000
258 for (size_t i = 0; i < 10000; i++) {
259 requests.insert(static_cast<int64_t>(i));
260 }
261 ASSERT_RESULT_OK(getPool()->addRequests(reinterpret_cast<const void*>(0), requests, callback));
262
263 auto result = getPool()->addRequests(reinterpret_cast<const void*>(0),
264 {static_cast<int64_t>(10000)}, callback);
265 ASSERT_FALSE(result.ok()) << "adding more pending requests than limit must fail";
266 ASSERT_EQ(result.error().code(), StatusCode::TRY_AGAIN);
267
268 getPool()->tryFinishRequests(reinterpret_cast<const void*>(0), requests);
269 }
270
271 } // namespace vehicle
272 } // namespace automotive
273 } // namespace hardware
274 } // namespace android
275