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 <thread>
18
19 #include <gtest/gtest.h>
20
21 #include <utils/SystemClock.h>
22
23 #include <VehicleHalTypes.h>
24 #include <VehicleObjectPool.h>
25 #include <VehicleUtils.h>
26
27 namespace android {
28 namespace hardware {
29 namespace automotive {
30 namespace vehicle {
31
32 namespace {
33
34 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
35 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
36 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
37
38 struct TestPropertyTypeInfo {
39 VehiclePropertyType type;
40 bool recyclable;
41 size_t vecSize;
42 };
43
getAllPropertyTypes()44 std::vector<TestPropertyTypeInfo> getAllPropertyTypes() {
45 return {
46 {
47 .type = VehiclePropertyType::INT32,
48 .recyclable = true,
49 .vecSize = 1,
50 },
51 {
52 .type = VehiclePropertyType::INT64,
53 .recyclable = true,
54 .vecSize = 1,
55 },
56 {
57 .type = VehiclePropertyType::FLOAT,
58 .recyclable = true,
59 .vecSize = 1,
60 },
61 {
62 .type = VehiclePropertyType::INT32_VEC,
63 .recyclable = true,
64 .vecSize = 4,
65 },
66 {
67 .type = VehiclePropertyType::INT64_VEC,
68 .recyclable = true,
69 .vecSize = 4,
70 },
71 {
72 .type = VehiclePropertyType::FLOAT_VEC,
73 .recyclable = true,
74 .vecSize = 4,
75 },
76 {
77 .type = VehiclePropertyType::BYTES,
78 .recyclable = true,
79 .vecSize = 4,
80 },
81 {
82 .type = VehiclePropertyType::INT32_VEC,
83 .recyclable = false,
84 .vecSize = 5,
85 },
86 {
87 .type = VehiclePropertyType::INT64_VEC,
88 .recyclable = false,
89 .vecSize = 5,
90 },
91 {
92 .type = VehiclePropertyType::FLOAT_VEC,
93 .recyclable = false,
94 .vecSize = 5,
95 },
96 {
97 .type = VehiclePropertyType::BYTES,
98 .recyclable = false,
99 .vecSize = 5,
100 },
101 {
102 .type = VehiclePropertyType::STRING,
103 .recyclable = false,
104 .vecSize = 0,
105 },
106 {
107 .type = VehiclePropertyType::MIXED,
108 .recyclable = false,
109 .vecSize = 0,
110 },
111 };
112 }
113
114 } // namespace
115
116 class VehicleObjectPoolTest : public ::testing::Test {
117 protected:
SetUp()118 void SetUp() override {
119 mStats = PoolStats::instance();
120 resetStats();
121 mValuePool.reset(new VehiclePropValuePool);
122 }
123
TearDown()124 void TearDown() override {
125 // At the end, all created objects should be either recycled or deleted.
126 ASSERT_EQ(mStats->Obtained, mStats->Recycled + mStats->Deleted);
127 // Some objects could be recycled multiple times.
128 ASSERT_LE(mStats->Created, mStats->Recycled + mStats->Deleted);
129 }
130
131 PoolStats* mStats;
132 std::unique_ptr<VehiclePropValuePool> mValuePool;
133
134 private:
resetStats()135 void resetStats() {
136 mStats->Obtained = 0;
137 mStats->Created = 0;
138 mStats->Recycled = 0;
139 mStats->Deleted = 0;
140 }
141 };
142
143 class VehiclePropertyTypesTest : public VehicleObjectPoolTest,
144 public testing::WithParamInterface<TestPropertyTypeInfo> {};
145
TEST_P(VehiclePropertyTypesTest,testRecycle)146 TEST_P(VehiclePropertyTypesTest, testRecycle) {
147 auto info = GetParam();
148 if (!info.recyclable) {
149 GTEST_SKIP();
150 }
151
152 auto value = mValuePool->obtain(info.type, info.vecSize);
153 void* raw = value.get();
154 value.reset();
155 // At this point, value should be recycled and the only object in the pool.
156 ASSERT_EQ(mValuePool->obtain(info.type, info.vecSize).get(), raw);
157
158 ASSERT_EQ(mStats->Obtained, 2u);
159 ASSERT_EQ(mStats->Created, 1u);
160 }
161
TEST_P(VehiclePropertyTypesTest,testNotRecyclable)162 TEST_P(VehiclePropertyTypesTest, testNotRecyclable) {
163 auto info = GetParam();
164 if (info.recyclable) {
165 GTEST_SKIP();
166 }
167
168 auto value = mValuePool->obtain(info.type, info.vecSize);
169
170 ASSERT_EQ(mStats->Obtained, 0u) << "Non recyclable object should not be obtained from the pool";
171 ASSERT_EQ(mStats->Created, 0u) << "Non recyclable object should not be created from the pool";
172 }
173
174 INSTANTIATE_TEST_SUITE_P(AllPropertyTypes, VehiclePropertyTypesTest,
175 ::testing::ValuesIn(getAllPropertyTypes()));
176
TEST_F(VehicleObjectPoolTest,testObtainNewObject)177 TEST_F(VehicleObjectPoolTest, testObtainNewObject) {
178 auto value = mValuePool->obtain(VehiclePropertyType::INT32);
179 void* raw = value.get();
180 value.reset();
181 // At this point, value should be recycled and the only object in the pool.
182 ASSERT_EQ(mValuePool->obtain(VehiclePropertyType::INT32).get(), raw);
183 // Obtaining value of another type - should return a new object
184 ASSERT_NE(mValuePool->obtain(VehiclePropertyType::FLOAT).get(), raw);
185
186 ASSERT_EQ(mStats->Obtained, 3u);
187 ASSERT_EQ(mStats->Created, 2u);
188 }
189
TEST_F(VehicleObjectPoolTest,testObtainStrings)190 TEST_F(VehicleObjectPoolTest, testObtainStrings) {
191 mValuePool->obtain(VehiclePropertyType::STRING);
192 auto stringProp = mValuePool->obtain(VehiclePropertyType::STRING);
193 stringProp->value.stringValue = "Hello";
194 void* raw = stringProp.get();
195 stringProp.reset(); // delete the pointer
196
197 auto newStringProp = mValuePool->obtain(VehiclePropertyType::STRING);
198
199 ASSERT_EQ(newStringProp->value.stringValue.size(), 0u);
200 ASSERT_NE(mValuePool->obtain(VehiclePropertyType::STRING).get(), raw);
201 ASSERT_EQ(mStats->Obtained, 0u);
202 }
203
TEST_F(VehicleObjectPoolTest,testObtainBoolean)204 TEST_F(VehicleObjectPoolTest, testObtainBoolean) {
205 auto prop = mValuePool->obtainBoolean(true);
206
207 ASSERT_NE(prop, nullptr);
208 ASSERT_EQ(*prop, (VehiclePropValue{
209 .value = {.int32Values = {1}},
210 }));
211 }
212
TEST_F(VehicleObjectPoolTest,testObtainInt32)213 TEST_F(VehicleObjectPoolTest, testObtainInt32) {
214 auto prop = mValuePool->obtainInt32(1234);
215
216 ASSERT_NE(prop, nullptr);
217 ASSERT_EQ(*prop, (VehiclePropValue{
218 .value = {.int32Values = {1234}},
219 }));
220 }
221
TEST_F(VehicleObjectPoolTest,testObtainInt64)222 TEST_F(VehicleObjectPoolTest, testObtainInt64) {
223 auto prop = mValuePool->obtainInt64(1234);
224
225 ASSERT_NE(prop, nullptr);
226 ASSERT_EQ(*prop, (VehiclePropValue{
227 .value = {.int64Values = {1234}},
228 }));
229 }
230
TEST_F(VehicleObjectPoolTest,testObtainFloat)231 TEST_F(VehicleObjectPoolTest, testObtainFloat) {
232 auto prop = mValuePool->obtainFloat(1.234);
233
234 ASSERT_NE(prop, nullptr);
235 ASSERT_EQ(*prop, (VehiclePropValue{
236 .value = {.floatValues = {1.234}},
237 }));
238 }
239
TEST_F(VehicleObjectPoolTest,testObtainString)240 TEST_F(VehicleObjectPoolTest, testObtainString) {
241 auto prop = mValuePool->obtainString("test");
242
243 ASSERT_NE(prop, nullptr);
244 ASSERT_EQ(*prop, (VehiclePropValue{
245 .value = {.stringValue = "test"},
246 }));
247 }
248
TEST_F(VehicleObjectPoolTest,testObtainComplex)249 TEST_F(VehicleObjectPoolTest, testObtainComplex) {
250 auto prop = mValuePool->obtainComplex();
251
252 ASSERT_NE(prop, nullptr);
253 ASSERT_EQ(*prop, VehiclePropValue{});
254 }
255
TEST_F(VehicleObjectPoolTest,testObtainCopyInt32Values)256 TEST_F(VehicleObjectPoolTest, testObtainCopyInt32Values) {
257 VehiclePropValue prop{
258 // INT32_VEC property.
259 .prop = toInt(VehicleProperty::INFO_FUEL_TYPE),
260 .areaId = 2,
261 .timestamp = 3,
262 .value = {.int32Values = {1, 2, 3, 4}},
263 };
264 auto gotValue = mValuePool->obtain(prop);
265
266 ASSERT_NE(gotValue, nullptr);
267 ASSERT_EQ(*gotValue, prop);
268 }
269
TEST_F(VehicleObjectPoolTest,testObtainCopyInt32ValuesEmptyArray)270 TEST_F(VehicleObjectPoolTest, testObtainCopyInt32ValuesEmptyArray) {
271 VehiclePropValue prop{
272 // INT32_VEC property.
273 .prop = toInt(VehicleProperty::INFO_FUEL_TYPE),
274 .areaId = 2,
275 .timestamp = 3,
276 .value = {.int32Values = {}},
277 };
278 auto gotValue = mValuePool->obtain(prop);
279
280 ASSERT_NE(gotValue, nullptr);
281 ASSERT_EQ(*gotValue, prop);
282 }
283
TEST_F(VehicleObjectPoolTest,testObtainCopyInt64Values)284 TEST_F(VehicleObjectPoolTest, testObtainCopyInt64Values) {
285 VehiclePropValue prop{
286 // INT64_VEC property.
287 .prop = toInt(VehicleProperty::WHEEL_TICK),
288 .areaId = 2,
289 .timestamp = 3,
290 .value = {.int64Values = {1, 2, 3, 4}},
291 };
292 auto gotValue = mValuePool->obtain(prop);
293
294 ASSERT_NE(gotValue, nullptr);
295 ASSERT_EQ(*gotValue, prop);
296 }
297
TEST_F(VehicleObjectPoolTest,testObtainCopyFloatValues)298 TEST_F(VehicleObjectPoolTest, testObtainCopyFloatValues) {
299 VehiclePropValue prop{
300 // FLOAT_VEC property.
301 .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION),
302 .areaId = 2,
303 .timestamp = 3,
304 .value = {.floatValues = {1, 2, 3, 4}},
305 };
306 auto gotValue = mValuePool->obtain(prop);
307
308 ASSERT_NE(gotValue, nullptr);
309 ASSERT_EQ(*gotValue, prop);
310 }
311
TEST_F(VehicleObjectPoolTest,testObtainCopyString)312 TEST_F(VehicleObjectPoolTest, testObtainCopyString) {
313 VehiclePropValue prop{
314 // STRING property.
315 .prop = toInt(VehicleProperty::INFO_VIN),
316 .areaId = 2,
317 .timestamp = 3,
318 .value = {.stringValue = "test"},
319 };
320 auto gotValue = mValuePool->obtain(prop);
321
322 ASSERT_NE(gotValue, nullptr);
323 ASSERT_EQ(*gotValue, prop);
324 }
325
TEST_F(VehicleObjectPoolTest,testObtainCopyMixed)326 TEST_F(VehicleObjectPoolTest, testObtainCopyMixed) {
327 VehiclePropValue prop{
328 // MIxed property.
329 .prop = toInt(VehicleProperty::VEHICLE_MAP_SERVICE),
330 .areaId = 2,
331 .timestamp = 3,
332 .value =
333 {
334 .int32Values = {1, 2, 3},
335 .floatValues = {4.0, 5.0},
336 .stringValue = "test",
337 },
338 };
339 auto gotValue = mValuePool->obtain(prop);
340
341 ASSERT_NE(gotValue, nullptr);
342 ASSERT_EQ(*gotValue, prop);
343 }
344
TEST_F(VehicleObjectPoolTest,testMultithreaded)345 TEST_F(VehicleObjectPoolTest, testMultithreaded) {
346 // In this test we have T threads that concurrently in C cycles
347 // obtain and release O VehiclePropValue objects of FLOAT / INT32 types.
348
349 const int T = 2;
350 const int C = 500;
351 const int O = 100;
352
353 auto poolPtr = mValuePool.get();
354
355 std::vector<std::thread> threads;
356 for (int i = 0; i < T; i++) {
357 threads.push_back(std::thread([&poolPtr]() {
358 for (int j = 0; j < C; j++) {
359 std::vector<recyclable_ptr<VehiclePropValue>> vec;
360 for (int k = 0; k < O; k++) {
361 vec.push_back(poolPtr->obtain(k % 2 == 0 ? VehiclePropertyType::FLOAT
362 : VehiclePropertyType::INT32));
363 }
364 }
365 }));
366 }
367
368 for (auto& t : threads) {
369 t.join();
370 }
371
372 ASSERT_EQ(mStats->Obtained, static_cast<uint32_t>(T * C * O));
373 ASSERT_EQ(mStats->Recycled + mStats->Deleted, static_cast<uint32_t>(T * C * O));
374 // Created less than obtained in one cycle.
375 ASSERT_LE(mStats->Created, static_cast<uint32_t>(T * O));
376 }
377
TEST_F(VehicleObjectPoolTest,testMemoryLimitation)378 TEST_F(VehicleObjectPoolTest, testMemoryLimitation) {
379 std::vector<recyclable_ptr<VehiclePropValue>> vec;
380 for (size_t i = 0; i < 10000; i++) {
381 vec.push_back(mValuePool->obtain(VehiclePropertyType::INT32));
382 }
383 // We have too many values, not all of them would be recycled, some of them will be deleted.
384 vec.clear();
385
386 ASSERT_EQ(mStats->Obtained, 10000u);
387 ASSERT_EQ(mStats->Created, 10000u);
388 ASSERT_GT(mStats->Deleted, 0u) << "expect some values to be deleted, not recycled if too many "
389 "values are in the pool";
390 }
391
392 } // namespace vehicle
393 } // namespace automotive
394 } // namespace hardware
395 } // namespace android
396