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