a2dp_iodev_unittest.cc revision 5c890457a82d673cd0ab619d6b506a1fbea630eb
1// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdio.h>
6#include <stdint.h>
7#include <gtest/gtest.h>
8
9extern "C" {
10
11#include "a2dp-codecs.h"
12#include "cras_audio_area.h"
13#include "audio_thread.h"
14#include "audio_thread_log.h"
15#include "cras_bt_transport.h"
16#include "cras_iodev.h"
17
18#include "cras_a2dp_iodev.h"
19}
20
21#define FAKE_OBJECT_PATH "/fake/obj/path"
22
23#define MAX_A2DP_ENCODE_CALLS 2
24#define MAX_A2DP_WRITE_CALLS 4
25
26static struct cras_bt_transport *fake_transport;
27static cras_audio_format format;
28static size_t cras_bt_device_append_iodev_called;
29static size_t cras_bt_device_rm_iodev_called;
30static size_t cras_iodev_add_node_called;
31static size_t cras_iodev_rm_node_called;
32static size_t cras_iodev_set_active_node_called;
33static size_t cras_bt_transport_acquire_called;
34static size_t cras_bt_transport_configuration_called;
35static size_t cras_bt_transport_release_called;
36static size_t init_a2dp_called;
37static int init_a2dp_return_val;
38static size_t destroy_a2dp_called;
39static size_t drain_a2dp_called;
40static size_t a2dp_block_size_called;
41static size_t a2dp_queued_frames_val;
42static size_t cras_iodev_free_format_called;
43static size_t cras_iodev_free_resources_called;
44static int pcm_buf_size_val[MAX_A2DP_ENCODE_CALLS];
45static unsigned int a2dp_encode_processed_bytes_val[MAX_A2DP_ENCODE_CALLS];
46static unsigned int a2dp_encode_index;
47static int a2dp_write_return_val[MAX_A2DP_WRITE_CALLS];
48static unsigned int a2dp_write_index;
49static cras_audio_area *dummy_audio_area;
50static thread_callback write_callback;
51static void *write_callback_data;
52static const char *fake_device_name = "fake device name";
53static const char *cras_bt_device_name_ret;
54static unsigned int cras_bt_transport_write_mtu_ret;
55
56void ResetStubData() {
57  cras_bt_device_append_iodev_called = 0;
58  cras_bt_device_rm_iodev_called = 0;
59  cras_iodev_add_node_called = 0;
60  cras_iodev_rm_node_called = 0;
61  cras_iodev_set_active_node_called = 0;
62  cras_bt_transport_acquire_called = 0;
63  cras_bt_transport_configuration_called = 0;
64  cras_bt_transport_release_called = 0;
65  init_a2dp_called = 0;
66  init_a2dp_return_val = 0;
67  destroy_a2dp_called = 0;
68  drain_a2dp_called = 0;
69  a2dp_block_size_called = 0;
70  a2dp_queued_frames_val = 0;
71  cras_iodev_free_format_called = 0;
72  cras_iodev_free_resources_called = 0;
73  memset(a2dp_encode_processed_bytes_val, 0,
74         sizeof(a2dp_encode_processed_bytes_val));
75  a2dp_encode_index = 0;
76  a2dp_write_index = 0;
77  cras_bt_transport_write_mtu_ret = 800;
78
79  fake_transport = reinterpret_cast<struct cras_bt_transport *>(0x123);
80
81  if (!dummy_audio_area) {
82    dummy_audio_area = (cras_audio_area*)calloc(1,
83        sizeof(*dummy_audio_area) + sizeof(cras_channel_area) * 2);
84  }
85
86  write_callback = NULL;
87}
88
89int iodev_set_format(struct cras_iodev *iodev,
90                     struct cras_audio_format *fmt)
91{
92  fmt->format = SND_PCM_FORMAT_S16_LE;
93  fmt->num_channels = 2;
94  fmt->frame_rate = 44100;
95  iodev->format = fmt;
96  return 0;
97}
98
99namespace {
100
101static struct timespec time_now;
102
103TEST(A2dpIoInit, InitializeA2dpIodev) {
104  struct cras_iodev *iodev;
105
106  atlog = (audio_thread_event_log *)calloc(1, sizeof(audio_thread_event_log));
107
108  ResetStubData();
109
110  cras_bt_device_name_ret = NULL;
111  iodev = a2dp_iodev_create(fake_transport);
112
113  ASSERT_NE(iodev, (void *)NULL);
114  ASSERT_EQ(iodev->direction, CRAS_STREAM_OUTPUT);
115  ASSERT_EQ(1, cras_bt_transport_configuration_called);
116  ASSERT_EQ(1, init_a2dp_called);
117  ASSERT_EQ(1, cras_bt_device_append_iodev_called);
118  ASSERT_EQ(1, cras_iodev_add_node_called);
119  ASSERT_EQ(1, cras_iodev_set_active_node_called);
120
121  /* Assert iodev name matches the object path when bt device doesn't
122   * have its readable name populated. */
123  ASSERT_STREQ(FAKE_OBJECT_PATH, iodev->info.name);
124
125  a2dp_iodev_destroy(iodev);
126
127  ASSERT_EQ(1, cras_bt_device_rm_iodev_called);
128  ASSERT_EQ(1, cras_iodev_rm_node_called);
129  ASSERT_EQ(1, destroy_a2dp_called);
130  ASSERT_EQ(1, cras_iodev_free_resources_called);
131
132  cras_bt_device_name_ret = fake_device_name;
133  /* Assert iodev name matches the bt device's name */
134  iodev = a2dp_iodev_create(fake_transport);
135  ASSERT_STREQ(fake_device_name, iodev->info.name);
136
137  a2dp_iodev_destroy(iodev);
138}
139
140TEST(A2dpIoInit, InitializeFail) {
141  struct cras_iodev *iodev;
142
143  ResetStubData();
144
145  init_a2dp_return_val = -1;
146  iodev = a2dp_iodev_create(fake_transport);
147
148  ASSERT_EQ(iodev, (void *)NULL);
149  ASSERT_EQ(1, cras_bt_transport_configuration_called);
150  ASSERT_EQ(1, init_a2dp_called);
151  ASSERT_EQ(0, cras_bt_device_append_iodev_called);
152  ASSERT_EQ(0, cras_iodev_add_node_called);
153  ASSERT_EQ(0, cras_iodev_set_active_node_called);
154  ASSERT_EQ(0, cras_iodev_rm_node_called);
155}
156
157TEST(A2dpIoInit, OpenIodev) {
158  struct cras_iodev *iodev;
159
160  ResetStubData();
161  iodev = a2dp_iodev_create(fake_transport);
162
163  iodev_set_format(iodev, &format);
164  iodev->open_dev(iodev);
165
166  ASSERT_EQ(1, cras_bt_transport_acquire_called);
167
168  iodev->close_dev(iodev);
169  ASSERT_EQ(1, cras_bt_transport_release_called);
170  ASSERT_EQ(1, drain_a2dp_called);
171  ASSERT_EQ(1, cras_iodev_free_format_called);
172
173  a2dp_iodev_destroy(iodev);
174}
175
176TEST(A2dpIoInit, GetPutBuffer) {
177  struct cras_iodev *iodev;
178  struct cras_audio_area *area1, *area2, *area3;
179  uint8_t *area1_buf;
180  unsigned frames;
181
182  ResetStubData();
183  iodev = a2dp_iodev_create(fake_transport);
184
185  iodev_set_format(iodev, &format);
186  iodev->open_dev(iodev);
187  ASSERT_NE(write_callback, (void *)NULL);
188
189  frames = 256;
190  iodev->get_buffer(iodev, &area1, &frames);
191  ASSERT_EQ(256, frames);
192  ASSERT_EQ(256, area1->frames);
193  area1_buf = area1->channels[0].buf;
194
195  /* Test 100 frames(400 bytes) put and all processed. */
196  a2dp_encode_processed_bytes_val[0] = 4096 * 4;
197  a2dp_encode_processed_bytes_val[1] = 400;
198  a2dp_write_index = 0;
199  a2dp_write_return_val[0] = -EAGAIN;
200  a2dp_write_return_val[1] = 400;
201  iodev->put_buffer(iodev, 100);
202  write_callback(write_callback_data);
203  // Start with 4k frames.
204  EXPECT_EQ(4096, pcm_buf_size_val[0]);
205  EXPECT_EQ(400, pcm_buf_size_val[1]);
206
207  iodev->get_buffer(iodev, &area2, &frames);
208  ASSERT_EQ(256, frames);
209  ASSERT_EQ(256, area2->frames);
210
211  /* Assert buf2 points to the same position as buf1 */
212  ASSERT_EQ(400, area2->channels[0].buf - area1_buf);
213
214  /* Test 100 frames(400 bytes) put, only 360 bytes processed,
215   * 40 bytes left in pcm buffer.
216   */
217  a2dp_encode_index = 0;
218  a2dp_encode_processed_bytes_val[0] = 360;
219  a2dp_encode_processed_bytes_val[1] = 0;
220  a2dp_write_index = 0;
221  a2dp_write_return_val[0] = 360;
222  a2dp_write_return_val[1] = 0;
223  iodev->put_buffer(iodev, 100);
224  write_callback(write_callback_data);
225  EXPECT_EQ(400, pcm_buf_size_val[0]);
226  ASSERT_EQ(40, pcm_buf_size_val[1]);
227
228  iodev->get_buffer(iodev, &area3, &frames);
229
230  /* Existing buffer not completed processed, assert new buffer starts from
231   * current write pointer.
232   */
233  ASSERT_EQ(256, frames);
234  EXPECT_EQ(800, area3->channels[0].buf - area1_buf);
235
236  a2dp_iodev_destroy(iodev);
237}
238
239TEST(A2dpIoInif, FramesQueued) {
240  struct cras_iodev *iodev;
241  struct cras_audio_area *area;
242  unsigned frames;
243
244  ResetStubData();
245  iodev = a2dp_iodev_create(fake_transport);
246
247  iodev_set_format(iodev, &format);
248  time_now.tv_sec = 0;
249  time_now.tv_nsec = 0;
250  iodev->open_dev(iodev);
251  ASSERT_NE(write_callback, (void *)NULL);
252
253  frames = 256;
254  iodev->get_buffer(iodev, &area, &frames);
255  ASSERT_EQ(256, frames);
256  ASSERT_EQ(256, area->frames);
257
258  /* Put 100 frames, proccessed 400 bytes to a2dp buffer.
259   * Assume 200 bytes written out, queued 50 frames in a2dp buffer.
260   */
261  a2dp_encode_processed_bytes_val[0] = 400;
262  a2dp_encode_processed_bytes_val[1] = 0;
263  a2dp_write_return_val[0] = 200;
264  a2dp_write_return_val[1] = -EAGAIN;
265  a2dp_queued_frames_val = 50;
266  time_now.tv_sec = 0;
267  time_now.tv_nsec = 1000000;
268  iodev->put_buffer(iodev, 300);
269  write_callback(write_callback_data);
270  EXPECT_EQ(350, iodev->frames_queued(iodev));
271
272  /* After writing another 200 frames, check for correct buffer level. */
273  time_now.tv_sec = 0;
274  time_now.tv_nsec = 2000000;
275  a2dp_encode_index = 0;
276  a2dp_write_index = 0;
277  a2dp_encode_processed_bytes_val[0] = 800;
278  write_callback(write_callback_data);
279  /* 1000000 nsec has passed, estimated queued frames adjusted by 44 */
280  EXPECT_EQ(256, iodev->frames_queued(iodev));
281  EXPECT_EQ(1200, pcm_buf_size_val[0]);
282  EXPECT_EQ(400, pcm_buf_size_val[1]);
283
284  /* Queued frames and new put buffer are all written */
285  a2dp_encode_processed_bytes_val[0] = 400;
286  a2dp_encode_processed_bytes_val[1] = 0;
287  a2dp_encode_index = 0;
288  a2dp_write_return_val[0] = 400;
289  a2dp_write_return_val[1] = -EAGAIN;
290  a2dp_write_index = 0;
291
292  /* Add wnother 200 samples, get back to the original level. */
293  time_now.tv_sec = 0;
294  time_now.tv_nsec = 50000000;
295  a2dp_encode_processed_bytes_val[0] = 600;
296  iodev->put_buffer(iodev, 200);
297  EXPECT_EQ(1200, pcm_buf_size_val[0]);
298  EXPECT_EQ(200, iodev->frames_queued(iodev));
299}
300
301} // namespace
302
303int main(int argc, char **argv) {
304  ::testing::InitGoogleTest(&argc, argv);
305  return RUN_ALL_TESTS();
306}
307
308extern "C" {
309
310int cras_bt_transport_configuration(const struct cras_bt_transport *transport,
311                                    void *configuration, int len)
312{
313  cras_bt_transport_configuration_called++;
314  return 0;
315}
316
317int cras_bt_transport_acquire(struct cras_bt_transport *transport)
318{
319  cras_bt_transport_acquire_called++;
320  return 0;
321}
322
323int cras_bt_transport_release(struct cras_bt_transport *transport)
324{
325  cras_bt_transport_release_called++;
326  return 0;
327}
328
329int cras_bt_transport_fd(const struct cras_bt_transport *transport)
330{
331  return 0;
332}
333
334const char *cras_bt_transport_object_path(
335		const struct cras_bt_transport *transport)
336{
337  return FAKE_OBJECT_PATH;
338}
339
340uint16_t cras_bt_transport_write_mtu(const struct cras_bt_transport *transport)
341{
342  return cras_bt_transport_write_mtu_ret;
343}
344
345
346void cras_iodev_free_format(struct cras_iodev *iodev)
347{
348  cras_iodev_free_format_called++;
349}
350
351void cras_iodev_free_resources(struct cras_iodev *iodev)
352{
353  cras_iodev_free_resources_called++;
354}
355
356// Cras iodev
357void cras_iodev_add_node(struct cras_iodev *iodev, struct cras_ionode *node)
358{
359  cras_iodev_add_node_called++;
360  iodev->nodes = node;
361}
362
363void cras_iodev_rm_node(struct cras_iodev *iodev, struct cras_ionode *node)
364{
365  cras_iodev_rm_node_called++;
366  iodev->nodes = NULL;
367}
368
369void cras_iodev_set_active_node(struct cras_iodev *iodev,
370				struct cras_ionode *node)
371{
372  cras_iodev_set_active_node_called++;
373  iodev->active_node = node;
374}
375
376// From cras_bt_transport
377struct cras_bt_device *cras_bt_transport_device(
378	const struct cras_bt_transport *transport)
379{
380  return reinterpret_cast<struct cras_bt_device *>(0x456);;
381}
382
383enum cras_bt_device_profile cras_bt_transport_profile(
384  const struct cras_bt_transport *transport)
385{
386  return CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE;
387}
388
389// From cras_bt_device
390const char *cras_bt_device_name(const struct cras_bt_device *device)
391{
392  return cras_bt_device_name_ret;
393}
394
395const char *cras_bt_device_object_path(const struct cras_bt_device *device) {
396  return "/org/bluez/hci0/dev_1A_2B_3C_4D_5E_6F";
397}
398
399void cras_bt_device_append_iodev(struct cras_bt_device *device,
400                                 struct cras_iodev *iodev,
401                                 enum cras_bt_device_profile profile)
402{
403  cras_bt_device_append_iodev_called++;
404}
405
406void cras_bt_device_rm_iodev(struct cras_bt_device *device,
407                             struct cras_iodev *iodev)
408{
409  cras_bt_device_rm_iodev_called++;
410}
411
412int init_a2dp(struct a2dp_info *a2dp, a2dp_sbc_t *sbc)
413{
414  init_a2dp_called++;
415  return init_a2dp_return_val;
416}
417
418void destroy_a2dp(struct a2dp_info *a2dp)
419{
420  destroy_a2dp_called++;
421}
422
423int a2dp_codesize(struct a2dp_info *a2dp)
424{
425  return 512;
426}
427
428int a2dp_block_size(struct a2dp_info *a2dp, int encoded_bytes)
429{
430  a2dp_block_size_called++;
431
432  // Assumes a2dp block size is 1:1 before/after encode.
433  return encoded_bytes;
434}
435
436int a2dp_queued_frames(struct a2dp_info *a2dp)
437{
438  return a2dp_queued_frames_val;
439}
440
441void a2dp_drain(struct a2dp_info *a2dp)
442{
443  drain_a2dp_called++;
444}
445
446int a2dp_encode(struct a2dp_info *a2dp, const void *pcm_buf, int pcm_buf_size,
447                int format_bytes, size_t link_mtu) {
448  unsigned int processed;
449
450  if (a2dp_encode_index == MAX_A2DP_ENCODE_CALLS)
451    return 0;
452  processed = a2dp_encode_processed_bytes_val[a2dp_encode_index];
453  pcm_buf_size_val[a2dp_encode_index] = pcm_buf_size;
454  a2dp_encode_index++;
455  return processed;
456}
457
458int a2dp_write(struct a2dp_info *a2dp, int stream_fd, size_t link_mtu) {
459  return a2dp_write_return_val[a2dp_write_index++];;
460}
461
462int clock_gettime(clockid_t clk_id, struct timespec *tp) {
463  *tp = time_now;
464  return 0;
465}
466
467void cras_iodev_init_audio_area(struct cras_iodev *iodev,
468                                int num_channels) {
469  iodev->area = dummy_audio_area;
470}
471
472void cras_iodev_free_audio_area(struct cras_iodev *iodev) {
473}
474
475void cras_audio_area_config_buf_pointers(struct cras_audio_area *area,
476					 const struct cras_audio_format *fmt,
477					 uint8_t *base_buffer)
478{
479  dummy_audio_area->channels[0].buf = base_buffer;
480}
481
482// From audio_thread
483struct audio_thread_event_log *atlog;
484
485void audio_thread_add_write_callback(int fd, thread_callback cb, void *data) {
486  write_callback = cb;
487  write_callback_data = data;
488}
489
490void audio_thread_rm_callback(int fd) {
491}
492
493void audio_thread_enable_callback(int fd, int enabled) {
494}
495
496// From a2dp endpoint
497int cras_a2dp_has_suspend_timer()
498{
499  return 0;
500}
501
502void cras_a2dp_schedule_suspend_timer(struct cras_iodev *iodev,
503                                      unsigned int msec)
504{
505}
506
507void cras_a2dp_cancel_suspend_timer(struct cras_iodev *iodev)
508{
509}
510
511}
512