1 #include <audio_utils/hal_smoothness.h>
2 #include <errno.h>
3 #include <float.h>
4 #include <gtest/gtest.h>
5 #include <limits.h>
6
7 #include <memory>
8
9 struct TestDeleter {
operator ()TestDeleter10 void operator()(hal_smoothness *p) { hal_smoothness_free(&p); }
11 };
12
13 struct custom_private_data {
14 bool ran_callback;
15
16 hal_smoothness_metrics metrics;
17 };
18
custom_flush(hal_smoothness_metrics * metrics,void * private_data)19 void custom_flush(hal_smoothness_metrics *metrics, void *private_data) {
20 custom_private_data *data = (custom_private_data *)private_data;
21
22 data->ran_callback = true;
23
24 memcpy(&data->metrics, metrics, sizeof(hal_smoothness_metrics));
25 }
26
27 class HalSmoothnessTest : public ::testing::Test {
28 protected:
CommonSmoothnessInit(unsigned int num_writes_to_log)29 void CommonSmoothnessInit(unsigned int num_writes_to_log) {
30 hal_smoothness *smoothness_init;
31 data = {};
32 data.ran_callback = false;
33 int result =
34 hal_smoothness_initialize(&smoothness_init, HAL_SMOOTHNESS_VERSION_1,
35 num_writes_to_log, custom_flush, &data);
36 ASSERT_EQ(result, 0);
37 smoothness = std::unique_ptr<hal_smoothness, TestDeleter>{smoothness_init};
38 }
39
40 std::unique_ptr<hal_smoothness, TestDeleter> smoothness;
41 custom_private_data data;
42 };
43
44 // Test that the callback runs after the total write count is equal to
45 // "num_writes_to_log".
TEST_F(HalSmoothnessTest,callback_should_run)46 TEST_F(HalSmoothnessTest, callback_should_run) {
47 ASSERT_NO_FATAL_FAILURE(
48 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 1));
49 // Since "num_writes_to_log" is set to 1, after this write, the callback
50 // should run.
51 smoothness->increment_total_writes(smoothness.get(),
52 /* frames_written= */ 100,
53 /* timestamp */ 200);
54
55 EXPECT_EQ(data.ran_callback, true);
56 }
57
58 // Test that the callback should not run if the total write count is less than
59 // "num_writes_to_log".
TEST_F(HalSmoothnessTest,callback_should_not_run)60 TEST_F(HalSmoothnessTest, callback_should_not_run) {
61 ASSERT_NO_FATAL_FAILURE(
62 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 2));
63
64 // Since "num_writes_to_log" is set to 2, after this write, the callback
65 // should NOT run.
66 smoothness->increment_total_writes(smoothness.get(),
67 /* frames_written= */ 100,
68 /* timestamp */ 200);
69 EXPECT_EQ(data.ran_callback, false);
70
71 smoothness->increment_total_writes(smoothness.get(),
72 /* frames_written= */ 100,
73 /* timestamp */ 200);
74 EXPECT_EQ(data.ran_callback, true);
75 }
76
77 // Test that metric values in "struct hal_smoothness_metrics" that is passed
78 // into the callback are correct.
TEST_F(HalSmoothnessTest,verify_metrics)79 TEST_F(HalSmoothnessTest, verify_metrics) {
80 ASSERT_NO_FATAL_FAILURE(
81 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 6));
82
83 unsigned int timestamp = 200;
84
85 // Simulate how these increment methods would be called during a real runtime.
86 smoothness->increment_total_writes(smoothness.get(),
87 /* frames_written= */ 1000, timestamp++);
88 smoothness->increment_total_writes(smoothness.get(),
89 /* frames_written= */ 1000, timestamp++);
90 smoothness->increment_underrun(smoothness.get(), /* frames_lost= */ 900);
91 smoothness->increment_total_writes(smoothness.get(),
92 /* frames_written= */ 100, timestamp++);
93 smoothness->increment_overrun(smoothness.get(), /* frames_lost */ 900);
94 smoothness->increment_total_writes(smoothness.get(),
95 /* frames_written= */ 100, timestamp++);
96 smoothness->increment_underrun(smoothness.get(), /* frames_lost */ 900);
97 smoothness->increment_total_writes(smoothness.get(),
98 /* frames_written= */ 100, timestamp++);
99 smoothness->increment_total_writes(smoothness.get(),
100 /* frames_written= */ 1000, timestamp);
101
102 EXPECT_EQ(data.metrics.underrun_count, 2U);
103 EXPECT_EQ(data.metrics.overrun_count, 1U);
104 EXPECT_EQ(data.metrics.total_writes, 6U);
105 EXPECT_EQ(data.metrics.total_frames_written, 3300U);
106 EXPECT_EQ(data.metrics.total_frames_lost, 2700U);
107 EXPECT_EQ(data.metrics.timestamp, timestamp);
108 }
109
110 // Test that metric values in "struct hal_smoothness_metrics" are reset after it
111 // has met "num_writes_to_log".
TEST_F(HalSmoothnessTest,verify_metrics_reset)112 TEST_F(HalSmoothnessTest, verify_metrics_reset) {
113 const unsigned int num_write_to_log = 6;
114 ASSERT_NO_FATAL_FAILURE(HalSmoothnessTest::CommonSmoothnessInit(
115 /* num_writes_to_log= */ num_write_to_log));
116
117 int timestamp = 200;
118 // Simulate how these increment methods would be called during a real runtime.
119 smoothness->increment_total_writes(smoothness.get(),
120 /* frames_written= */ 1000,
121 /* timestamp */ timestamp++);
122 smoothness->increment_total_writes(smoothness.get(),
123 /* frames_written= */ 1000,
124 /* timestamp */ timestamp++);
125 smoothness->increment_underrun(smoothness.get(), /* frames_lost= */ 900);
126 smoothness->increment_total_writes(smoothness.get(),
127 /* frames_written= */ 100,
128 /* timestamp */ timestamp++);
129 smoothness->increment_overrun(smoothness.get(), /* frames_lost */ 900);
130 smoothness->increment_total_writes(smoothness.get(),
131 /* frames_written= */ 100,
132 /* timestamp */ timestamp++);
133 smoothness->increment_underrun(smoothness.get(), /* frames_lost */ 900);
134 smoothness->increment_total_writes(smoothness.get(),
135 /* frames_written= */ 100,
136 /* timestamp */ timestamp++);
137 smoothness->increment_total_writes(smoothness.get(),
138 /* frames_written= */ 1000,
139 /* timestamp */ timestamp++);
140
141 const unsigned int frames_written_on_write = 1000;
142 // At this point, metrics values should be reset. We will write 6 more times
143 // to trigger the callback again.
144 for (unsigned int i = 0; i < num_write_to_log; i++) {
145 // last timestamp will be 211 because 206 + 5.
146 smoothness->increment_total_writes(smoothness.get(),
147 /* frames_written= */
148 frames_written_on_write,
149 /* timestamp */ timestamp + i);
150 }
151
152 EXPECT_EQ(data.metrics.underrun_count, 0U);
153 EXPECT_EQ(data.metrics.overrun_count, 0U);
154 EXPECT_EQ(data.metrics.total_writes, 6U);
155 EXPECT_EQ(data.metrics.total_frames_written,
156 frames_written_on_write * num_write_to_log);
157 EXPECT_EQ(data.metrics.total_frames_lost, 0U);
158 EXPECT_EQ(data.metrics.timestamp, 211U);
159 }
160
161 // Test that metric values in "struct hal_smoothness_metrics" that is passed
162 // into the callback are correct.
TEST_F(HalSmoothnessTest,smoothness_value_10ish)163 TEST_F(HalSmoothnessTest, smoothness_value_10ish) {
164 ASSERT_NO_FATAL_FAILURE(
165 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 5));
166
167 unsigned int timestamp = 200;
168 // Simulate how these increment methods would be called during a real runtime.
169 smoothness->increment_total_writes(smoothness.get(),
170 /* frames_written= */ 8000, timestamp++);
171 smoothness->increment_total_writes(smoothness.get(),
172 /* frames_written= */ 8000, timestamp++);
173 smoothness->increment_total_writes(smoothness.get(),
174 /* frames_written= */ 8000, timestamp++);
175 smoothness->increment_total_writes(smoothness.get(),
176 /* frames_written= */ 8000, timestamp++);
177 smoothness->increment_underrun(smoothness.get(), /* frames_lost */ 1);
178 smoothness->increment_total_writes(smoothness.get(),
179 /* frames_written= */ 7999, timestamp++);
180
181 // -ln(1/40000)
182 EXPECT_FLOAT_EQ(data.metrics.smoothness_value, 10.596635);
183 }
184
TEST_F(HalSmoothnessTest,smoothness_value_6ish)185 TEST_F(HalSmoothnessTest, smoothness_value_6ish) {
186 ASSERT_NO_FATAL_FAILURE(
187 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 5));
188
189 unsigned int timestamp = 200;
190 // Simulate how these increment methods would be called during a real runtime.
191 smoothness->increment_total_writes(smoothness.get(),
192 /* frames_written= */ 8000, timestamp++);
193 smoothness->increment_total_writes(smoothness.get(),
194 /* frames_written= */ 8000, timestamp++);
195 smoothness->increment_total_writes(smoothness.get(),
196 /* frames_written= */ 8000, timestamp++);
197 smoothness->increment_total_writes(smoothness.get(),
198 /* frames_written= */ 8000, timestamp++);
199 smoothness->increment_underrun(smoothness.get(), /* frames_lost */ 100);
200 smoothness->increment_total_writes(smoothness.get(),
201 /* frames_written= */ 7900, timestamp++);
202
203 // -ln(1/400)
204 EXPECT_FLOAT_EQ(data.metrics.smoothness_value, 5.9914646);
205 }
206
TEST_F(HalSmoothnessTest,log_zero_smoothness_value)207 TEST_F(HalSmoothnessTest, log_zero_smoothness_value) {
208 ASSERT_NO_FATAL_FAILURE(
209 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 1));
210
211 // Simulate how these increment methods would be called during a real runtime.
212 smoothness->increment_total_writes(smoothness.get(),
213 /* frames_written= */ 8000,
214 /* timestamp */ 200);
215
216 // -ln(0). This should return DBL_MAX
217 EXPECT_FLOAT_EQ(data.metrics.smoothness_value, DBL_MAX);
218 }
219
TEST(hal_smoothness,init_fail_with_zero_num_writes_to_log)220 TEST(hal_smoothness, init_fail_with_zero_num_writes_to_log) {
221 hal_smoothness *smoothness;
222 custom_private_data data;
223 int result = hal_smoothness_initialize(&smoothness, HAL_SMOOTHNESS_VERSION_1,
224 /* num_writes_to_log= */ 0,
225 custom_flush, &data);
226 EXPECT_EQ(result, -EINVAL);
227 }
228
TEST(hal_smoothness,init_pass_with_null_private_data)229 TEST(hal_smoothness, init_pass_with_null_private_data) {
230 hal_smoothness *smoothness_init;
231 int result =
232 hal_smoothness_initialize(&smoothness_init, HAL_SMOOTHNESS_VERSION_1,
233 /* num_writes_to_log= */ 6, custom_flush, NULL);
234 ASSERT_EQ(result, 0);
235 auto smoothness =
236 std::unique_ptr<hal_smoothness, TestDeleter>{smoothness_init};
237 }
238
TEST(hal_smoothness,hal_smoothness_free)239 TEST(hal_smoothness, hal_smoothness_free) {
240 hal_smoothness *smoothness;
241 custom_private_data data;
242 int result = hal_smoothness_initialize(&smoothness, HAL_SMOOTHNESS_VERSION_1,
243 /* num_writes_to_log= */ 6,
244 custom_flush, &data);
245 ASSERT_EQ(result, 0);
246
247 hal_smoothness_free(&smoothness);
248 EXPECT_EQ(smoothness, nullptr);
249 }
250
TEST(hal_smoothness,hal_smoothness_free_pass_in_null)251 TEST(hal_smoothness, hal_smoothness_free_pass_in_null) {
252 hal_smoothness *smoothness;
253
254 hal_smoothness_free(&smoothness);
255 EXPECT_EQ(smoothness, nullptr);
256 }
257
258 // Excluded testing overflow for values that only increment by 1 (ie.
259 // underrun_count, overrun_count, total_writes).
TEST_F(HalSmoothnessTest,underrun_overflow)260 TEST_F(HalSmoothnessTest, underrun_overflow) {
261 ASSERT_NO_FATAL_FAILURE(
262 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 1));
263
264 ASSERT_EQ(smoothness->increment_underrun(smoothness.get(),
265 /* frames_lost= */ UINT_MAX),
266 0);
267 ASSERT_EQ(
268 smoothness->increment_underrun(smoothness.get(), /* frames_lost= */ 1),
269 -EOVERFLOW);
270 }
271
TEST_F(HalSmoothnessTest,overrun_overflow)272 TEST_F(HalSmoothnessTest, overrun_overflow) {
273 ASSERT_NO_FATAL_FAILURE(
274 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 1));
275
276 ASSERT_EQ(smoothness->increment_overrun(smoothness.get(),
277 /* frames_lost= */ UINT_MAX),
278 0);
279 ASSERT_EQ(
280 smoothness->increment_overrun(smoothness.get(), /* frames_lost= */ 1),
281 -EOVERFLOW);
282 }
283
TEST_F(HalSmoothnessTest,overflow_total_writes)284 TEST_F(HalSmoothnessTest, overflow_total_writes) {
285 ASSERT_NO_FATAL_FAILURE(
286 HalSmoothnessTest::CommonSmoothnessInit(/* num_writes_to_log= */ 2));
287
288 unsigned int timestamp = 200;
289 ASSERT_EQ(smoothness->increment_total_writes(smoothness.get(),
290 /* frames_written= */ UINT_MAX,
291 timestamp++),
292 0);
293 ASSERT_EQ(
294 smoothness->increment_total_writes(smoothness.get(),
295 /* frames_written= */ 1, timestamp++),
296 -EOVERFLOW);
297 }
298
TEST_F(HalSmoothnessTest,flush)299 TEST_F(HalSmoothnessTest, flush) {
300 const unsigned int num_write_to_log = 5;
301 ASSERT_NO_FATAL_FAILURE(HalSmoothnessTest::CommonSmoothnessInit(
302 /* num_writes_to_log= */ num_write_to_log));
303
304 unsigned int timestamp = 201;
305 smoothness->increment_total_writes(smoothness.get(),
306 /* frames_written= */ 1000, timestamp++);
307 smoothness->increment_underrun(smoothness.get(), /* frames_lost= */ 900);
308 smoothness->increment_total_writes(smoothness.get(),
309 /* frames_written= */ 100, timestamp++);
310 smoothness->increment_overrun(smoothness.get(), /* frames_lost */ 900);
311 smoothness->increment_total_writes(smoothness.get(),
312 /* frames_written= */ 100, timestamp);
313
314 // Verify metrics have not been flushed yet
315 ASSERT_EQ(data.metrics.underrun_count, 0U);
316 ASSERT_EQ(data.metrics.overrun_count, 0U);
317 ASSERT_EQ(data.metrics.total_writes, 0U);
318 ASSERT_EQ(data.metrics.total_frames_written, 0U);
319 ASSERT_EQ(data.metrics.total_frames_lost, 0U);
320 ASSERT_EQ(data.metrics.timestamp, 0U);
321
322 smoothness->flush(smoothness.get());
323
324 // Verify metrics have been flushed.
325 EXPECT_EQ(data.metrics.underrun_count, 1U);
326 EXPECT_EQ(data.metrics.overrun_count, 1U);
327 EXPECT_EQ(data.metrics.total_writes, 3U);
328 EXPECT_EQ(data.metrics.total_frames_written, 1200U);
329 EXPECT_EQ(data.metrics.total_frames_lost, 1800U);
330 EXPECT_EQ(data.metrics.timestamp, timestamp++);
331
332 const unsigned int frames_written_on_write = 1000;
333 // At this point, metrics values should be reset. We will write 5 more times
334 // to trigger the callback again.
335 for (unsigned int i = 0; i < num_write_to_log; i++) {
336 // last timestamp will be 208 because 204 + 4.
337 smoothness->increment_total_writes(smoothness.get(),
338 /* frames_written= */
339 frames_written_on_write,
340 /* timestamp */ timestamp + i);
341 }
342
343 EXPECT_EQ(data.metrics.underrun_count, 0U);
344 EXPECT_EQ(data.metrics.overrun_count, 0U);
345 EXPECT_EQ(data.metrics.total_writes, 5U);
346 EXPECT_EQ(data.metrics.total_frames_written,
347 frames_written_on_write * num_write_to_log);
348 EXPECT_EQ(data.metrics.total_frames_lost, 0U);
349 EXPECT_EQ(data.metrics.timestamp, 208U);
350 }
351