• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 extern "C" {
9 #include "cras_messages.h"
10 
11 //  Include C file to test static functions.
12 #include "cras_client.c"
13 }
14 
15 static const cras_stream_id_t FIRST_STREAM_ID = 1;
16 
17 static int pthread_create_called;
18 static int pthread_join_called;
19 static int pthread_cond_timedwait_called;
20 static int pthread_cond_timedwait_retval;
21 static int close_called;
22 static int sendmsg_called;
23 static void* mmap_return_value;
24 static int samples_ready_called;
25 static int samples_ready_frames_value;
26 static uint8_t* samples_ready_samples_value;
27 
28 static int pthread_create_returned_value;
29 
30 namespace {
31 
InitStaticVariables()32 void InitStaticVariables() {
33   pthread_create_called = 0;
34   pthread_join_called = 0;
35   pthread_cond_timedwait_called = 0;
36   pthread_cond_timedwait_retval = 0;
37   close_called = 0;
38   sendmsg_called = 0;
39   pthread_create_returned_value = 0;
40   mmap_return_value = NULL;
41   samples_ready_called = 0;
42   samples_ready_frames_value = 0;
43 }
44 
45 class CrasClientTestSuite : public testing::Test {
46  protected:
InitShm()47   struct cras_audio_shm* InitShm() {
48     struct cras_audio_shm* shm =
49         static_cast<struct cras_audio_shm*>(calloc(1, sizeof(*shm)));
50     shm->header =
51         static_cast<cras_audio_shm_header*>(calloc(1, sizeof(*shm->header)));
52     cras_shm_set_frame_bytes(shm, 4);
53     uint32_t used_size = shm_writable_frames_ * 4;
54     cras_shm_set_used_size(shm, used_size);
55     shm->samples_info.length = used_size * 2;
56     memcpy(&shm->header->config, &shm->config, sizeof(shm->config));
57     return shm;
58   }
59 
SetUp()60   virtual void SetUp() {
61     shm_writable_frames_ = 100;
62     InitStaticVariables();
63 
64     memset(&client_, 0, sizeof(client_));
65     client_.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
66     memset(&stream_, 0, sizeof(stream_));
67     stream_.id = FIRST_STREAM_ID;
68 
69     struct cras_stream_params* config =
70         static_cast<cras_stream_params*>(calloc(1, sizeof(*config)));
71     config->buffer_frames = 1024;
72     config->cb_threshold = 512;
73     stream_.config = config;
74   }
75 
TearDown()76   virtual void TearDown() {
77     if (stream_.config) {
78       free(stream_.config);
79       stream_.config = NULL;
80     }
81 
82     if (stream_.shm) {
83       free(stream_.shm->header);
84     }
85     free(stream_.shm);
86     stream_.shm = NULL;
87   }
88 
89   void StreamConnected(CRAS_STREAM_DIRECTION direction);
90 
91   void StreamConnectedFail(CRAS_STREAM_DIRECTION direction);
92 
93   struct client_stream stream_;
94   struct cras_client client_;
95   int shm_writable_frames_;
96 };
97 
set_audio_format(struct cras_audio_format * format,snd_pcm_format_t pcm_format,size_t frame_rate,size_t num_channels)98 void set_audio_format(struct cras_audio_format* format,
99                       snd_pcm_format_t pcm_format,
100                       size_t frame_rate,
101                       size_t num_channels) {
102   format->format = pcm_format;
103   format->frame_rate = frame_rate;
104   format->num_channels = num_channels;
105   for (size_t i = 0; i < CRAS_CH_MAX; ++i)
106     format->channel_layout[i] = i < num_channels ? i : -1;
107 }
108 
capture_samples_ready(cras_client * client,cras_stream_id_t stream_id,uint8_t * samples,size_t frames,const timespec * sample_ts,void * arg)109 int capture_samples_ready(cras_client* client,
110                           cras_stream_id_t stream_id,
111                           uint8_t* samples,
112                           size_t frames,
113                           const timespec* sample_ts,
114                           void* arg) {
115   samples_ready_called++;
116   samples_ready_samples_value = samples;
117   samples_ready_frames_value = frames;
118   return frames;
119 }
120 
TEST_F(CrasClientTestSuite,HandleCaptureDataReady)121 TEST_F(CrasClientTestSuite, HandleCaptureDataReady) {
122   struct cras_audio_shm* shm;
123 
124   stream_.direction = CRAS_STREAM_INPUT;
125 
126   shm_writable_frames_ = 480;
127   shm = InitShm();
128   stream_.shm = shm;
129   stream_.config->buffer_frames = 480;
130   stream_.config->cb_threshold = 480;
131   stream_.config->aud_cb = capture_samples_ready;
132   stream_.config->unified_cb = 0;
133 
134   shm->header->write_buf_idx = 0;
135   shm->header->read_buf_idx = 0;
136   shm->header->write_offset[0] = 480 * 4;
137   shm->header->read_offset[0] = 0;
138 
139   /* Normal scenario: read buffer has full of data written,
140    * handle_capture_data_ready() should consume all 480 frames and move
141    * read_buf_idx to the next buffer. */
142   handle_capture_data_ready(&stream_, 480);
143   EXPECT_EQ(1, samples_ready_called);
144   EXPECT_EQ(480, samples_ready_frames_value);
145   EXPECT_EQ(cras_shm_buff_for_idx(shm, 0), samples_ready_samples_value);
146   EXPECT_EQ(1, shm->header->read_buf_idx);
147   EXPECT_EQ(0, shm->header->write_offset[0]);
148   EXPECT_EQ(0, shm->header->read_offset[0]);
149 
150   /* At the beginning of overrun: handle_capture_data_ready() should not
151    * proceed to call audio_cb because there's no data captured. */
152   shm->header->read_buf_idx = 0;
153   shm->header->write_offset[0] = 0;
154   shm->header->read_offset[0] = 0;
155   handle_capture_data_ready(&stream_, 480);
156   EXPECT_EQ(1, samples_ready_called);
157   EXPECT_EQ(0, shm->header->read_buf_idx);
158 
159   /* In the middle of overrun: partially written buffer should trigger
160    * audio_cb, feed the full-sized read buffer to client. */
161   shm->header->read_buf_idx = 0;
162   shm->header->write_offset[0] = 123;
163   shm->header->read_offset[0] = 0;
164   handle_capture_data_ready(&stream_, 480);
165   EXPECT_EQ(1, samples_ready_called);
166   EXPECT_EQ(0, shm->header->read_buf_idx);
167 }
168 
StreamConnected(CRAS_STREAM_DIRECTION direction)169 void CrasClientTestSuite::StreamConnected(CRAS_STREAM_DIRECTION direction) {
170   struct cras_client_stream_connected msg;
171   int shm_fds[2] = {0, 1};
172   int shm_max_size = 600;
173   size_t format_bytes;
174   size_t effects = 123;
175   struct cras_audio_shm_header* header;
176 
177   stream_.direction = direction;
178   set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
179 
180   struct cras_audio_format server_format;
181   set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
182 
183   // Initialize shm area
184   format_bytes = cras_get_format_bytes(&server_format);
185   header = (struct cras_audio_shm_header*)calloc(1, sizeof(*header));
186   header->config.frame_bytes = format_bytes;
187   header->config.used_size = shm_writable_frames_ * format_bytes;
188 
189   mmap_return_value = header;
190 
191   cras_fill_client_stream_connected(&msg, 0, stream_.id, &server_format,
192                                     shm_max_size, effects);
193 
194   stream_connected(&stream_, &msg, shm_fds, 2);
195 
196   EXPECT_EQ(CRAS_THREAD_RUNNING, stream_.thread.state);
197   EXPECT_EQ(header, stream_.shm->header);
198 }
199 
TEST_F(CrasClientTestSuite,InputStreamConnected)200 TEST_F(CrasClientTestSuite, InputStreamConnected) {
201   StreamConnected(CRAS_STREAM_INPUT);
202 }
203 
TEST_F(CrasClientTestSuite,OutputStreamConnected)204 TEST_F(CrasClientTestSuite, OutputStreamConnected) {
205   StreamConnected(CRAS_STREAM_OUTPUT);
206 }
207 
StreamConnectedFail(CRAS_STREAM_DIRECTION direction)208 void CrasClientTestSuite::StreamConnectedFail(CRAS_STREAM_DIRECTION direction) {
209   struct cras_client_stream_connected msg;
210   int shm_fds[2] = {0, 1};
211   int shm_max_size = 600;
212   size_t format_bytes;
213   size_t effects = 123;
214   struct cras_audio_shm_header header;
215   int rc;
216 
217   stream_.direction = direction;
218   set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
219 
220   struct cras_audio_format server_format;
221   set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
222 
223   // Thread setup
224   rc = pipe(stream_.wake_fds);
225   ASSERT_EQ(0, rc);
226   stream_.thread.state = CRAS_THREAD_WARMUP;
227 
228   // Initialize shm area
229   format_bytes = cras_get_format_bytes(&server_format);
230   memset(&header, 0, sizeof(header));
231   header.config.frame_bytes = format_bytes;
232   header.config.used_size = shm_writable_frames_ * format_bytes;
233 
234   mmap_return_value = &header;
235 
236   // Put an error in the message.
237   cras_fill_client_stream_connected(&msg, 1, stream_.id, &server_format,
238                                     shm_max_size, effects);
239 
240   stream_connected(&stream_, &msg, shm_fds, 2);
241 
242   EXPECT_EQ(CRAS_THREAD_STOP, stream_.thread.state);
243   EXPECT_EQ(4, close_called);  // close the pipefds and shm_fds
244 }
245 
TEST_F(CrasClientTestSuite,InputStreamConnectedFail)246 TEST_F(CrasClientTestSuite, InputStreamConnectedFail) {
247   StreamConnectedFail(CRAS_STREAM_INPUT);
248 }
249 
TEST_F(CrasClientTestSuite,OutputStreamConnectedFail)250 TEST_F(CrasClientTestSuite, OutputStreamConnectedFail) {
251   StreamConnectedFail(CRAS_STREAM_OUTPUT);
252 }
253 
TEST_F(CrasClientTestSuite,AddAndRemoveStream)254 TEST_F(CrasClientTestSuite, AddAndRemoveStream) {
255   cras_stream_id_t stream_id;
256   struct cras_disconnect_stream_message msg;
257   int serv_fds[2];
258   int rc;
259 
260   // Dynamically allocate the stream so that it can be freed later.
261   struct client_stream* stream_ptr =
262       (struct client_stream*)malloc(sizeof(*stream_ptr));
263   memcpy(stream_ptr, &stream_, sizeof(client_stream));
264 
265   stream_ptr->config =
266       (struct cras_stream_params*)malloc(sizeof(*(stream_ptr->config)));
267   memcpy(stream_ptr->config, stream_.config, sizeof(*(stream_.config)));
268 
269   stream_ptr->wake_fds[0] = -1;
270   stream_ptr->wake_fds[1] = -1;
271 
272   pthread_cond_timedwait_retval = ETIMEDOUT;
273   EXPECT_EQ(-ETIMEDOUT, client_thread_add_stream(&client_, stream_ptr,
274                                                  &stream_id, NO_DEVICE));
275   EXPECT_EQ(pthread_cond_timedwait_called, 1);
276   EXPECT_EQ(pthread_join_called, 0);
277 
278   InitStaticVariables();
279   EXPECT_EQ(
280       0, client_thread_add_stream(&client_, stream_ptr, &stream_id, NO_DEVICE));
281   EXPECT_EQ(&client_, stream_ptr->client);
282   EXPECT_EQ(stream_id, stream_ptr->id);
283   EXPECT_EQ(pthread_create_called, 1);
284   EXPECT_NE(-1, stream_ptr->wake_fds[0]);
285   EXPECT_NE(-1, stream_ptr->wake_fds[1]);
286   EXPECT_EQ(1, sendmsg_called);  // send connect message to server
287   EXPECT_EQ(stream_ptr, stream_from_id(&client_, stream_id));
288 
289   stream_ptr->thread.state = CRAS_THREAD_RUNNING;
290 
291   rc = pipe(serv_fds);
292   EXPECT_EQ(0, rc);
293   client_.server_fd = serv_fds[1];
294   client_.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
295   EXPECT_EQ(0, client_thread_rm_stream(&client_, stream_id));
296 
297   rc = read(serv_fds[0], &msg, sizeof(msg));
298   EXPECT_EQ(sizeof(msg), rc);
299   EXPECT_EQ(stream_id, msg.stream_id);
300   EXPECT_EQ(1, pthread_join_called);
301 
302   EXPECT_EQ(NULL, stream_from_id(&client_, stream_id));
303 }
304 
TEST_F(CrasClientTestSuite,SetOutputStreamVolume)305 TEST_F(CrasClientTestSuite, SetOutputStreamVolume) {
306   cras_stream_id_t stream_id;
307 
308   client_thread_add_stream(&client_, &stream_, &stream_id, NO_DEVICE);
309   EXPECT_EQ(&stream_, stream_from_id(&client_, stream_id));
310 
311   /* Set volume before stream connected. */
312   client_thread_set_stream_volume(&client_, stream_id, 0.3f);
313   StreamConnected(CRAS_STREAM_OUTPUT);
314   EXPECT_EQ(0.3f, cras_shm_get_volume_scaler(stream_.shm));
315 
316   /* Set volume after stream connected. */
317   client_thread_set_stream_volume(&client_, stream_id, 0.6f);
318   EXPECT_EQ(0.6f, cras_shm_get_volume_scaler(stream_.shm));
319 }
320 
TEST_F(CrasClientTestSuite,SetInputStreamVolume)321 TEST_F(CrasClientTestSuite, SetInputStreamVolume) {
322   cras_stream_id_t stream_id;
323 
324   client_thread_add_stream(&client_, &stream_, &stream_id, NO_DEVICE);
325   EXPECT_EQ(&stream_, stream_from_id(&client_, stream_id));
326 
327   /* Set volume before stream connected. */
328   client_thread_set_stream_volume(&client_, stream_id, 0.3f);
329   StreamConnected(CRAS_STREAM_INPUT);
330   EXPECT_EQ(0.3f, cras_shm_get_volume_scaler(stream_.shm));
331 
332   /* Set volume after stream connected. */
333   client_thread_set_stream_volume(&client_, stream_id, 0.6f);
334   EXPECT_EQ(0.6f, cras_shm_get_volume_scaler(stream_.shm));
335 }
336 
TEST(CrasClientTest,InitStreamVolume)337 TEST(CrasClientTest, InitStreamVolume) {
338   cras_stream_id_t stream_id;
339   struct cras_stream_params config;
340   struct add_stream_command_message cmd_msg;
341   int rc;
342   struct cras_client client;
343 
344   memset(&client, 0, sizeof(client));
345   memset(&config, 0, sizeof(config));
346   client.server_fd_state = CRAS_SOCKET_STATE_CONNECTED;
347 
348   config.aud_cb = reinterpret_cast<cras_playback_cb_t>(0x123);
349   config.err_cb = reinterpret_cast<cras_error_cb_t>(0x456);
350   client.thread.state = CRAS_THREAD_RUNNING;
351   rc = pipe(client.command_reply_fds);
352   EXPECT_EQ(0, rc);
353   rc = pipe(client.command_fds);
354   EXPECT_EQ(0, rc);
355 
356   rc = write(client.command_reply_fds[1], &rc, sizeof(rc));
357   cras_client_add_stream(&client, &stream_id, &config);
358 
359   rc = read(client.command_fds[0], &cmd_msg, sizeof(cmd_msg));
360   EXPECT_EQ(sizeof(cmd_msg), rc);
361   EXPECT_NE((void*)NULL, cmd_msg.stream);
362 
363   EXPECT_EQ(1.0f, cmd_msg.stream->volume_scaler);
364 
365   if (cmd_msg.stream->config)
366     free(cmd_msg.stream->config);
367   free(cmd_msg.stream);
368 }
369 
370 }  // namespace
371 
main(int argc,char ** argv)372 int main(int argc, char** argv) {
373   ::testing::InitGoogleTest(&argc, argv);
374   return RUN_ALL_TESTS();
375 }
376 
377 /* stubs */
378 extern "C" {
379 
sendmsg(int sockfd,const struct msghdr * msg,int flags)380 ssize_t sendmsg(int sockfd, const struct msghdr* msg, int flags) {
381   ++sendmsg_called;
382   return msg->msg_iov->iov_len;
383 }
384 
close(int fd)385 int close(int fd) {
386   ++close_called;
387   return 0;
388 }
389 
pthread_create(pthread_t * thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)390 int pthread_create(pthread_t* thread,
391                    const pthread_attr_t* attr,
392                    void* (*start_routine)(void*),
393                    void* arg) {
394   ++pthread_create_called;
395   return pthread_create_returned_value;
396 }
397 
pthread_join(pthread_t thread,void ** retval)398 int pthread_join(pthread_t thread, void** retval) {
399   ++pthread_join_called;
400   return 0;
401 }
402 
pthread_cond_timedwait(pthread_cond_t * __restrict cond,pthread_mutex_t * __restrict mutex,const struct timespec * __restrict timeout)403 int pthread_cond_timedwait(pthread_cond_t* __restrict cond,
404                            pthread_mutex_t* __restrict mutex,
405                            const struct timespec* __restrict timeout) {
406   ++pthread_cond_timedwait_called;
407   return pthread_cond_timedwait_retval;
408 }
409 
clock_gettime(clockid_t clk_id,struct timespec * tp)410 int clock_gettime(clockid_t clk_id, struct timespec* tp) {
411   tp->tv_sec = 0;
412   tp->tv_nsec = 0;
413   return 0;
414 }
415 
mmap(void * addr,size_t length,int prot,int flags,int fd,off_t offset)416 void* mmap(void* addr,
417            size_t length,
418            int prot,
419            int flags,
420            int fd,
421            off_t offset) {
422   return mmap_return_value;
423 }
424 
cras_audio_format_create(snd_pcm_format_t format,size_t frame_rate,size_t num_channels)425 struct cras_audio_format* cras_audio_format_create(snd_pcm_format_t format,
426                                                    size_t frame_rate,
427                                                    size_t num_channels) {
428   return reinterpret_cast<struct cras_audio_format*>(0x123);
429 }
430 
cras_audio_format_destroy(struct cras_audio_format * fmt)431 void cras_audio_format_destroy(struct cras_audio_format* fmt) {}
432 }
433