• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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