blob: 437c77bfa3dfba8178ec3c1f57e203e9b39013ea [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <sys/shm.h>
#include <gtest/gtest.h>
extern "C" {
#include "cras_messages.h"
// Include C file to test static functions.
#include "cras_client.c"
}
static const cras_stream_id_t FIRST_STREAM_ID = 1;
static int shmat_called;
static int shmdt_called;
static int shmget_called;
static int pthread_create_called;
static int pthread_join_called;
static int close_called;
static int pipe_called;
static int sendmsg_called;
static int write_called;
static void* shmat_returned_value;
static int pthread_create_returned_value;
namespace {
void InitStaticVariables() {
shmat_called = 0;
shmdt_called = 0;
shmget_called = 0;
pthread_create_called = 0;
pthread_join_called = 0;
close_called = 0;
pipe_called = 0;
sendmsg_called = 0;
write_called = 0;
shmat_returned_value = NULL;
pthread_create_returned_value = 0;
}
class CrasClientTestSuite : public testing::Test {
protected:
void InitShm(struct cras_audio_shm* shm) {
shm->area = static_cast<cras_audio_shm_area*>(
calloc(1, sizeof(*shm->area)));
cras_shm_set_frame_bytes(shm, 4);
cras_shm_set_used_size(shm, shm_writable_frames_ * 4);
memcpy(&shm->area->config, &shm->config, sizeof(shm->config));
}
void FreeShm(struct cras_audio_shm* shm) {
if (shm->area) {
free(shm->area);
shm->area = NULL;
}
}
virtual void SetUp() {
shm_writable_frames_ = 100;
InitStaticVariables();
memset(&client_, 0, sizeof(client_));
memset(&stream_, 0, sizeof(stream_));
stream_.id = FIRST_STREAM_ID;
struct cras_stream_params* config =
static_cast<cras_stream_params*>(calloc(1, sizeof(*config)));
config->buffer_frames = 1024;
config->cb_threshold = 512;
stream_.config = config;
}
virtual void TearDown() {
if (stream_.config) {
free(stream_.config);
stream_.config = NULL;
}
}
void StreamConnected(CRAS_STREAM_DIRECTION direction);
void StreamConnectedFail(CRAS_STREAM_DIRECTION direction);
struct client_stream stream_;
struct cras_client client_;
int shm_writable_frames_;
};
void set_audio_format(struct cras_audio_format* format,
snd_pcm_format_t pcm_format,
size_t frame_rate,
size_t num_channels) {
format->format = pcm_format;
format->frame_rate = frame_rate;
format->num_channels = num_channels;
for (size_t i = 0; i < CRAS_CH_MAX; ++i)
format->channel_layout[i] = i < num_channels ? i : -1;
}
void CrasClientTestSuite::StreamConnected(CRAS_STREAM_DIRECTION direction) {
struct cras_client_stream_connected msg;
int input_shm_key = 0;
int output_shm_key = 1;
int shm_max_size = 600;
size_t format_bytes;
struct cras_audio_shm_area area;
stream_.direction = direction;
set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
struct cras_audio_format server_format;
set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
// Initialize shm area
format_bytes = cras_get_format_bytes(&server_format);
memset(&area, 0, sizeof(area));
area.config.frame_bytes = format_bytes;
area.config.used_size = shm_writable_frames_ * format_bytes;
shmat_returned_value = &area;
cras_fill_client_stream_connected(
&msg,
0,
stream_.id,
&server_format,
input_shm_key,
output_shm_key,
shm_max_size);
stream_connected(&stream_, &msg);
EXPECT_EQ(1, shmget_called);
EXPECT_EQ(1, shmat_called);
EXPECT_NE(0, stream_.thread.running);
if (direction == CRAS_STREAM_OUTPUT) {
EXPECT_EQ(NULL, stream_.capture_shm.area);
EXPECT_EQ(&area, stream_.play_shm.area);
} else {
EXPECT_EQ(NULL, stream_.play_shm.area);
EXPECT_EQ(&area, stream_.capture_shm.area);
}
}
TEST_F(CrasClientTestSuite, InputStreamConnected) {
StreamConnected(CRAS_STREAM_INPUT);
}
TEST_F(CrasClientTestSuite, OutputStreamConnected) {
StreamConnected(CRAS_STREAM_OUTPUT);
}
void CrasClientTestSuite::StreamConnectedFail(
CRAS_STREAM_DIRECTION direction) {
struct cras_client_stream_connected msg;
int input_shm_key = 0;
int output_shm_key = 1;
int shm_max_size = 600;
size_t format_bytes;
struct cras_audio_shm_area area;
stream_.direction = direction;
set_audio_format(&stream_.config->format, SND_PCM_FORMAT_S16_LE, 48000, 4);
struct cras_audio_format server_format;
set_audio_format(&server_format, SND_PCM_FORMAT_S16_LE, 44100, 2);
// Initialize shm area
format_bytes = cras_get_format_bytes(&server_format);
memset(&area, 0, sizeof(area));
area.config.frame_bytes = format_bytes;
area.config.used_size = shm_writable_frames_ * format_bytes;
shmat_returned_value = &area;
// let pthread_create fail
pthread_create_returned_value = -1;
cras_fill_client_stream_connected(
&msg,
0,
stream_.id,
&server_format,
input_shm_key,
output_shm_key,
shm_max_size);
stream_connected(&stream_, &msg);
EXPECT_EQ(0, stream_.thread.running);
EXPECT_EQ(1, shmget_called);
EXPECT_EQ(1, shmat_called);
EXPECT_EQ(1, shmdt_called);
EXPECT_EQ(1, pipe_called);
EXPECT_EQ(2, close_called); // close the pipefds
}
TEST_F(CrasClientTestSuite, InputStreamConnectedFail) {
StreamConnectedFail(CRAS_STREAM_INPUT);
}
TEST_F(CrasClientTestSuite, OutputStreamConnectedFail) {
StreamConnectedFail(CRAS_STREAM_OUTPUT);
}
TEST_F(CrasClientTestSuite, AddAndRemoveStream) {
cras_stream_id_t stream_id;
// Dynamically allocat the stream so that it can be freed later.
struct client_stream* stream_ptr = (struct client_stream *)
malloc(sizeof(*stream_ptr));
memcpy(stream_ptr, &stream_, sizeof(client_stream));
stream_ptr->config = (struct cras_stream_params *)
malloc(sizeof(*(stream_ptr->config)));
memcpy(stream_ptr->config, stream_.config, sizeof(*(stream_.config)));
EXPECT_EQ(0, client_thread_add_stream(
&client_, stream_ptr, &stream_id, NO_DEVICE));
EXPECT_EQ(&client_, stream_ptr->client);
EXPECT_EQ(stream_id, stream_ptr->id);
EXPECT_EQ(1, sendmsg_called); // send connect message to server
EXPECT_EQ(stream_ptr, stream_from_id(&client_, stream_id));
stream_ptr->thread.running = 1;
EXPECT_EQ(0, client_thread_rm_stream(&client_, stream_id));
// One for the disconnect message to server,
// the other is to wake_up the audio thread
EXPECT_EQ(2, write_called);
EXPECT_EQ(1, pthread_join_called);
EXPECT_EQ(NULL, stream_from_id(&client_, stream_id));
}
} // namepsace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
/* stubs */
extern "C" {
int shmget(key_t key, size_t size, int shmflg) {
++shmget_called;
return 0;
}
void* shmat(int shmid, const void* shmaddr, int shmflg) {
++shmat_called;
return shmat_returned_value;
}
int shmdt(const void *shmaddr) {
++shmdt_called;
return 0;
}
ssize_t write(int fd, const void *buf, size_t count) {
++write_called;
return count;
}
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags) {
++sendmsg_called;
return msg->msg_iov->iov_len;
}
int pipe(int pipefd[2]) {
pipefd[0] = 1;
pipefd[1] = 2;
++pipe_called;
return 0;
}
int close(int fd) {
++close_called;
return 0;
}
int pthread_create(pthread_t *thread,
const pthread_attr_t *attr,
void *(*start_routine)(void *),
void *arg) {
++pthread_create_called;
return pthread_create_returned_value;
}
int pthread_join(pthread_t thread, void **retval) {
++pthread_join_called;
return 0;
}
int clock_gettime(clockid_t clk_id, struct timespec *tp) {
tp->tv_sec = 0;
tp->tv_nsec = 0;
return 0;
}
}