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