blob: 6820e26598b301c04c661f1d0dfcb3ca66a65db0 [file] [log] [blame]
// Copyright (c) 2012 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 <gtest/gtest.h>
#include <vector>
extern "C" {
#include "cras_alsa_mixer.h"
#include "cras_types.h"
#include "cras_util.h"
#include "cras_volume_curve.h"
// Include C file to test static functions and use the definition of some
// structure.
#include "cras_alsa_mixer.c"
}
namespace {
static size_t snd_mixer_open_called;
static int snd_mixer_open_return_value;
static size_t snd_mixer_close_called;
static size_t snd_mixer_attach_called;
static int snd_mixer_attach_return_value;
const char *snd_mixer_attach_mixdev;
static size_t snd_mixer_selem_register_called;
static int snd_mixer_selem_register_return_value;
static size_t snd_mixer_load_called;
static int snd_mixer_load_return_value;
static size_t snd_mixer_first_elem_called;
static snd_mixer_elem_t *snd_mixer_first_elem_return_value;
static int snd_mixer_elem_next_called;
static snd_mixer_elem_t **snd_mixer_elem_next_return_values;
static int snd_mixer_elem_next_return_values_index;
static int snd_mixer_elem_next_return_values_length;
static int snd_mixer_selem_set_playback_dB_all_called;
static long *snd_mixer_selem_set_playback_dB_all_values;
static int snd_mixer_selem_set_playback_dB_all_values_length;
static int snd_mixer_selem_set_playback_switch_all_called;
static int snd_mixer_selem_set_playback_switch_all_value;
static int snd_mixer_selem_has_playback_volume_called;
static int *snd_mixer_selem_has_playback_volume_return_values;
static int snd_mixer_selem_has_playback_volume_return_values_length;
static int snd_mixer_selem_has_playback_switch_called;
static int *snd_mixer_selem_has_playback_switch_return_values;
static int snd_mixer_selem_has_playback_switch_return_values_length;
static int snd_mixer_selem_set_capture_dB_all_called;
static long *snd_mixer_selem_set_capture_dB_all_values;
static int snd_mixer_selem_set_capture_dB_all_values_length;
static int snd_mixer_selem_set_capture_switch_all_called;
static int snd_mixer_selem_set_capture_switch_all_value;
static int snd_mixer_selem_has_capture_volume_called;
static int *snd_mixer_selem_has_capture_volume_return_values;
static int snd_mixer_selem_has_capture_volume_return_values_length;
static int snd_mixer_selem_has_capture_switch_called;
static int *snd_mixer_selem_has_capture_switch_return_values;
static int snd_mixer_selem_has_capture_switch_return_values_length;
static int snd_mixer_selem_get_name_called;
static const char **snd_mixer_selem_get_name_return_values;
static int snd_mixer_selem_get_name_return_values_length;
static int snd_mixer_selem_get_playback_dB_called;
static long *snd_mixer_selem_get_playback_dB_return_values;
static int snd_mixer_selem_get_playback_dB_return_values_length;
static int snd_mixer_selem_get_capture_dB_called;
static long *snd_mixer_selem_get_capture_dB_return_values;
static int snd_mixer_selem_get_capture_dB_return_values_length;
static size_t cras_card_config_get_volume_curve_for_control_called;
static size_t cras_volume_curve_destroy_called;
static size_t snd_mixer_selem_get_playback_dB_range_called;
static size_t snd_mixer_selem_get_playback_dB_range_values_length;
static const long *snd_mixer_selem_get_playback_dB_range_min_values;
static const long *snd_mixer_selem_get_playback_dB_range_max_values;
static size_t snd_mixer_selem_get_capture_dB_range_called;
static size_t snd_mixer_selem_get_capture_dB_range_values_length;
static const long *snd_mixer_selem_get_capture_dB_range_min_values;
static const long *snd_mixer_selem_get_capture_dB_range_max_values;
static size_t iniparser_getstring_return_index;
static size_t iniparser_getstring_return_length;
static char **iniparser_getstring_returns;
static void ResetStubData() {
iniparser_getstring_return_index = 0;
iniparser_getstring_return_length = 0;
snd_mixer_open_called = 0;
snd_mixer_open_return_value = 0;
snd_mixer_close_called = 0;
snd_mixer_attach_called = 0;
snd_mixer_attach_return_value = 0;
snd_mixer_attach_mixdev = static_cast<const char *>(NULL);
snd_mixer_selem_register_called = 0;
snd_mixer_selem_register_return_value = 0;
snd_mixer_load_called = 0;
snd_mixer_load_return_value = 0;
snd_mixer_first_elem_called = 0;
snd_mixer_first_elem_return_value = static_cast<snd_mixer_elem_t *>(NULL);
snd_mixer_elem_next_called = 0;
snd_mixer_elem_next_return_values = static_cast<snd_mixer_elem_t **>(NULL);
snd_mixer_elem_next_return_values_index = 0;
snd_mixer_elem_next_return_values_length = 0;
snd_mixer_selem_set_playback_dB_all_called = 0;
snd_mixer_selem_set_playback_dB_all_values = static_cast<long *>(NULL);
snd_mixer_selem_set_playback_dB_all_values_length = 0;
snd_mixer_selem_set_playback_switch_all_called = 0;
snd_mixer_selem_has_playback_volume_called = 0;
snd_mixer_selem_has_playback_volume_return_values = static_cast<int *>(NULL);
snd_mixer_selem_has_playback_volume_return_values_length = 0;
snd_mixer_selem_has_playback_switch_called = 0;
snd_mixer_selem_has_playback_switch_return_values = static_cast<int *>(NULL);
snd_mixer_selem_has_playback_switch_return_values_length = 0;
snd_mixer_selem_set_capture_dB_all_called = 0;
snd_mixer_selem_set_capture_dB_all_values = static_cast<long *>(NULL);
snd_mixer_selem_set_capture_dB_all_values_length = 0;
snd_mixer_selem_set_capture_switch_all_called = 0;
snd_mixer_selem_has_capture_volume_called = 0;
snd_mixer_selem_has_capture_volume_return_values = static_cast<int *>(NULL);
snd_mixer_selem_has_capture_volume_return_values_length = 0;
snd_mixer_selem_has_capture_switch_called = 0;
snd_mixer_selem_has_capture_switch_return_values = static_cast<int *>(NULL);
snd_mixer_selem_has_capture_switch_return_values_length = 0;
snd_mixer_selem_get_name_called = 0;
snd_mixer_selem_get_name_return_values = static_cast<const char **>(NULL);
snd_mixer_selem_get_name_return_values_length = 0;
snd_mixer_selem_get_playback_dB_called = 0;
snd_mixer_selem_get_playback_dB_return_values = static_cast<long *>(NULL);
snd_mixer_selem_get_playback_dB_return_values_length = 0;
snd_mixer_selem_get_capture_dB_called = 0;
snd_mixer_selem_get_capture_dB_return_values = static_cast<long *>(NULL);
snd_mixer_selem_get_capture_dB_return_values_length = 0;
cras_card_config_get_volume_curve_for_control_called = 0;
cras_volume_curve_destroy_called = 0;
snd_mixer_selem_get_playback_dB_range_called = 0;
snd_mixer_selem_get_playback_dB_range_values_length = 0;
snd_mixer_selem_get_playback_dB_range_min_values = static_cast<long *>(NULL);
snd_mixer_selem_get_playback_dB_range_max_values = static_cast<long *>(NULL);
snd_mixer_selem_get_capture_dB_range_called = 0;
snd_mixer_selem_get_capture_dB_range_values_length = 0;
snd_mixer_selem_get_capture_dB_range_min_values = static_cast<long *>(NULL);
snd_mixer_selem_get_capture_dB_range_max_values = static_cast<long *>(NULL);
}
TEST(AlsaMixer, CreateFailOpen) {
struct cras_alsa_mixer *c;
ResetStubData();
snd_mixer_open_return_value = -1;
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(0, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateFailAttach) {
struct cras_alsa_mixer *c;
ResetStubData();
snd_mixer_attach_return_value = -1;
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateFailSelemRegister) {
struct cras_alsa_mixer *c;
ResetStubData();
snd_mixer_selem_register_return_value = -1;
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateFailLoad) {
struct cras_alsa_mixer *c;
ResetStubData();
snd_mixer_load_return_value = -1;
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
EXPECT_EQ(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateNoElements) {
struct cras_alsa_mixer *c;
ResetStubData();
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
/* set mute shouldn't call anything. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(0, snd_mixer_selem_set_playback_switch_all_called);
/* set volume shouldn't call anything. */
cras_alsa_mixer_set_dBFS(c, 0, NULL);
EXPECT_EQ(0, snd_mixer_selem_set_playback_dB_all_called);
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateOneUnknownElementWithoutVolume) {
struct cras_alsa_mixer *c;
int element_playback_volume[] = {
0,
};
const char *element_names[] = {
"Unknown",
};
struct mixer_output_control output;
struct mixer_control *mixer_output;
mixer_output = reinterpret_cast<mixer_control *>(&output);
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
snd_mixer_selem_has_playback_volume_return_values_length =
ARRAY_SIZE(element_playback_volume);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(1, snd_mixer_selem_has_playback_volume_called);
EXPECT_EQ(1, snd_mixer_selem_get_name_called);
EXPECT_EQ(0, snd_mixer_selem_get_playback_dB_range_called);
/* set mute shouldn't call anything. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(0, snd_mixer_selem_set_playback_switch_all_called);
/* if passed a mixer output then it should mute that. */
mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(0x454);
mixer_output->has_mute = 1;
cras_alsa_mixer_set_mute(c, 0, mixer_output);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
/* set volume shouldn't call anything. */
cras_alsa_mixer_set_dBFS(c, 0, NULL);
EXPECT_EQ(0, snd_mixer_selem_set_playback_dB_all_called);
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateOneUnknownElementWithVolume) {
struct cras_alsa_mixer *c;
static const long min_volumes[] = {-500};
static const long max_volumes[] = {40};
int element_playback_volume[] = {
1,
};
const char *element_names[] = {
"Unknown",
};
struct mixer_output_control output;
struct mixer_control *mixer_output;
mixer_output = reinterpret_cast<mixer_control *>(&output);
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
snd_mixer_selem_has_playback_volume_return_values_length =
ARRAY_SIZE(element_playback_volume);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(2, snd_mixer_selem_has_playback_volume_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_range_called);
EXPECT_EQ(1, snd_mixer_selem_get_name_called);
/* should use the unknown element as a fallback */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
/* if passed a mixer output then it should mute that. */
mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(0x454);
mixer_output->has_mute = 1;
cras_alsa_mixer_set_mute(c, 0, mixer_output);
EXPECT_EQ(2, snd_mixer_selem_set_playback_switch_all_called);
cras_alsa_mixer_set_dBFS(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_dB_all_called);
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateOneMasterElement) {
struct cras_alsa_mixer *c;
int element_playback_volume[] = {
1,
};
int element_playback_switches[] = {
1,
};
const char *element_names[] = {
"Master",
};
struct mixer_output_control output;
struct mixer_control *mixer_output;
mixer_output = reinterpret_cast<mixer_control *>(&output);
mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(2);
mixer_output->has_mute = 1;
mixer_output->has_volume = 1;
output.max_volume_dB = 950;
long set_dB_values[3];
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
snd_mixer_selem_has_playback_volume_return_values_length =
ARRAY_SIZE(element_playback_volume);
snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
snd_mixer_selem_has_playback_switch_return_values_length =
ARRAY_SIZE(element_playback_switches);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(1, snd_mixer_selem_get_name_called);
EXPECT_EQ(1, snd_mixer_elem_next_called);
/* set mute should be called for Master. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
/* set volume should be called for Master. */
cras_alsa_mixer_set_dBFS(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_dB_all_called);
/* if passed a mixer output then it should set the volume for that too. */
snd_mixer_selem_set_playback_dB_all_values = set_dB_values;
snd_mixer_selem_set_playback_dB_all_values_length =
ARRAY_SIZE(set_dB_values);
snd_mixer_selem_set_playback_dB_all_called = 0;
snd_mixer_selem_get_playback_dB_called = 0;
cras_alsa_mixer_set_dBFS(c, 0, mixer_output);
EXPECT_EQ(2, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(950, set_dB_values[0]);
EXPECT_EQ(950, set_dB_values[1]);
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateTwoMainVolumeElements) {
struct cras_alsa_mixer *c;
snd_mixer_elem_t *elements[] = {
reinterpret_cast<snd_mixer_elem_t *>(2),
};
int element_playback_volume[] = {
1,
1,
};
int element_playback_switches[] = {
1,
1,
};
const char *element_names[] = {
"Master",
"PCM",
};
struct mixer_output_control output;
struct mixer_control *mixer_output;
mixer_output = reinterpret_cast<mixer_control *>(&output);
mixer_output->elem = reinterpret_cast<snd_mixer_elem_t *>(3);
mixer_output->has_volume = 1;
output.max_volume_dB = 0;
static const long min_volumes[] = {-500, -1250, -500};
static const long max_volumes[] = {40, 40, 0};
long get_dB_returns[] = {0, 0, 0};
long set_dB_values[3];
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
snd_mixer_elem_next_return_values = elements;
snd_mixer_elem_next_return_values_length = ARRAY_SIZE(elements);
snd_mixer_selem_has_playback_volume_return_values = element_playback_volume;
snd_mixer_selem_has_playback_volume_return_values_length =
ARRAY_SIZE(element_playback_volume);
snd_mixer_selem_has_playback_switch_return_values = element_playback_switches;
snd_mixer_selem_has_playback_switch_return_values_length =
ARRAY_SIZE(element_playback_switches);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
snd_mixer_selem_get_playback_dB_range_called = 0;
snd_mixer_selem_get_playback_dB_range_min_values = min_volumes;
snd_mixer_selem_get_playback_dB_range_max_values = max_volumes;
snd_mixer_selem_get_playback_dB_range_values_length = ARRAY_SIZE(min_volumes);
snd_mixer_selem_set_playback_dB_all_values = set_dB_values;
snd_mixer_selem_set_playback_dB_all_values_length =
ARRAY_SIZE(set_dB_values);
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(3, snd_mixer_selem_get_playback_dB_range_called);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(2, snd_mixer_elem_next_called);
EXPECT_EQ(2, snd_mixer_selem_get_name_called);
EXPECT_EQ(1, snd_mixer_selem_has_playback_switch_called);
/* Set mute should be called for Master only. */
cras_alsa_mixer_set_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
/* Set volume should be called for Master and PCM. If Master doesn't set to
* anything but zero then the entire volume should be passed to the PCM
* control.*/
/* Set volume should be called for Master and PCM. (without mixer_output) */
snd_mixer_selem_get_playback_dB_return_values = get_dB_returns;
snd_mixer_selem_get_playback_dB_return_values_length =
ARRAY_SIZE(get_dB_returns);
cras_alsa_mixer_set_dBFS(c, -50, NULL);
EXPECT_EQ(2, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_called);
/* volume should be set relative to max volume (40 + 40). */
EXPECT_EQ(30, set_dB_values[0]);
EXPECT_EQ(30, set_dB_values[1]);
/* Set volume should be called for Master, PCM, and the mixer_output passed
* in. If Master doesn't set to anything but zero then the entire volume
* should be passed to the PCM control.*/
snd_mixer_selem_set_playback_dB_all_called = 0;
snd_mixer_selem_get_playback_dB_called = 0;
cras_alsa_mixer_set_dBFS(c, -50, mixer_output);
EXPECT_EQ(3, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_called);
EXPECT_EQ(30, set_dB_values[0]);
EXPECT_EQ(30, set_dB_values[1]);
EXPECT_EQ(30, set_dB_values[2]);
/* Set volume should be called for Master and PCM. Since the controls were
* sorted, Master should get the volume remaining after PCM is set, in this
* case -50 - -24 = -26. */
long get_dB_returns2[] = {
-25,
-24,
};
snd_mixer_selem_get_playback_dB_return_values = get_dB_returns2;
snd_mixer_selem_get_playback_dB_return_values_length =
ARRAY_SIZE(get_dB_returns2);
snd_mixer_selem_set_playback_dB_all_called = 0;
snd_mixer_selem_get_playback_dB_called = 0;
mixer_output->has_volume = 0;
cras_alsa_mixer_set_dBFS(c, -50, mixer_output);
EXPECT_EQ(2, snd_mixer_selem_set_playback_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_playback_dB_called);
EXPECT_EQ(54, set_dB_values[0]); // Master
EXPECT_EQ(30, set_dB_values[1]); // PCM
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
}
TEST(AlsaMixer, CreateTwoMainCaptureElements) {
struct cras_alsa_mixer *c;
snd_mixer_elem_t *elements[] = {
reinterpret_cast<snd_mixer_elem_t *>(2),
};
int element_capture_volume[] = {
1,
1,
1,
};
int element_capture_switches[] = {
1,
1,
1,
};
const char *element_names[] = {
"Capture",
"Digital Capture",
"Mic",
};
struct mixer_control *mixer_input;
mixer_input = (struct mixer_control *)calloc(1, sizeof(*mixer_input));
mixer_input->elem = reinterpret_cast<snd_mixer_elem_t *>(3);
ResetStubData();
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
snd_mixer_elem_next_return_values = elements;
snd_mixer_elem_next_return_values_length = ARRAY_SIZE(elements);
snd_mixer_selem_has_capture_volume_return_values = element_capture_volume;
snd_mixer_selem_has_capture_volume_return_values_length =
ARRAY_SIZE(element_capture_volume);
snd_mixer_selem_has_capture_switch_return_values = element_capture_switches;
snd_mixer_selem_has_capture_switch_return_values_length =
ARRAY_SIZE(element_capture_switches);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
c = cras_alsa_mixer_create("hw:0", NULL, NULL, 0, NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), c);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(2, snd_mixer_elem_next_called);
EXPECT_EQ(4, snd_mixer_selem_get_name_called);
EXPECT_EQ(1, snd_mixer_selem_has_capture_switch_called);
/* Set mute should be called for Master only. */
cras_alsa_mixer_set_capture_mute(c, 0, NULL);
EXPECT_EQ(1, snd_mixer_selem_set_capture_switch_all_called);
/* Set volume should be called for Capture and Digital Capture. If Capture
* doesn't set to anything but zero then the entire volume should be passed to
* the Digital Capture control. */
long get_dB_returns[] = {
0,
0,
};
long set_dB_values[2];
snd_mixer_selem_get_capture_dB_return_values = get_dB_returns;
snd_mixer_selem_get_capture_dB_return_values_length =
ARRAY_SIZE(get_dB_returns);
snd_mixer_selem_set_capture_dB_all_values = set_dB_values;
snd_mixer_selem_set_capture_dB_all_values_length =
ARRAY_SIZE(set_dB_values);
cras_alsa_mixer_set_capture_dBFS(c, -10, NULL);
EXPECT_EQ(2, snd_mixer_selem_set_capture_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_capture_dB_called);
EXPECT_EQ(-10, set_dB_values[0]);
EXPECT_EQ(-10, set_dB_values[1]);
/* Set volume should be called for Capture and Digital Capture. Capture should
* get the gain remaining after Mic Boos is set, in this case 20 - 25 = -5. */
long get_dB_returns2[] = {
25,
-5,
};
snd_mixer_selem_get_capture_dB_return_values = get_dB_returns2;
snd_mixer_selem_get_capture_dB_return_values_length =
ARRAY_SIZE(get_dB_returns2);
snd_mixer_selem_set_capture_dB_all_values = set_dB_values;
snd_mixer_selem_set_capture_dB_all_values_length =
ARRAY_SIZE(set_dB_values);
snd_mixer_selem_set_capture_dB_all_called = 0;
snd_mixer_selem_get_capture_dB_called = 0;
cras_alsa_mixer_set_capture_dBFS(c, 20, NULL);
EXPECT_EQ(2, snd_mixer_selem_set_capture_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_capture_dB_called);
EXPECT_EQ(20, set_dB_values[0]);
EXPECT_EQ(-5, set_dB_values[1]);
/* Set volume to the two main controls plus additional specific input
* volume control */
long get_dB_returns3[] = {
0,
0,
};
long set_dB_values3[3];
mixer_input->has_volume = 1;
snd_mixer_selem_get_capture_dB_return_values = get_dB_returns3;
snd_mixer_selem_get_capture_dB_return_values_length =
ARRAY_SIZE(get_dB_returns3);
snd_mixer_selem_set_capture_dB_all_values = set_dB_values3;
snd_mixer_selem_set_capture_dB_all_values_length =
ARRAY_SIZE(set_dB_values3);
snd_mixer_selem_set_capture_dB_all_called = 0;
snd_mixer_selem_get_capture_dB_called = 0;
cras_alsa_mixer_set_capture_dBFS(c, 20, mixer_input);
EXPECT_EQ(3, snd_mixer_selem_set_capture_dB_all_called);
EXPECT_EQ(2, snd_mixer_selem_get_capture_dB_called);
EXPECT_EQ(20, set_dB_values3[0]);
EXPECT_EQ(20, set_dB_values3[1]);
EXPECT_EQ(20, set_dB_values3[2]);
cras_alsa_mixer_destroy(c);
EXPECT_EQ(1, snd_mixer_close_called);
free(mixer_input);
}
class AlsaMixerOutputs : public testing::Test {
protected:
virtual void SetUp() {
output_called_values_.clear();
output_callback_called_ = 0;
static snd_mixer_elem_t *elements[] = {
reinterpret_cast<snd_mixer_elem_t *>(2), // PCM
reinterpret_cast<snd_mixer_elem_t *>(3), // Headphone
reinterpret_cast<snd_mixer_elem_t *>(4), // Speaker
reinterpret_cast<snd_mixer_elem_t *>(5), // HDMI
reinterpret_cast<snd_mixer_elem_t *>(6), // IEC958
reinterpret_cast<snd_mixer_elem_t *>(7), // Mic Boost
reinterpret_cast<snd_mixer_elem_t *>(8), // Capture
};
static int element_playback_volume[] = {
1,
1,
1,
0,
0,
1,
1,
};
static int element_playback_switches[] = {
1,
1,
1,
0,
1,
1,
1,
};
static int element_capture_volume[] = {0, 0, 0, 0, 0, 0,
1,
1,
};
static int element_capture_switches[] = {0, 0, 0, 0, 0, 0,
1,
1,
};
static const char *element_names[] = {
"Master",
"PCM",
"Headphone",
"Speaker",
"HDMI",
"IEC958",
"Capture",
"Digital Capture",
};
static const char *output_names_extra[] = {
"IEC958"
};
static char *iniparser_returns[] = {
NULL,
};
ResetStubData();
snd_mixer_first_elem_return_value =
reinterpret_cast<snd_mixer_elem_t *>(1); // Master
snd_mixer_elem_next_return_values = elements;
snd_mixer_elem_next_return_values_length = ARRAY_SIZE(elements);
snd_mixer_selem_has_playback_volume_return_values =
element_playback_volume;
snd_mixer_selem_has_playback_volume_return_values_length =
ARRAY_SIZE(element_playback_volume);
snd_mixer_selem_has_playback_switch_return_values =
element_playback_switches;
snd_mixer_selem_has_playback_switch_return_values_length =
ARRAY_SIZE(element_playback_switches);
snd_mixer_selem_has_capture_volume_return_values =
element_capture_volume;
snd_mixer_selem_has_capture_volume_return_values_length =
ARRAY_SIZE(element_capture_volume);
snd_mixer_selem_has_capture_switch_return_values =
element_capture_switches;
snd_mixer_selem_has_capture_switch_return_values_length =
ARRAY_SIZE(element_capture_switches);
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
iniparser_getstring_returns = iniparser_returns;
iniparser_getstring_return_length = ARRAY_SIZE(iniparser_returns);
cras_mixer_ = cras_alsa_mixer_create("hw:0",
reinterpret_cast<struct cras_card_config*>(5),
output_names_extra, ARRAY_SIZE(output_names_extra), NULL);
ASSERT_NE(static_cast<struct cras_alsa_mixer *>(NULL), cras_mixer_);
EXPECT_EQ(1, snd_mixer_open_called);
EXPECT_EQ(1, snd_mixer_attach_called);
EXPECT_EQ(0, strcmp(snd_mixer_attach_mixdev, "hw:0"));
EXPECT_EQ(1, snd_mixer_selem_register_called);
EXPECT_EQ(1, snd_mixer_load_called);
EXPECT_EQ(0, snd_mixer_close_called);
EXPECT_EQ(ARRAY_SIZE(elements) + 1, snd_mixer_elem_next_called);
EXPECT_EQ(6, snd_mixer_selem_has_playback_volume_called);
EXPECT_EQ(5, snd_mixer_selem_has_playback_switch_called);
EXPECT_EQ(2, snd_mixer_selem_has_capture_volume_called);
EXPECT_EQ(1, snd_mixer_selem_has_capture_switch_called);
EXPECT_EQ(5, cras_card_config_get_volume_curve_for_control_called);
}
virtual void TearDown() {
cras_alsa_mixer_destroy(cras_mixer_);
EXPECT_EQ(1, snd_mixer_close_called);
}
static void OutputCallback(struct mixer_control *out, void *arg) {
output_callback_called_++;
output_called_values_.push_back(out);
}
struct cras_alsa_mixer *cras_mixer_;
static size_t output_callback_called_;
static std::vector<struct mixer_control *> output_called_values_;
};
size_t AlsaMixerOutputs::output_callback_called_;
std::vector<struct mixer_control *>
AlsaMixerOutputs::output_called_values_;
TEST_F(AlsaMixerOutputs, CheckFourOutputs) {
cras_alsa_mixer_list_outputs(cras_mixer_,
AlsaMixerOutputs::OutputCallback,
reinterpret_cast<void*>(555));
EXPECT_EQ(4, output_callback_called_);
}
TEST_F(AlsaMixerOutputs, CheckFindOutputByNameNoMatch) {
struct mixer_control *out;
snd_mixer_selem_get_name_called = 0;
out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
"AAAAA Jack");
EXPECT_EQ(static_cast<struct mixer_control *>(NULL), out);
EXPECT_EQ(4, snd_mixer_selem_get_name_called);
}
TEST_F(AlsaMixerOutputs, CheckFindOutputByName) {
struct mixer_control *out;
snd_mixer_selem_get_name_called = 0;
out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
"Headphone Jack");
EXPECT_NE(static_cast<struct mixer_control *>(NULL), out);
EXPECT_EQ(1, snd_mixer_selem_get_name_called);
}
TEST_F(AlsaMixerOutputs, CheckFindOutputHDMIByName) {
struct mixer_control *out;
snd_mixer_selem_get_name_called = 0;
out = cras_alsa_mixer_get_output_matching_name(cras_mixer_,
"HDMI Jack");
EXPECT_NE(static_cast<struct mixer_control *>(NULL), out);
EXPECT_EQ(3, snd_mixer_selem_get_name_called);
}
TEST_F(AlsaMixerOutputs, CheckFindInputName) {
struct mixer_control *control;
snd_mixer_elem_t *elements[] = {
reinterpret_cast<snd_mixer_elem_t *>(2), // Headphone
reinterpret_cast<snd_mixer_elem_t *>(3), // MIC
};
const char *element_names[] = {
"Speaker",
"Headphone",
"MIC",
};
snd_mixer_first_elem_return_value = reinterpret_cast<snd_mixer_elem_t *>(1);
snd_mixer_elem_next_return_values = elements;
snd_mixer_elem_next_return_values_index = 0;
snd_mixer_elem_next_return_values_length = ARRAY_SIZE(elements);
snd_mixer_selem_get_name_called = 0;
snd_mixer_selem_get_name_return_values = element_names;
snd_mixer_selem_get_name_return_values_length = ARRAY_SIZE(element_names);
control = cras_alsa_mixer_get_input_matching_name(cras_mixer_,
"MIC");
EXPECT_NE(static_cast<struct mixer_control *>(NULL), control);
/* 3 + 1, one more for log */
EXPECT_EQ(4, snd_mixer_selem_get_name_called);
}
TEST_F(AlsaMixerOutputs, ActivateDeactivate) {
int rc;
cras_alsa_mixer_list_outputs(cras_mixer_,
AlsaMixerOutputs::OutputCallback,
reinterpret_cast<void*>(555));
EXPECT_EQ(4, output_callback_called_);
EXPECT_EQ(4, output_called_values_.size());
rc = cras_alsa_mixer_set_output_active_state(output_called_values_[0], 0);
ASSERT_EQ(0, rc);
EXPECT_EQ(1, snd_mixer_selem_set_playback_switch_all_called);
cras_alsa_mixer_set_output_active_state(output_called_values_[0], 1);
EXPECT_EQ(2, snd_mixer_selem_set_playback_switch_all_called);
}
TEST_F(AlsaMixerOutputs, MinMaxCaptureGain) {
long min, max;
static const long min_volumes[] = {0, 0, 0, 0, 0, 0, 500, -1250};
static const long max_volumes[] = {0, 0, 0, 0, 0, 0, 3000, 400};
snd_mixer_selem_get_capture_dB_range_called = 0;
snd_mixer_selem_get_capture_dB_range_min_values = min_volumes;
snd_mixer_selem_get_capture_dB_range_max_values = max_volumes;
snd_mixer_selem_get_capture_dB_range_values_length = ARRAY_SIZE(min_volumes);
min = cras_alsa_mixer_get_minimum_capture_gain(cras_mixer_,
NULL);
EXPECT_EQ(-750, min);
max = cras_alsa_mixer_get_maximum_capture_gain(cras_mixer_,
NULL);
EXPECT_EQ(3400, max);
}
TEST_F(AlsaMixerOutputs, MinMaxCaptureGainWithActiveInput) {
struct mixer_control *mixer_input;
long min, max;
static const long min_volumes[] = {0, 0, 0, 0, 0, 0, 500, -1250, 50};
static const long max_volumes[] = {0, 0, 0, 0, 0, 0, 3000, 400, 60};
snd_mixer_selem_get_capture_dB_range_called = 0;
snd_mixer_selem_get_capture_dB_range_min_values = min_volumes;
snd_mixer_selem_get_capture_dB_range_max_values = max_volumes;
snd_mixer_selem_get_capture_dB_range_values_length = ARRAY_SIZE(min_volumes);
mixer_input = (struct mixer_control *)calloc(1, sizeof(*mixer_input));
mixer_input->elem = reinterpret_cast<snd_mixer_elem_t *>(9);
min = cras_alsa_mixer_get_minimum_capture_gain(cras_mixer_, mixer_input);
max = cras_alsa_mixer_get_maximum_capture_gain(cras_mixer_, mixer_input);
EXPECT_EQ(-700, min);
EXPECT_EQ(3460, max);
free((void *)mixer_input);
}
/* Stubs */
extern "C" {
int snd_mixer_open(snd_mixer_t **mixer, int mode) {
snd_mixer_open_called++;
*mixer = reinterpret_cast<snd_mixer_t *>(2);
return snd_mixer_open_return_value;
}
int snd_mixer_attach(snd_mixer_t *mixer, const char *name) {
snd_mixer_attach_called++;
snd_mixer_attach_mixdev = name;
return snd_mixer_attach_return_value;
}
int snd_mixer_selem_register(snd_mixer_t *mixer,
struct snd_mixer_selem_regopt *options,
snd_mixer_class_t **classp) {
snd_mixer_selem_register_called++;
return snd_mixer_selem_register_return_value;
}
int snd_mixer_load(snd_mixer_t *mixer) {
snd_mixer_load_called++;
return snd_mixer_load_return_value;
}
const char *snd_mixer_selem_get_name(snd_mixer_elem_t *elem) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_get_name_called++;
if (index >= snd_mixer_selem_get_name_return_values_length)
return static_cast<char *>(NULL);
return snd_mixer_selem_get_name_return_values[index];
}
unsigned int snd_mixer_selem_get_index(snd_mixer_elem_t *elem) {
return 0;
}
int snd_mixer_selem_has_playback_volume(snd_mixer_elem_t *elem) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_has_playback_volume_called++;
if (index >= snd_mixer_selem_has_playback_volume_return_values_length)
return -1;
return snd_mixer_selem_has_playback_volume_return_values[index];
}
int snd_mixer_selem_has_playback_switch(snd_mixer_elem_t *elem) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_has_playback_switch_called++;
if (index >= snd_mixer_selem_has_playback_switch_return_values_length)
return -1;
return snd_mixer_selem_has_playback_switch_return_values[index];
}
int snd_mixer_selem_has_capture_volume(snd_mixer_elem_t *elem) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_has_capture_volume_called++;
if (index >= snd_mixer_selem_has_capture_volume_return_values_length)
return -1;
return snd_mixer_selem_has_capture_volume_return_values[index];
}
int snd_mixer_selem_has_capture_switch(snd_mixer_elem_t *elem) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_has_capture_switch_called++;
if (index >= snd_mixer_selem_has_capture_switch_return_values_length)
return -1;
return snd_mixer_selem_has_capture_switch_return_values[index];
}
snd_mixer_elem_t *snd_mixer_first_elem(snd_mixer_t *mixer) {
snd_mixer_first_elem_called++;
return snd_mixer_first_elem_return_value;
}
snd_mixer_elem_t *snd_mixer_elem_next(snd_mixer_elem_t *elem) {
snd_mixer_elem_next_called++;
if (snd_mixer_elem_next_return_values_index >=
snd_mixer_elem_next_return_values_length)
return static_cast<snd_mixer_elem_t *>(NULL);
return snd_mixer_elem_next_return_values[
snd_mixer_elem_next_return_values_index++];
}
int snd_mixer_close(snd_mixer_t *mixer) {
snd_mixer_close_called++;
return 0;
}
int snd_mixer_selem_set_playback_dB_all(snd_mixer_elem_t *elem,
long value,
int dir) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_set_playback_dB_all_called++;
if (index < snd_mixer_selem_set_playback_dB_all_values_length)
snd_mixer_selem_set_playback_dB_all_values[index] = value;
return 0;
}
int snd_mixer_selem_get_playback_dB(snd_mixer_elem_t *elem,
snd_mixer_selem_channel_id_t channel,
long *value) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_get_playback_dB_called++;
if (index >= snd_mixer_selem_get_playback_dB_return_values_length)
*value = 0;
else
*value = snd_mixer_selem_get_playback_dB_return_values[index];
return 0;
}
int snd_mixer_selem_set_playback_switch_all(snd_mixer_elem_t *elem, int value) {
snd_mixer_selem_set_playback_switch_all_called++;
snd_mixer_selem_set_playback_switch_all_value = value;
return 0;
}
int snd_mixer_selem_set_capture_dB_all(snd_mixer_elem_t *elem,
long value,
int dir) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_set_capture_dB_all_called++;
if (index < snd_mixer_selem_set_capture_dB_all_values_length)
snd_mixer_selem_set_capture_dB_all_values[index] = value;
return 0;
}
int snd_mixer_selem_get_capture_dB(snd_mixer_elem_t *elem,
snd_mixer_selem_channel_id_t channel,
long *value) {
int index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_get_capture_dB_called++;
if (index >= snd_mixer_selem_get_capture_dB_return_values_length)
*value = 0;
else
*value = snd_mixer_selem_get_capture_dB_return_values[index];
return 0;
}
int snd_mixer_selem_set_capture_switch_all(snd_mixer_elem_t *elem, int value) {
snd_mixer_selem_set_capture_switch_all_called++;
snd_mixer_selem_set_capture_switch_all_value = value;
return 0;
}
int snd_mixer_selem_get_capture_dB_range(snd_mixer_elem_t *elem, long *min,
long *max) {
size_t index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_get_capture_dB_range_called++;
if (index >= snd_mixer_selem_get_capture_dB_range_values_length) {
*min = 0;
*max = 0;
} else {
*min = snd_mixer_selem_get_capture_dB_range_min_values[index];
*max = snd_mixer_selem_get_capture_dB_range_max_values[index];
}
return 0;
}
int snd_mixer_selem_get_playback_dB_range(snd_mixer_elem_t *elem,
long *min,
long *max) {
size_t index = reinterpret_cast<size_t>(elem) - 1;
snd_mixer_selem_get_playback_dB_range_called++;
if (index >= snd_mixer_selem_get_playback_dB_range_values_length) {
*min = 0;
*max = 0;
} else {
*min = snd_mixer_selem_get_playback_dB_range_min_values[index];
*max = snd_mixer_selem_get_playback_dB_range_max_values[index];
}
return 0;
}
// From cras_volume_curve.
static long get_dBFS_default(const struct cras_volume_curve *curve,
size_t volume)
{
return 100 * (volume - 100);
}
void cras_volume_curve_destroy(struct cras_volume_curve *curve)
{
cras_volume_curve_destroy_called++;
free(curve);
}
// From libiniparser.
struct cras_volume_curve *cras_card_config_get_volume_curve_for_control(
const struct cras_card_config *card_config,
const char *control_name)
{
struct cras_volume_curve *curve;
curve = (struct cras_volume_curve *)calloc(1, sizeof(*curve));
cras_card_config_get_volume_curve_for_control_called++;
if (curve != NULL)
curve->get_dBFS = get_dBFS_default;
return curve;
}
} /* extern "C" */
} // namespace
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}