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 <gtest/gtest.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8
9 extern "C" {
10 // For audio_thread_log.h use.
11 struct audio_thread_event_log* atlog;
12 int atlog_rw_shm_fd;
13 int atlog_ro_shm_fd;
14 #include "audio_thread_log.h"
15 #include "cras_audio_area.h"
16 #include "cras_iodev.h"
17 #include "cras_iodev_list.h"
18 #include "cras_loopback_iodev.h"
19 #include "cras_shm.h"
20 #include "cras_types.h"
21 #include "dev_stream.h"
22 #include "utlist.h"
23 }
24
25 namespace {
26
27 static const unsigned int kBufferFrames = 16384;
28 static const unsigned int kFrameBytes = 4;
29 static const unsigned int kBufferSize = kBufferFrames * kFrameBytes;
30
31 static struct timespec time_now;
32 static cras_audio_area* mock_audio_area;
33 static loopback_hook_data_t loop_hook;
34 static struct cras_iodev* enabled_dev;
35 static unsigned int cras_iodev_list_add_input_called;
36 static unsigned int cras_iodev_list_rm_input_called;
37 static unsigned int cras_iodev_list_set_device_enabled_callback_called;
38 static device_enabled_callback_t device_enabled_callback_cb;
39 static device_disabled_callback_t device_disabled_callback_cb;
40 static void* device_enabled_callback_cb_data;
41 static int cras_iodev_list_register_loopback_called;
42 static int cras_iodev_list_unregister_loopback_called;
43
44 static char* atlog_name;
45
46 class LoopBackTestSuite : public testing::Test {
47 protected:
SetUp()48 virtual void SetUp() {
49 mock_audio_area = (cras_audio_area*)calloc(
50 1, sizeof(*mock_audio_area) + sizeof(cras_channel_area) * 2);
51 for (unsigned int i = 0; i < kBufferSize; i++) {
52 buf_[i] = rand();
53 }
54 fmt_.frame_rate = 48000;
55 fmt_.num_channels = 2;
56 fmt_.format = SND_PCM_FORMAT_S16_LE;
57
58 loop_in_ = loopback_iodev_create(LOOPBACK_POST_MIX_PRE_DSP);
59 EXPECT_EQ(1, cras_iodev_list_add_input_called);
60 loop_in_->format = &fmt_;
61
62 loop_hook = NULL;
63 cras_iodev_list_add_input_called = 0;
64 cras_iodev_list_rm_input_called = 0;
65 cras_iodev_list_set_device_enabled_callback_called = 0;
66 cras_iodev_list_register_loopback_called = 0;
67 cras_iodev_list_unregister_loopback_called = 0;
68
69 ASSERT_FALSE(asprintf(&atlog_name, "/ATlog-%d", getpid()) < 0);
70 /* To avoid un-used variable warning. */
71 atlog_rw_shm_fd = atlog_ro_shm_fd = -1;
72 atlog = audio_thread_event_log_init(atlog_name);
73 }
74
TearDown()75 virtual void TearDown() {
76 loopback_iodev_destroy(loop_in_);
77 EXPECT_EQ(1, cras_iodev_list_rm_input_called);
78 EXPECT_EQ(NULL, device_enabled_callback_cb);
79 EXPECT_EQ(NULL, device_disabled_callback_cb);
80 free(mock_audio_area);
81 audio_thread_event_log_deinit(atlog, atlog_name);
82 free(atlog_name);
83 }
84
85 uint8_t buf_[kBufferSize];
86 struct cras_audio_format fmt_;
87 struct cras_iodev* loop_in_;
88 };
89
TEST_F(LoopBackTestSuite,InstallLoopHook)90 TEST_F(LoopBackTestSuite, InstallLoopHook) {
91 struct cras_iodev iodev;
92 struct timespec tstamp;
93
94 iodev.direction = CRAS_STREAM_OUTPUT;
95 iodev.format = &fmt_;
96 iodev.streams = NULL;
97 iodev.info.idx = 123;
98 enabled_dev = &iodev;
99
100 // Open loopback devices.
101 EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
102 EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
103 EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
104
105 // Signal an output device is enabled.
106 device_enabled_callback_cb(&iodev, device_enabled_callback_cb_data);
107
108 // Expect that a hook was added to the iodev
109 EXPECT_EQ(2, cras_iodev_list_register_loopback_called);
110 ASSERT_NE(reinterpret_cast<loopback_hook_data_t>(NULL), loop_hook);
111
112 // Check zero frames queued.
113 EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
114
115 device_disabled_callback_cb(&iodev, device_enabled_callback_cb_data);
116 EXPECT_EQ(1, cras_iodev_list_unregister_loopback_called);
117 EXPECT_EQ(3, cras_iodev_list_register_loopback_called);
118
119 enabled_dev->info.idx = 456;
120 device_enabled_callback_cb(&iodev, device_enabled_callback_cb_data);
121 EXPECT_EQ(4, cras_iodev_list_register_loopback_called);
122
123 // Close loopback devices.
124 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
125 EXPECT_EQ(2, cras_iodev_list_unregister_loopback_called);
126 EXPECT_EQ(2, cras_iodev_list_set_device_enabled_callback_called);
127 }
128
TEST_F(LoopBackTestSuite,SelectDevFromAToB)129 TEST_F(LoopBackTestSuite, SelectDevFromAToB) {
130 struct cras_iodev iodev1, iodev2;
131
132 iodev1.direction = CRAS_STREAM_OUTPUT;
133 iodev2.direction = CRAS_STREAM_OUTPUT;
134 enabled_dev = &iodev1;
135
136 enabled_dev->info.idx = 111;
137 EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
138 EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
139 EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
140
141 /* Not the current sender being disabled, assert unregister not called. */
142 iodev2.info.idx = 222;
143 device_disabled_callback_cb(&iodev2, device_enabled_callback_cb_data);
144 EXPECT_EQ(0, cras_iodev_list_unregister_loopback_called);
145 EXPECT_EQ(1, cras_iodev_list_register_loopback_called);
146
147 enabled_dev = &iodev2;
148 device_disabled_callback_cb(&iodev1, device_enabled_callback_cb_data);
149 EXPECT_EQ(1, cras_iodev_list_unregister_loopback_called);
150 EXPECT_EQ(2, cras_iodev_list_register_loopback_called);
151
152 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
153 }
154
155 // Test how loopback works if there isn't any output devices open.
TEST_F(LoopBackTestSuite,OpenIdleSystem)156 TEST_F(LoopBackTestSuite, OpenIdleSystem) {
157 cras_audio_area* area;
158 unsigned int nread = 1024;
159 struct timespec tstamp;
160 int rc;
161
162 // No active output device.
163 enabled_dev = NULL;
164 time_now.tv_sec = 100;
165 time_now.tv_nsec = 0;
166
167 EXPECT_EQ(0, loop_in_->configure_dev(loop_in_));
168 EXPECT_EQ(1, cras_iodev_list_set_device_enabled_callback_called);
169
170 // Should be 480 samples after 480/frame rate seconds
171 time_now.tv_nsec += 480 * 1e9 / 48000;
172 EXPECT_EQ(480, loop_in_->frames_queued(loop_in_, &tstamp));
173
174 // Verify frames from loopback record.
175 loop_in_->get_buffer(loop_in_, &area, &nread);
176 EXPECT_EQ(480, nread);
177 memset(buf_, 0, nread * kFrameBytes);
178 rc = memcmp(area->channels[0].buf, buf_, nread * kFrameBytes);
179 EXPECT_EQ(0, rc);
180 loop_in_->put_buffer(loop_in_, nread);
181
182 // Check zero frames queued.
183 EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
184
185 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
186 }
187
TEST_F(LoopBackTestSuite,SimpleLoopback)188 TEST_F(LoopBackTestSuite, SimpleLoopback) {
189 cras_audio_area* area;
190 unsigned int nframes = 1024;
191 unsigned int nread = 1024;
192 int rc;
193 struct cras_iodev iodev;
194 struct dev_stream stream;
195 struct timespec tstamp;
196
197 iodev.streams = &stream;
198 enabled_dev = &iodev;
199
200 loop_in_->configure_dev(loop_in_);
201 ASSERT_NE(reinterpret_cast<void*>(NULL), loop_hook);
202
203 // Loopback callback for the hook.
204 loop_hook(buf_, nframes, &fmt_, loop_in_);
205
206 // Verify frames from loopback record.
207 loop_in_->get_buffer(loop_in_, &area, &nread);
208 EXPECT_EQ(nframes, nread);
209 rc = memcmp(area->channels[0].buf, buf_, nframes * kFrameBytes);
210 EXPECT_EQ(0, rc);
211 loop_in_->put_buffer(loop_in_, nread);
212
213 // Check zero frames queued.
214 EXPECT_EQ(0, loop_in_->frames_queued(loop_in_, &tstamp));
215
216 EXPECT_EQ(0, loop_in_->close_dev(loop_in_));
217 }
218
219 // TODO(chinyue): Test closing last iodev while streaming loopback data.
220
221 /* Stubs */
222 extern "C" {
223
cras_audio_area_config_buf_pointers(struct cras_audio_area * area,const struct cras_audio_format * fmt,uint8_t * base_buffer)224 void cras_audio_area_config_buf_pointers(struct cras_audio_area* area,
225 const struct cras_audio_format* fmt,
226 uint8_t* base_buffer) {
227 mock_audio_area->channels[0].buf = base_buffer;
228 }
229
cras_iodev_free_audio_area(struct cras_iodev * iodev)230 void cras_iodev_free_audio_area(struct cras_iodev* iodev) {}
231
cras_iodev_free_format(struct cras_iodev * iodev)232 void cras_iodev_free_format(struct cras_iodev* iodev) {}
233
cras_iodev_init_audio_area(struct cras_iodev * iodev,int num_channels)234 void cras_iodev_init_audio_area(struct cras_iodev* iodev, int num_channels) {
235 iodev->area = mock_audio_area;
236 }
237
cras_iodev_add_node(struct cras_iodev * iodev,struct cras_ionode * node)238 void cras_iodev_add_node(struct cras_iodev* iodev, struct cras_ionode* node) {
239 DL_APPEND(iodev->nodes, node);
240 }
241
cras_iodev_set_active_node(struct cras_iodev * iodev,struct cras_ionode * node)242 void cras_iodev_set_active_node(struct cras_iodev* iodev,
243 struct cras_ionode* node) {}
cras_iodev_list_register_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,unsigned int output_dev_idx,loopback_hook_data_t hook_data,loopback_hook_control_t hook_start,unsigned int loopback_dev_idx)244 void cras_iodev_list_register_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,
245 unsigned int output_dev_idx,
246 loopback_hook_data_t hook_data,
247 loopback_hook_control_t hook_start,
248 unsigned int loopback_dev_idx) {
249 cras_iodev_list_register_loopback_called++;
250 loop_hook = hook_data;
251 }
252
cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,unsigned int output_dev_idx,unsigned int loopback_dev_idx)253 void cras_iodev_list_unregister_loopback(enum CRAS_LOOPBACK_TYPE loopback_type,
254 unsigned int output_dev_idx,
255 unsigned int loopback_dev_idx) {
256 cras_iodev_list_unregister_loopback_called++;
257 }
258
cras_iodev_list_add_input(struct cras_iodev * input)259 int cras_iodev_list_add_input(struct cras_iodev* input) {
260 cras_iodev_list_add_input_called++;
261 return 0;
262 }
263
cras_iodev_list_rm_input(struct cras_iodev * input)264 int cras_iodev_list_rm_input(struct cras_iodev* input) {
265 cras_iodev_list_rm_input_called++;
266 return 0;
267 }
268
cras_iodev_list_set_device_enabled_callback(device_enabled_callback_t enabled_cb,device_disabled_callback_t disabled_cb,void * cb_data)269 int cras_iodev_list_set_device_enabled_callback(
270 device_enabled_callback_t enabled_cb,
271 device_disabled_callback_t disabled_cb,
272 void* cb_data) {
273 cras_iodev_list_set_device_enabled_callback_called++;
274 device_enabled_callback_cb = enabled_cb;
275 device_disabled_callback_cb = disabled_cb;
276 device_enabled_callback_cb_data = cb_data;
277 return 0;
278 }
279
clock_gettime(clockid_t clk_id,struct timespec * tp)280 int clock_gettime(clockid_t clk_id, struct timespec* tp) {
281 *tp = time_now;
282 return 0;
283 }
284
cras_iodev_list_get_first_enabled_iodev(enum CRAS_STREAM_DIRECTION direction)285 struct cras_iodev* cras_iodev_list_get_first_enabled_iodev(
286 enum CRAS_STREAM_DIRECTION direction) {
287 return enabled_dev;
288 }
289
290 } // extern "C"
291
292 } // namespace
293
main(int argc,char ** argv)294 int main(int argc, char** argv) {
295 ::testing::InitGoogleTest(&argc, argv);
296 return RUN_ALL_TESTS();
297 }
298