1 // Copyright 2019 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 <unistd.h>
8
9 extern "C" {
10 #include "audio_thread.h"
11 #include "cras_bt_log.h"
12 #include "cras_messages.h"
13 #include "cras_rclient.h"
14 #include "cras_rstream.h"
15 #include "cras_system_state.h"
16
17 // Access to data structures and static functions.
18 #include "cras_playback_rclient.c"
19 #include "cras_rclient_util.c"
20 }
21 static bool audio_format_valid;
22 static unsigned int cras_make_fd_nonblocking_called;
23 static unsigned int cras_observer_remove_called;
24 static int stream_list_add_called;
25 static int stream_list_add_return;
26 static unsigned int stream_list_rm_called;
27 static struct cras_audio_shm mock_shm;
28 static struct cras_rstream mock_rstream;
29
ResetStubData()30 void ResetStubData() {
31 audio_format_valid = true;
32 cras_make_fd_nonblocking_called = 0;
33 cras_observer_remove_called = 0;
34 stream_list_add_called = 0;
35 stream_list_add_return = 0;
36 stream_list_rm_called = 0;
37 }
38
39 namespace {
40
TEST(RClientSuite,CreateSendMessage)41 TEST(RClientSuite, CreateSendMessage) {
42 struct cras_rclient* rclient;
43 int rc;
44 struct cras_client_connected msg;
45 int pipe_fds[2];
46
47 ResetStubData();
48
49 rc = pipe(pipe_fds);
50 ASSERT_EQ(0, rc);
51
52 rclient = cras_playback_rclient_create(pipe_fds[1], 800);
53 ASSERT_NE((void*)NULL, rclient);
54 EXPECT_EQ(800, rclient->id);
55
56 rc = read(pipe_fds[0], &msg, sizeof(msg));
57 EXPECT_EQ(sizeof(msg), rc);
58 EXPECT_EQ(CRAS_CLIENT_CONNECTED, msg.header.id);
59
60 rclient->ops->destroy(rclient);
61 EXPECT_EQ(1, cras_observer_remove_called);
62 close(pipe_fds[0]);
63 close(pipe_fds[1]);
64 }
65
66 class CPRMessageSuite : public testing::Test {
67 protected:
SetUp()68 virtual void SetUp() {
69 int rc;
70 struct cras_client_connected msg;
71
72 rc = pipe(pipe_fds_);
73 if (rc < 0)
74 return;
75
76 rclient_ = cras_playback_rclient_create(pipe_fds_[1], 1);
77 rc = read(pipe_fds_[0], &msg, sizeof(msg));
78 if (rc < 0)
79 return;
80
81 fmt = {
82 .format = SND_PCM_FORMAT_S16_LE,
83 .frame_rate = 48000,
84 .num_channels = 2,
85 };
86 cras_audio_format_set_default_channel_layout(&fmt);
87 ResetStubData();
88 }
89
TearDown()90 virtual void TearDown() {
91 rclient_->ops->destroy(rclient_);
92 close(pipe_fds_[0]);
93 close(pipe_fds_[1]);
94 }
95
96 struct cras_rclient* rclient_;
97 struct cras_audio_format fmt;
98 int pipe_fds_[2];
99 int fd_;
100 };
101
TEST_F(CPRMessageSuite,StreamConnectMessage)102 TEST_F(CPRMessageSuite, StreamConnectMessage) {
103 struct cras_client_stream_connected out_msg;
104 int rc;
105
106 struct cras_connect_message msg;
107 cras_stream_id_t stream_id = 0x10002;
108 cras_fill_connect_message(&msg, CRAS_STREAM_OUTPUT, stream_id,
109 CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_UNKNOWN,
110 480, 240, /*flags=*/0, /*effects=*/0, fmt,
111 NO_DEVICE);
112 ASSERT_EQ(stream_id, msg.stream_id);
113
114 fd_ = 100;
115 rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
116 EXPECT_EQ(1, cras_make_fd_nonblocking_called);
117 EXPECT_EQ(1, stream_list_add_called);
118 EXPECT_EQ(0, stream_list_rm_called);
119
120 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
121 EXPECT_EQ(sizeof(out_msg), rc);
122 EXPECT_EQ(stream_id, out_msg.stream_id);
123 }
124
TEST_F(CPRMessageSuite,StreamConnectMessageInvalidDirection)125 TEST_F(CPRMessageSuite, StreamConnectMessageInvalidDirection) {
126 struct cras_client_stream_connected out_msg;
127 int rc;
128
129 struct cras_connect_message msg;
130 cras_stream_id_t stream_id = 0x10002;
131
132 for (int i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
133 const auto dir = static_cast<CRAS_STREAM_DIRECTION>(i);
134 if (dir == CRAS_STREAM_OUTPUT)
135 continue;
136 cras_fill_connect_message(&msg, dir, stream_id, CRAS_STREAM_TYPE_DEFAULT,
137 CRAS_CLIENT_TYPE_UNKNOWN, 480, 240, /*flags=*/0,
138 /*effects=*/0, fmt, NO_DEVICE);
139 ASSERT_EQ(stream_id, msg.stream_id);
140
141 fd_ = 100;
142 rc = rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_,
143 1);
144 EXPECT_EQ(0, rc);
145 EXPECT_EQ(0, cras_make_fd_nonblocking_called);
146 EXPECT_EQ(0, stream_list_add_called);
147 EXPECT_EQ(0, stream_list_rm_called);
148
149 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
150 EXPECT_EQ(sizeof(out_msg), rc);
151 EXPECT_EQ(-EINVAL, out_msg.err);
152 EXPECT_EQ(stream_id, out_msg.stream_id);
153 }
154 }
155
TEST_F(CPRMessageSuite,StreamConnectMessageInvalidClientId)156 TEST_F(CPRMessageSuite, StreamConnectMessageInvalidClientId) {
157 struct cras_client_stream_connected out_msg;
158 int rc;
159
160 struct cras_connect_message msg;
161 cras_stream_id_t stream_id = 0x20002; // stream_id with invalid client_id
162 cras_fill_connect_message(&msg, CRAS_STREAM_OUTPUT, stream_id,
163 CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_UNKNOWN,
164 480, 240, /*flags=*/0, /*effects=*/0, fmt,
165 NO_DEVICE);
166 ASSERT_EQ(stream_id, msg.stream_id);
167
168 fd_ = 100;
169 rc =
170 rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
171 EXPECT_EQ(0, rc);
172 EXPECT_EQ(0, cras_make_fd_nonblocking_called);
173 EXPECT_EQ(0, stream_list_add_called);
174 EXPECT_EQ(0, stream_list_rm_called);
175
176 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
177 EXPECT_EQ(sizeof(out_msg), rc);
178 EXPECT_EQ(-EINVAL, out_msg.err);
179 EXPECT_EQ(stream_id, out_msg.stream_id);
180 }
181
TEST_F(CPRMessageSuite,StreamConnectMessageInvalidAudioFormat)182 TEST_F(CPRMessageSuite, StreamConnectMessageInvalidAudioFormat) {
183 struct cras_client_stream_connected out_msg;
184 int rc;
185
186 struct cras_connect_message msg;
187 cras_stream_id_t stream_id = 0x10002;
188 cras_fill_connect_message(&msg, CRAS_STREAM_OUTPUT, stream_id,
189 CRAS_STREAM_TYPE_DEFAULT, CRAS_CLIENT_TYPE_UNKNOWN,
190 480, 240, /*flags=*/0, /*effects=*/0, fmt,
191 NO_DEVICE);
192 ASSERT_EQ(stream_id, msg.stream_id);
193
194 audio_format_valid = false; // stubs out verification failure.
195
196 fd_ = 100;
197 rc =
198 rclient_->ops->handle_message_from_client(rclient_, &msg.header, &fd_, 1);
199 EXPECT_EQ(0, rc);
200 EXPECT_EQ(0, cras_make_fd_nonblocking_called);
201 EXPECT_EQ(0, stream_list_add_called);
202 EXPECT_EQ(0, stream_list_rm_called);
203
204 rc = read(pipe_fds_[0], &out_msg, sizeof(out_msg));
205 EXPECT_EQ(sizeof(out_msg), rc);
206 EXPECT_EQ(-EINVAL, out_msg.err);
207 EXPECT_EQ(stream_id, out_msg.stream_id);
208 }
209
TEST_F(CPRMessageSuite,StreamDisconnectMessage)210 TEST_F(CPRMessageSuite, StreamDisconnectMessage) {
211 struct cras_disconnect_stream_message msg;
212 cras_stream_id_t stream_id = 0x10002;
213 cras_fill_disconnect_stream_message(&msg, stream_id);
214
215 rclient_->ops->handle_message_from_client(rclient_, &msg.header, NULL, 0);
216 EXPECT_EQ(0, stream_list_add_called);
217 EXPECT_EQ(1, stream_list_rm_called);
218 }
219
TEST_F(CPRMessageSuite,StreamDisconnectMessageInvalidClientId)220 TEST_F(CPRMessageSuite, StreamDisconnectMessageInvalidClientId) {
221 struct cras_disconnect_stream_message msg;
222 cras_stream_id_t stream_id = 0x20002; // stream_id with invalid client_id
223 cras_fill_disconnect_stream_message(&msg, stream_id);
224
225 rclient_->ops->handle_message_from_client(rclient_, &msg.header, NULL, 0);
226 EXPECT_EQ(0, stream_list_add_called);
227 EXPECT_EQ(0, stream_list_rm_called);
228 }
229 } // namespace
230
main(int argc,char ** argv)231 int main(int argc, char** argv) {
232 ::testing::InitGoogleTest(&argc, argv);
233 return RUN_ALL_TESTS();
234 }
235
236 /* stubs */
237 extern "C" {
238
cras_iodev_list_get_stream_list()239 struct stream_list* cras_iodev_list_get_stream_list() {
240 return NULL;
241 }
242
cras_make_fd_nonblocking(int fd)243 int cras_make_fd_nonblocking(int fd) {
244 cras_make_fd_nonblocking_called++;
245 return 0;
246 }
247
cras_observer_remove(struct cras_observer_client * client)248 void cras_observer_remove(struct cras_observer_client* client) {
249 cras_observer_remove_called++;
250 }
251
cras_rstream_get_effects(const struct cras_rstream * stream)252 unsigned int cras_rstream_get_effects(const struct cras_rstream* stream) {
253 return 0;
254 }
255
cras_send_with_fds(int sockfd,const void * buf,size_t len,int * fd,unsigned int num_fds)256 int cras_send_with_fds(int sockfd,
257 const void* buf,
258 size_t len,
259 int* fd,
260 unsigned int num_fds) {
261 return write(sockfd, buf, len);
262 }
263
cras_sys_state_shm_fd()264 key_t cras_sys_state_shm_fd() {
265 return 1;
266 }
267
cras_system_set_suspended(int suspended)268 void cras_system_set_suspended(int suspended) {}
269
stream_list_rm_all_client_streams(struct stream_list * list,struct cras_rclient * rclient)270 int stream_list_rm_all_client_streams(struct stream_list* list,
271 struct cras_rclient* rclient) {
272 return 0;
273 }
274
stream_list_rm(struct stream_list * list,cras_stream_id_t id)275 int stream_list_rm(struct stream_list* list, cras_stream_id_t id) {
276 stream_list_rm_called++;
277 return 0;
278 }
279
stream_list_add(struct stream_list * list,struct cras_rstream_config * config,struct cras_rstream ** stream)280 int stream_list_add(struct stream_list* list,
281 struct cras_rstream_config* config,
282 struct cras_rstream** stream) {
283 int ret;
284
285 *stream = &mock_rstream;
286
287 stream_list_add_called++;
288 ret = stream_list_add_return;
289 if (ret)
290 stream_list_add_return = -EINVAL;
291
292 mock_rstream.shm = &mock_shm;
293 mock_rstream.direction = config->direction;
294 mock_rstream.stream_id = config->stream_id;
295
296 return ret;
297 }
298
cras_audio_format_valid(const struct cras_audio_format * fmt)299 bool cras_audio_format_valid(const struct cras_audio_format* fmt) {
300 return audio_format_valid;
301 }
302
detect_rtc_stream_pair(struct stream_list * list,struct cras_rstream * stream)303 void detect_rtc_stream_pair(struct stream_list* list,
304 struct cras_rstream* stream) {
305 return;
306 }
307
308 } // extern "C"
309