1 /*
2 * Copyright (C) 2019 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 #define LOG_TAG "StreamBufferCacheManagerTests"
18 #include <cutils/properties.h>
19 #include <gtest/gtest.h>
20 #include <log/log.h>
21
22 #include <chrono>
23 #include <map>
24 #include <string>
25 #include <thread>
26 #include <unordered_set>
27 #include <vector>
28
29 #include "stream_buffer_cache_manager.h"
30
31 namespace android {
32 namespace google_camera_hal {
33
34 using namespace std::chrono_literals;
35
36 class StreamBufferCacheManagerTests : public ::testing::Test {
37 protected:
38 // This is used to mock the framework callback latency
39 static constexpr auto kAllocateBufferFuncLatency = 10ms;
40 // The minimum interval two successful buffer acquisition must have, this
41 // should be close to kAllocateBufferFuncLatency, but leave a safe gap(1ms)
42 // in case of timing fluctuation.
43 static constexpr auto kBufferAcquireMinLatency = 9ms;
44 // The maximum latency that the cached buffer is returned to the framework
45 static constexpr auto kBufferReturnMaxLatency = 5ms;
46 static const uint32_t kDefaultRemainingFulfillment = 2;
47
AllocateBufferFunc(uint32_t num_buffer,std::vector<StreamBuffer> * buffers,StreamBufferRequestError * status)48 status_t AllocateBufferFunc(uint32_t num_buffer,
49 std::vector<StreamBuffer>* buffers,
50 StreamBufferRequestError* status) {
51 *status = StreamBufferRequestError::kOk;
52 buffers->clear();
53 if (remaining_number_of_fulfillment_ != 0) {
54 buffers->resize(num_buffer);
55 } else {
56 *status = StreamBufferRequestError::kStreamDisconnected;
57 return OK;
58 }
59
60 // Mocking the framework callback latency
61 std::this_thread::sleep_for(kAllocateBufferFuncLatency);
62 remaining_number_of_fulfillment_--;
63 return OK;
64 }
65
ReturnBufferFunc(const std::vector<StreamBuffer> &)66 status_t ReturnBufferFunc(const std::vector<StreamBuffer>& /*buffers*/) {
67 num_return_buffer_func_called++;
68 return OK;
69 }
70
71 const StreamBufferCacheRegInfo kDummyCacheRegInfo{
72 .request_func =
73 [this](uint32_t num_buffer, std::vector<StreamBuffer>* buffers,
__anon64f806620102() 74 StreamBufferRequestError* status) {
75 return this->AllocateBufferFunc(num_buffer, buffers, status);
76 },
77 .return_func =
__anon64f806620202() 78 [this](const std::vector<StreamBuffer>& buffers) {
79 return this->ReturnBufferFunc(buffers);
80 },
81 .stream_id = 1,
82 .width = 640,
83 .height = 480,
84 .format = HAL_PIXEL_FORMAT_RAW10,
85 .producer_flags = 0,
86 .consumer_flags = 0,
87 .num_buffers_to_cache = 1};
88
SetUp()89 void SetUp() override {
90 // Skip test if product is unsupported.
91 char product_name[PROPERTY_VALUE_MAX];
92 // TODO(b/142732212): Flacky occurred,
93 // Remove "blueline", "crosshatch", "flame", "coral"
94 // from supported_product_list first.
95 std::unordered_set<std::string> const supported_product_list{""};
96 property_get("ro.build.product", product_name, "");
97 bool product_support_test =
98 supported_product_list.find(std::string{product_name}) !=
99 supported_product_list.end();
100 if (!product_support_test) {
101 GTEST_SKIP();
102 }
103
104 cache_manager_ = StreamBufferCacheManager::Create();
105 ASSERT_NE(cache_manager_, nullptr)
106 << " Creating StreamBufferCacheManager failed";
107 }
108
109 // Set remaining_number_of_fulfillment_. This should be called before any
110 // other operations if the test needs to control this.
SetRemainingFulfillment(uint32_t remaining_num)111 void SetRemainingFulfillment(uint32_t remaining_num) {
112 remaining_number_of_fulfillment_ = remaining_num;
113 }
114
115 // StreamBufferCacheManager created by this test fixture
116 std::unique_ptr<StreamBufferCacheManager> cache_manager_;
117
118 // Counts the total number of buffers acquired by each stream id
119 std::map<int32_t, uint32_t> buffer_allocation_cnt_;
120
121 // Counts the total number of buffers returned by each stream id
122 std::map<int32_t, uint32_t> buffer_return_cnt_;
123
124 // Max number of requests that AllocateBufferFunc can fulfill. This is used to
125 // mock the failure of buffer provider from the framework.
126 uint32_t remaining_number_of_fulfillment_ = kDefaultRemainingFulfillment;
127
128 // Number of times the ReturnBufferFunc is called.
129 int32_t num_return_buffer_func_called = 0;
130 };
131
132 // Test RegisterStream
TEST_F(StreamBufferCacheManagerTests,RegisterStream)133 TEST_F(StreamBufferCacheManagerTests, RegisterStream) {
134 // RegisterStream should succeed
135 status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
136 ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
137
138 // RegisterStream should fail when registering the same stream twice
139 res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
140 ASSERT_NE(res, OK) << " RegisterStream succeeded when registering the same "
141 "stream for more than once!";
142
143 // RegisterStream should succeed when registering another stream
144 StreamBufferCacheRegInfo another_reg_info = kDummyCacheRegInfo;
145 another_reg_info.stream_id = kDummyCacheRegInfo.stream_id + 1;
146 res = cache_manager_->RegisterStream(another_reg_info);
147 ASSERT_EQ(res, OK) << " RegisterStream another stream failed!"
148 << strerror(res);
149 }
150
151 // Test NotifyProviderReadiness
TEST_F(StreamBufferCacheManagerTests,NotifyProviderReadiness)152 TEST_F(StreamBufferCacheManagerTests, NotifyProviderReadiness) {
153 // Need to register stream before notifying provider readiness
154 status_t res =
155 cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
156 ASSERT_NE(res, OK) << " NotifyProviderReadiness succeeded without reigstering"
157 " the stream.";
158
159 res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
160 ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
161
162 // Notify ProviderReadiness should succeed after the stream is registered
163 res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
164 ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
165 }
166
167 // Test the correct order of calling GetStreamBuffer
TEST_F(StreamBufferCacheManagerTests,BasicGetStreamBuffer)168 TEST_F(StreamBufferCacheManagerTests, BasicGetStreamBuffer) {
169 StreamBufferRequestResult req_result;
170 // GetStreamBuffer should fail before the stream is registered.
171 status_t res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
172 &req_result);
173 ASSERT_NE(res, OK) << " GetStreamBuffer should fail before stream is "
174 "registered and provider readiness is notified.";
175
176 res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
177 ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
178
179 // GetStreamBuffer should fail before the stream's provider is notified for
180 // readiness.
181 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
182 &req_result);
183 ASSERT_NE(res, OK) << " GetStreamBuffer should fail before stream is "
184 "registered and provider readiness is notified.";
185
186 res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
187 ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
188
189 // GetStreamBuffer should succeed after the stream is registered and its
190 // provider's readiness is notified.
191 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
192 &req_result);
193 ASSERT_EQ(res, OK) << " Getting stream buffer failed!" << strerror(res);
194 }
195
196 // Test sequence of function call to GetStreamBuffer
TEST_F(StreamBufferCacheManagerTests,SequenceOfGetStreamBuffer)197 TEST_F(StreamBufferCacheManagerTests, SequenceOfGetStreamBuffer) {
198 const uint32_t kValidBufferRequests = 2;
199 SetRemainingFulfillment(kValidBufferRequests);
200 status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
201 ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
202
203 res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
204 ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
205
206 // Allow enough time for the buffer allocator to refill the cache
207 std::this_thread::sleep_for(kAllocateBufferFuncLatency);
208
209 // First GetStreamBuffer should succeed immediately with a non-dummy buffer
210 StreamBufferRequestResult req_result;
211 auto t_start = std::chrono::high_resolution_clock::now();
212 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
213 &req_result);
214 auto t_end = std::chrono::high_resolution_clock::now();
215 ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
216 ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
217 << " First buffer request should be fulfilled immediately.";
218 ASSERT_EQ(req_result.is_dummy_buffer, false)
219 << " First buffer request got dummy buffer.";
220
221 // Second GetStreamBuffer should succeed with a non-dummy buffer, but should
222 // happen after a gap longer than kBufferAcquireMinLatency.
223 t_start = std::chrono::high_resolution_clock::now();
224 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
225 &req_result);
226 t_end = std::chrono::high_resolution_clock::now();
227 ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
228 ASSERT_EQ(true, t_end - t_start > kBufferAcquireMinLatency)
229 << " Buffer acquisition gap between two consecutive reqs is too small.";
230 ASSERT_EQ(req_result.is_dummy_buffer, false)
231 << " Second buffer request got dummy buffer.";
232
233 // Allow enough time for the buffer allocator to refill the cache
234 std::this_thread::sleep_for(kAllocateBufferFuncLatency);
235 // No more remaining fulfilment so StreamBufferCache should be either deactive
236 // or inactive.
237 bool is_active = false;
238 res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
239 ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
240 ASSERT_EQ(is_active, false)
241 << " StreamBufferCache should be either deactive or inactive!";
242
243 // Third GetStreamBuffer should succeed with a dummy buffer immediately
244 t_start = std::chrono::high_resolution_clock::now();
245 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
246 &req_result);
247 t_end = std::chrono::high_resolution_clock::now();
248 ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
249 ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
250 << " Buffer acquisition gap for a dummy return should be negligible.";
251 ASSERT_EQ(req_result.is_dummy_buffer, true)
252 << " Third buffer request did not get dummy buffer.";
253 }
254
255 // Test NotifyFlushingAll
TEST_F(StreamBufferCacheManagerTests,NotifyFlushingAll)256 TEST_F(StreamBufferCacheManagerTests, NotifyFlushingAll) {
257 // One before the first GetStreamBuffer. One after that. One for the
258 // GetStreamBuffer happens after the NotifyFlushingAll.
259 const uint32_t kValidBufferRequests = 3;
260 SetRemainingFulfillment(kValidBufferRequests);
261 status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
262 ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
263
264 res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
265 ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
266
267 // Allow enough time for the buffer allocator to refill the cache
268 std::this_thread::sleep_for(kAllocateBufferFuncLatency);
269
270 // First GetStreamBuffer should succeed immediately with a non-dummy buffer
271 StreamBufferRequestResult req_result;
272 auto t_start = std::chrono::high_resolution_clock::now();
273 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
274 &req_result);
275 auto t_end = std::chrono::high_resolution_clock::now();
276 ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
277 ASSERT_EQ(true, t_end - t_start < kBufferAcquireMinLatency)
278 << " First buffer request should be fulfilled immediately.";
279 ASSERT_EQ(req_result.is_dummy_buffer, false)
280 << " First buffer request got dummy buffer.";
281
282 // Allow enough time for the buffer allocator to refill the cache
283 std::this_thread::sleep_for(kAllocateBufferFuncLatency);
284 // NotifyFlushingAll should succeed
285 ASSERT_EQ(num_return_buffer_func_called, 0)
286 << " ReturnBufferFunc should not be called before NotifyFlushingAll!";
287 res = cache_manager_->NotifyFlushingAll();
288 ASSERT_EQ(res, OK) << " NotifyFlushingAll failed!" << strerror(res);
289 std::this_thread::sleep_for(kBufferReturnMaxLatency);
290 ASSERT_EQ(num_return_buffer_func_called, 1)
291 << " ReturnBufferFunc was not called after NotifyFlushingAll is invoked!";
292
293 // GetStreamBuffer should still be able to re-trigger cache to refill after
294 // NotifyFlushingAll is called.
295 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
296 &req_result);
297 ASSERT_EQ(res, OK) << " GetStreamBuffer failed!" << strerror(res);
298 ASSERT_EQ(req_result.is_dummy_buffer, false)
299 << " Buffer request got dummy buffer.";
300 }
301
302 // Test IsStreamActive
TEST_F(StreamBufferCacheManagerTests,IsStreamActive)303 TEST_F(StreamBufferCacheManagerTests, IsStreamActive) {
304 const uint32_t kValidBufferRequests = 1;
305 SetRemainingFulfillment(kValidBufferRequests);
306 status_t res = cache_manager_->RegisterStream(kDummyCacheRegInfo);
307 ASSERT_EQ(res, OK) << " RegisterStream failed!" << strerror(res);
308
309 res = cache_manager_->NotifyProviderReadiness(kDummyCacheRegInfo.stream_id);
310 ASSERT_EQ(res, OK) << " NotifyProviderReadiness failed!" << strerror(res);
311
312 // Allow enough time for the buffer allocator to refill the cache
313 std::this_thread::sleep_for(kAllocateBufferFuncLatency);
314
315 // StreamBufferCache should be valid before dummy buffer is used.
316 bool is_active = false;
317 res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
318 ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
319 ASSERT_EQ(is_active, true) << " StreamBufferCache should be active!";
320
321 StreamBufferRequestResult req_result;
322 res = cache_manager_->GetStreamBuffer(kDummyCacheRegInfo.stream_id,
323 &req_result);
324
325 // Allow enough time for buffer provider to finish its job
326 std::this_thread::sleep_for(kAllocateBufferFuncLatency);
327 // There is only one valid buffer request. So the stream will be deactive
328 // after the GetStreamBuffer(when the cache tries the second buffer request).
329 res = cache_manager_->IsStreamActive(kDummyCacheRegInfo.stream_id, &is_active);
330 ASSERT_EQ(res, OK) << " IsStreamActive failed!" << strerror(res);
331 ASSERT_EQ(is_active, false) << " StreamBufferCache should be deactived!";
332 }
333
334 } // namespace google_camera_hal
335 } // namespace android
336