普通文本  |  659行  |  26.35 KB

// Copyright 2016 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 <gtest/gtest.h>
#include <stdio.h>
#include <syslog.h>

#include <vector>
#include <map>

extern "C" {
#include "cras_observer.c"
}

namespace {

static size_t cras_alert_destroy_called;
static size_t cras_alert_create_called;
static std::vector<struct cras_alert *> cras_alert_create_return_values;
typedef std::map<struct cras_alert *, void *> alert_callback_map;
static alert_callback_map cras_alert_create_prepare_map;
static alert_callback_map cras_alert_add_callback_map;
typedef std::map<struct cras_alert *, unsigned int> alert_flags_map;
static alert_flags_map cras_alert_create_flags_map;
static struct cras_alert *cras_alert_pending_alert_value;
static void *cras_alert_pending_data_value = NULL;
static size_t cras_alert_pending_data_size_value;
static size_t cras_iodev_list_update_device_list_called;
static std::vector<void *> cb_context;
static size_t cb_output_volume_changed_called;
static std::vector<int32_t> cb_output_volume_changed_volume;
static size_t cb_output_mute_changed_called;
static std::vector<int> cb_output_mute_changed_muted;
static std::vector<int> cb_output_mute_changed_user_muted;
static std::vector<int> cb_output_mute_changed_mute_locked;
static size_t cb_capture_gain_changed_called;
static std::vector<int32_t> cb_capture_gain_changed_gain;
static size_t cb_capture_mute_changed_called;
static std::vector<int> cb_capture_mute_changed_muted;
static std::vector<int> cb_capture_mute_changed_mute_locked;
static size_t cb_nodes_changed_called;
static size_t cb_active_node_changed_called;
static std::vector<enum CRAS_STREAM_DIRECTION> cb_active_node_changed_dir;
static std::vector<cras_node_id_t> cb_active_node_changed_node_id;
static size_t cb_output_node_volume_changed_called;
static std::vector<cras_node_id_t> cb_output_node_volume_changed_node_id;
static std::vector<int32_t> cb_output_node_volume_changed_volume;
static size_t cb_node_left_right_swapped_changed_called;
static std::vector<cras_node_id_t> cb_node_left_right_swapped_changed_node_id;
static std::vector<int> cb_node_left_right_swapped_changed_swapped;
static size_t cb_input_node_gain_changed_called;
static std::vector<cras_node_id_t> cb_input_node_gain_changed_node_id;
static std::vector<int32_t> cb_input_node_gain_changed_gain;
static size_t cb_num_active_streams_changed_called;
static std::vector<enum CRAS_STREAM_DIRECTION>
    cb_num_active_streams_changed_dir;
static std::vector<uint32_t> cb_num_active_streams_changed_num;

static void ResetStubData() {
  cras_alert_destroy_called = 0;
  cras_alert_create_called = 0;
  cras_alert_create_return_values.clear();
  cras_alert_create_prepare_map.clear();
  cras_alert_create_flags_map.clear();
  cras_alert_add_callback_map.clear();
  cras_alert_pending_alert_value = NULL;
  cras_alert_pending_data_size_value = 0;
  if (cras_alert_pending_data_value) {
    free(cras_alert_pending_data_value);
    cras_alert_pending_data_value = NULL;
  }
  cras_iodev_list_update_device_list_called = 0;
  cb_context.clear();
  cb_output_volume_changed_called = 0;
  cb_output_volume_changed_volume.clear();
  cb_output_mute_changed_called = 0;
  cb_output_mute_changed_muted.clear();
  cb_output_mute_changed_user_muted.clear();
  cb_output_mute_changed_mute_locked.clear();
  cb_capture_gain_changed_called = 0;
  cb_capture_gain_changed_gain.clear();
  cb_capture_mute_changed_called = 0;
  cb_capture_mute_changed_muted.clear();
  cb_capture_mute_changed_mute_locked.clear();
  cb_nodes_changed_called = 0;
  cb_active_node_changed_called = 0;
  cb_active_node_changed_dir.clear();
  cb_active_node_changed_node_id.clear();
  cb_output_node_volume_changed_called = 0;
  cb_output_node_volume_changed_node_id.clear();
  cb_output_node_volume_changed_volume.clear();
  cb_node_left_right_swapped_changed_called = 0;
  cb_node_left_right_swapped_changed_node_id.clear();
  cb_node_left_right_swapped_changed_swapped.clear();
  cb_input_node_gain_changed_called = 0;
  cb_input_node_gain_changed_node_id.clear();
  cb_input_node_gain_changed_gain.clear();
  cb_num_active_streams_changed_called = 0;
  cb_num_active_streams_changed_dir.clear();
  cb_num_active_streams_changed_num.clear();
}

/* System output volume changed. */
void cb_output_volume_changed(void *context, int32_t volume) {
  cb_output_volume_changed_called++;
  cb_context.push_back(context);
  cb_output_volume_changed_volume.push_back(volume);
}
/* System output mute changed. */
void cb_output_mute_changed(void *context,
                            int muted, int user_muted, int mute_locked) {
  cb_output_mute_changed_called++;
  cb_context.push_back(context);
  cb_output_mute_changed_muted.push_back(muted);
  cb_output_mute_changed_user_muted.push_back(user_muted);
  cb_output_mute_changed_mute_locked.push_back(mute_locked);
}
/* System input/capture gain changed. */
void cb_capture_gain_changed(void *context, int32_t gain) {
  cb_capture_gain_changed_called++;
  cb_context.push_back(context);
  cb_capture_gain_changed_gain.push_back(gain);
}

/* System input/capture mute changed. */
void cb_capture_mute_changed(void *context, int muted, int mute_locked) {
  cb_capture_mute_changed_called++;
  cb_context.push_back(context);
  cb_capture_mute_changed_muted.push_back(muted);
  cb_capture_mute_changed_mute_locked.push_back(mute_locked);
}

/* Device or node topology changed. */
void cb_nodes_changed(void *context) {
  cb_nodes_changed_called++;
  cb_context.push_back(context);
}

/* Active node changed. A notification is sent for every change.
 * When there is no active node, node_id is 0. */
void cb_active_node_changed(void *context,
                            enum CRAS_STREAM_DIRECTION dir,
                            cras_node_id_t node_id) {
  cb_active_node_changed_called++;
  cb_context.push_back(context);
  cb_active_node_changed_dir.push_back(dir);
  cb_active_node_changed_node_id.push_back(node_id);
}

/* Output node volume changed. */
void cb_output_node_volume_changed(void *context,
                                   cras_node_id_t node_id,
                                   int32_t volume) {
  cb_output_node_volume_changed_called++;
  cb_context.push_back(context);
  cb_output_node_volume_changed_node_id.push_back(node_id);
  cb_output_node_volume_changed_volume.push_back(volume);
}

/* Node left/right swapped state change. */
void cb_node_left_right_swapped_changed(void *context,
                                        cras_node_id_t node_id,
                                        int swapped) {
  cb_node_left_right_swapped_changed_called++;
  cb_context.push_back(context);
  cb_node_left_right_swapped_changed_node_id.push_back(node_id);
  cb_node_left_right_swapped_changed_swapped.push_back(swapped);
}

/* Input gain changed. */
void cb_input_node_gain_changed(void *context,
                                cras_node_id_t node_id,
                                int32_t gain) {
  cb_input_node_gain_changed_called++;
  cb_context.push_back(context);
  cb_input_node_gain_changed_node_id.push_back(node_id);
  cb_input_node_gain_changed_gain.push_back(gain);
}

/* Number of active streams changed. */
void cb_num_active_streams_changed(void *context,
                                         enum CRAS_STREAM_DIRECTION dir,
                                         uint32_t num_active_streams) {
  cb_num_active_streams_changed_called++;
  cb_context.push_back(context);
  cb_num_active_streams_changed_dir.push_back(dir);
  cb_num_active_streams_changed_num.push_back(num_active_streams);
}

class ObserverTest : public testing::Test {
 protected:
  virtual void SetUp() {
    int rc;

    ResetStubData();
    rc = cras_observer_server_init();
    ASSERT_EQ(0, rc);
    EXPECT_EQ(15, cras_alert_create_called);
    EXPECT_EQ(reinterpret_cast<void *>(output_volume_alert),
              cras_alert_add_callback_map[g_observer->alerts.output_volume]);
    EXPECT_EQ(reinterpret_cast<void *>(output_mute_alert),
              cras_alert_add_callback_map[g_observer->alerts.output_mute]);
    EXPECT_EQ(reinterpret_cast<void *>(capture_gain_alert),
              cras_alert_add_callback_map[g_observer->alerts.capture_gain]);
    EXPECT_EQ(reinterpret_cast<void *>(capture_mute_alert),
              cras_alert_add_callback_map[g_observer->alerts.capture_mute]);
    EXPECT_EQ(reinterpret_cast<void *>(nodes_alert),
              cras_alert_add_callback_map[g_observer->alerts.nodes]);
    EXPECT_EQ(reinterpret_cast<void *>(nodes_prepare),
              cras_alert_create_prepare_map[g_observer->alerts.nodes]);
    EXPECT_EQ(reinterpret_cast<void *>(active_node_alert),
              cras_alert_add_callback_map[g_observer->alerts.active_node]);
    EXPECT_EQ(CRAS_ALERT_FLAG_KEEP_ALL_DATA,
              cras_alert_create_flags_map[g_observer->alerts.active_node]);
    EXPECT_EQ(reinterpret_cast<void *>(output_node_volume_alert),
            cras_alert_add_callback_map[g_observer->alerts.output_node_volume]);
    EXPECT_EQ(reinterpret_cast<void *>(node_left_right_swapped_alert),
       cras_alert_add_callback_map[g_observer->alerts.node_left_right_swapped]);
    EXPECT_EQ(reinterpret_cast<void *>(input_node_gain_alert),
            cras_alert_add_callback_map[g_observer->alerts.input_node_gain]);
    EXPECT_EQ(reinterpret_cast<void *>(num_active_streams_alert),
       cras_alert_add_callback_map[g_observer->alerts.num_active_streams[
                                               CRAS_STREAM_OUTPUT]]);
    EXPECT_EQ(reinterpret_cast<void *>(num_active_streams_alert),
       cras_alert_add_callback_map[g_observer->alerts.num_active_streams[
                                               CRAS_STREAM_INPUT]]);
    EXPECT_EQ(reinterpret_cast<void *>(num_active_streams_alert),
       cras_alert_add_callback_map[g_observer->alerts.num_active_streams[
                                               CRAS_STREAM_POST_MIX_PRE_DSP]]);
    EXPECT_EQ(reinterpret_cast<void *>(suspend_changed_alert),
       cras_alert_add_callback_map[g_observer->alerts.suspend_changed]);
    EXPECT_EQ(reinterpret_cast<void *>(hotword_triggered_alert),
        cras_alert_add_callback_map[g_observer->alerts.hotword_triggered]);
    EXPECT_EQ(reinterpret_cast<void *>(non_empty_audio_state_changed_alert),
        cras_alert_add_callback_map[
                g_observer->alerts.non_empty_audio_state_changed]);

    cras_observer_get_ops(NULL, &ops1_);
    EXPECT_NE(0, cras_observer_ops_are_empty(&ops1_));

    cras_observer_get_ops(NULL, &ops2_);
    EXPECT_NE(0, cras_observer_ops_are_empty(&ops2_));

    context1_ = reinterpret_cast<void *>(1);
    context2_ = reinterpret_cast<void *>(2);
  }

  virtual void TearDown() {
    cras_observer_server_free();
    EXPECT_EQ(15, cras_alert_destroy_called);
    ResetStubData();
  }

  void DoObserverAlert(cras_alert_cb alert, void *data) {
    client1_ = cras_observer_add(&ops1_, context1_);
    client2_ = cras_observer_add(&ops2_, context2_);
    ASSERT_NE(client1_, reinterpret_cast<struct cras_observer_client *>(NULL));
    ASSERT_NE(client2_, reinterpret_cast<struct cras_observer_client *>(NULL));

    ASSERT_NE(alert, reinterpret_cast<cras_alert_cb>(NULL));
    alert(NULL, data);

    EXPECT_EQ(cb_context[0], context1_);
    EXPECT_EQ(cb_context[1], context2_);
  }

  void DoObserverRemoveClear(cras_alert_cb alert, void *data) {
    ASSERT_NE(alert, reinterpret_cast<cras_alert_cb>(NULL));
    ASSERT_NE(client1_, reinterpret_cast<struct cras_observer_client *>(NULL));
    ASSERT_NE(client2_, reinterpret_cast<struct cras_observer_client *>(NULL));

    // Test observer removal.
    cras_observer_remove(client1_);
    cb_context.clear();
    alert(NULL, data);
    EXPECT_EQ(cb_context[0], context2_);
    EXPECT_EQ(cb_context.size(), 1);

    // Clear out ops1_.
    cras_observer_get_ops(NULL, &ops1_);
    EXPECT_NE(0, cras_observer_ops_are_empty(&ops1_));

    // Get the current value of ops2_ into ops1_.
    cras_observer_get_ops(client2_, &ops1_);
    EXPECT_EQ(0, memcmp((void *)&ops1_, (void *)&ops2_, sizeof(ops1_)));

    // Clear out opts for client2.
    cras_observer_get_ops(NULL, &ops2_);
    EXPECT_NE(0, cras_observer_ops_are_empty(&ops2_));
    cras_observer_set_ops(client2_, &ops2_);

    cras_observer_remove(client2_);
    cb_context.clear();
    alert(NULL, data);
    // No callbacks executed.
    EXPECT_EQ(cb_context.size(), 0);
  }

  struct cras_observer_client *client1_;
  struct cras_observer_client *client2_;
  struct cras_observer_ops ops1_;
  struct cras_observer_ops ops2_;
  void *context1_;
  void *context2_;
};

TEST_F(ObserverTest, NotifyOutputVolume) {
  struct cras_observer_alert_data_volume *data;
  const int32_t volume = 100;

  cras_observer_notify_output_volume(volume);
  EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.output_volume);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_volume *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->volume, volume);

  ops1_.output_volume_changed = cb_output_volume_changed;
  ops2_.output_volume_changed = cb_output_volume_changed;
  DoObserverAlert(output_volume_alert, data);
  ASSERT_EQ(2, cb_output_volume_changed_called);
  EXPECT_EQ(cb_output_volume_changed_volume[0], volume);
  EXPECT_EQ(cb_output_volume_changed_volume[1], volume);

  DoObserverRemoveClear(output_volume_alert, data);
};

TEST_F(ObserverTest, NotifyOutputMute) {
  struct cras_observer_alert_data_mute *data;
  const int muted = 1;
  const int user_muted = 0;
  const int mute_locked = 0;

  cras_observer_notify_output_mute(muted, user_muted, mute_locked);
  EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.output_mute);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_mute *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->muted, muted);
  EXPECT_EQ(data->user_muted, user_muted);
  EXPECT_EQ(data->mute_locked, mute_locked);

  ops1_.output_mute_changed = cb_output_mute_changed;
  ops2_.output_mute_changed = cb_output_mute_changed;
  DoObserverAlert(output_mute_alert, data);
  ASSERT_EQ(2, cb_output_mute_changed_called);
  EXPECT_EQ(cb_output_mute_changed_muted[0], muted);
  EXPECT_EQ(cb_output_mute_changed_muted[1], muted);
  EXPECT_EQ(cb_output_mute_changed_user_muted[0], user_muted);
  EXPECT_EQ(cb_output_mute_changed_user_muted[1], user_muted);
  EXPECT_EQ(cb_output_mute_changed_mute_locked[0], mute_locked);
  EXPECT_EQ(cb_output_mute_changed_mute_locked[1], mute_locked);

  DoObserverRemoveClear(output_mute_alert, data);
};

TEST_F(ObserverTest, NotifyCaptureGain) {
  struct cras_observer_alert_data_volume *data;
  const int32_t gain = -20;

  cras_observer_notify_capture_gain(gain);
  EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.capture_gain);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_volume *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->volume, gain);

  ops1_.capture_gain_changed = cb_capture_gain_changed;
  ops2_.capture_gain_changed = cb_capture_gain_changed;
  DoObserverAlert(capture_gain_alert, data);
  ASSERT_EQ(2, cb_capture_gain_changed_called);
  EXPECT_EQ(cb_capture_gain_changed_gain[0], gain);
  EXPECT_EQ(cb_capture_gain_changed_gain[1], gain);

  DoObserverRemoveClear(capture_gain_alert, data);
};

TEST_F(ObserverTest, NotifyCaptureMute) {
  struct cras_observer_alert_data_mute *data;
  const int muted = 1;
  const int mute_locked = 0;

  cras_observer_notify_capture_mute(muted, mute_locked);
  EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.capture_mute);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_mute *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->muted, muted);
  EXPECT_EQ(data->mute_locked, mute_locked);

  ops1_.capture_mute_changed = cb_capture_mute_changed;
  ops2_.capture_mute_changed = cb_capture_mute_changed;
  DoObserverAlert(capture_mute_alert, data);
  ASSERT_EQ(2, cb_capture_mute_changed_called);
  EXPECT_EQ(cb_capture_mute_changed_muted[0], muted);
  EXPECT_EQ(cb_capture_mute_changed_muted[1], muted);
  EXPECT_EQ(cb_capture_mute_changed_mute_locked[0], mute_locked);
  EXPECT_EQ(cb_capture_mute_changed_mute_locked[1], mute_locked);

  DoObserverRemoveClear(capture_mute_alert, data);
};

TEST_F(ObserverTest, NotifyNodes) {
  cras_observer_notify_nodes();
  EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.nodes);

  ops1_.nodes_changed = cb_nodes_changed;
  ops2_.nodes_changed = cb_nodes_changed;
  DoObserverAlert(nodes_alert, NULL);
  ASSERT_EQ(2, cb_nodes_changed_called);

  DoObserverRemoveClear(nodes_alert, NULL);
};

TEST_F(ObserverTest, NotifyActiveNode) {
  struct cras_observer_alert_data_active_node *data;
  const enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_INPUT;
  const cras_node_id_t node_id = 0x0001000100020002;

  cras_observer_notify_active_node(dir, node_id);
  EXPECT_EQ(cras_alert_pending_alert_value, g_observer->alerts.active_node);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_active_node *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->node_id, node_id);
  EXPECT_EQ(data->direction, dir);

  ops1_.active_node_changed = cb_active_node_changed;
  ops2_.active_node_changed = cb_active_node_changed;
  DoObserverAlert(active_node_alert, data);
  ASSERT_EQ(2, cb_active_node_changed_called);
  EXPECT_EQ(cb_active_node_changed_dir[0], dir);
  EXPECT_EQ(cb_active_node_changed_dir[1], dir);
  EXPECT_EQ(cb_active_node_changed_node_id[0], node_id);
  EXPECT_EQ(cb_active_node_changed_node_id[1], node_id);

  DoObserverRemoveClear(active_node_alert, data);
};

TEST_F(ObserverTest, NotifyOutputNodeVolume) {
  struct cras_observer_alert_data_node_volume *data;
  const cras_node_id_t node_id = 0x0001000100020002;
  const int32_t volume = 100;

  cras_observer_notify_output_node_volume(node_id, volume);
  EXPECT_EQ(cras_alert_pending_alert_value,
            g_observer->alerts.output_node_volume);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_node_volume *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->node_id, node_id);
  EXPECT_EQ(data->volume, volume);

  ops1_.output_node_volume_changed = cb_output_node_volume_changed;
  ops2_.output_node_volume_changed = cb_output_node_volume_changed;
  DoObserverAlert(output_node_volume_alert, data);
  ASSERT_EQ(2, cb_output_node_volume_changed_called);
  EXPECT_EQ(cb_output_node_volume_changed_volume[0], volume);
  EXPECT_EQ(cb_output_node_volume_changed_volume[1], volume);
  EXPECT_EQ(cb_output_node_volume_changed_node_id[0], node_id);
  EXPECT_EQ(cb_output_node_volume_changed_node_id[1], node_id);

  DoObserverRemoveClear(output_node_volume_alert, data);
};

TEST_F(ObserverTest, NotifyNodeLeftRightSwapped) {
  struct cras_observer_alert_data_node_lr_swapped *data;
  const cras_node_id_t node_id = 0x0001000100020002;
  const int swapped = 1;

  cras_observer_notify_node_left_right_swapped(node_id, swapped);
  EXPECT_EQ(cras_alert_pending_alert_value,
            g_observer->alerts.node_left_right_swapped);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_node_lr_swapped *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->node_id, node_id);
  EXPECT_EQ(data->swapped, swapped);

  ops1_.node_left_right_swapped_changed = cb_node_left_right_swapped_changed;
  ops2_.node_left_right_swapped_changed = cb_node_left_right_swapped_changed;
  DoObserverAlert(node_left_right_swapped_alert, data);
  ASSERT_EQ(2, cb_node_left_right_swapped_changed_called);
  EXPECT_EQ(cb_node_left_right_swapped_changed_swapped[0], swapped);
  EXPECT_EQ(cb_node_left_right_swapped_changed_swapped[1], swapped);
  EXPECT_EQ(cb_node_left_right_swapped_changed_node_id[0], node_id);
  EXPECT_EQ(cb_node_left_right_swapped_changed_node_id[1], node_id);

  DoObserverRemoveClear(node_left_right_swapped_alert, data);
};

TEST_F(ObserverTest, NotifyInputNodeGain) {
  struct cras_observer_alert_data_node_volume *data;
  const cras_node_id_t node_id = 0x0001000100020002;
  const int32_t gain = -20;

  cras_observer_notify_input_node_gain(node_id, gain);
  EXPECT_EQ(cras_alert_pending_alert_value,
            g_observer->alerts.input_node_gain);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_node_volume *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->node_id, node_id);
  EXPECT_EQ(data->volume, gain);

  ops1_.input_node_gain_changed = cb_input_node_gain_changed;
  ops2_.input_node_gain_changed = cb_input_node_gain_changed;
  DoObserverAlert(input_node_gain_alert, data);
  ASSERT_EQ(2, cb_input_node_gain_changed_called);
  EXPECT_EQ(cb_input_node_gain_changed_gain[0], gain);
  EXPECT_EQ(cb_input_node_gain_changed_gain[1], gain);
  EXPECT_EQ(cb_input_node_gain_changed_node_id[0], node_id);
  EXPECT_EQ(cb_input_node_gain_changed_node_id[1], node_id);

  DoObserverRemoveClear(input_node_gain_alert, data);
};

TEST_F(ObserverTest, NotifySuspendChanged) {
  struct cras_observer_alert_data_suspend *data;

  cras_observer_notify_suspend_changed(1);
  EXPECT_EQ(cras_alert_pending_alert_value,
            g_observer->alerts.suspend_changed);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_suspend *>(
      cras_alert_pending_data_value);
  EXPECT_EQ(data->suspended, 1);

  cras_observer_notify_suspend_changed(0);
  EXPECT_EQ(cras_alert_pending_alert_value,
            g_observer->alerts.suspend_changed);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_suspend *>(
      cras_alert_pending_data_value);
  EXPECT_EQ(data->suspended, 0);
}

TEST_F(ObserverTest, NotifyNumActiveStreams) {
  struct cras_observer_alert_data_streams *data;
  const enum CRAS_STREAM_DIRECTION dir = CRAS_STREAM_INPUT;
  const uint32_t active_streams = 10;

  cras_observer_notify_num_active_streams(dir, active_streams);
  EXPECT_EQ(cras_alert_pending_alert_value,
            g_observer->alerts.num_active_streams[CRAS_STREAM_INPUT]);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_streams *>(
             cras_alert_pending_data_value);
  EXPECT_EQ(data->num_active_streams, active_streams);
  EXPECT_EQ(data->direction, dir);

  ops1_.num_active_streams_changed = cb_num_active_streams_changed;
  ops2_.num_active_streams_changed = cb_num_active_streams_changed;
  DoObserverAlert(num_active_streams_alert, data);
  ASSERT_EQ(2, cb_num_active_streams_changed_called);
  EXPECT_EQ(cb_num_active_streams_changed_dir[0], dir);
  EXPECT_EQ(cb_num_active_streams_changed_dir[1], dir);
  EXPECT_EQ(cb_num_active_streams_changed_num[0], active_streams);
  EXPECT_EQ(cb_num_active_streams_changed_num[1], active_streams);

  DoObserverRemoveClear(num_active_streams_alert, data);
};

TEST_F(ObserverTest, NotifyHotwordTriggered) {
  struct cras_observer_alert_data_hotword_triggered *data;

  cras_observer_notify_hotword_triggered(100, 200);
  EXPECT_EQ(cras_alert_pending_alert_value,
      g_observer->alerts.hotword_triggered);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_alert_data_hotword_triggered *>(
          cras_alert_pending_data_value);
  EXPECT_EQ(data->tv_sec, 100);
  EXPECT_EQ(data->tv_nsec, 200);
}

TEST_F(ObserverTest, NonEmpyAudioStateChanged) {
  struct cras_observer_non_empty_audio_state *data;

  cras_observer_notify_non_empty_audio_state_changed(1);
  EXPECT_EQ(cras_alert_pending_alert_value,
    g_observer->alerts.non_empty_audio_state_changed);
  ASSERT_EQ(cras_alert_pending_data_size_value, sizeof(*data));
  ASSERT_NE(cras_alert_pending_data_value, reinterpret_cast<void *>(NULL));
  data = reinterpret_cast<struct cras_observer_non_empty_audio_state *>(
    cras_alert_pending_data_value);
  EXPECT_EQ(data->non_empty, 1);
}

// Stubs
extern "C" {

void cras_alert_destroy(struct cras_alert *alert) {
  cras_alert_destroy_called++;
}

struct cras_alert *cras_alert_create(cras_alert_prepare prepare,
                                     unsigned int flags) {
  struct cras_alert *alert = NULL;

  cras_alert_create_called++;
  alert = reinterpret_cast<struct cras_alert*>(cras_alert_create_called);
  cras_alert_create_return_values.push_back(alert);
  cras_alert_create_flags_map[alert] = flags;
  cras_alert_create_prepare_map[alert] = reinterpret_cast<void *>(prepare);
  return alert;
}

int cras_alert_add_callback(struct cras_alert *alert, cras_alert_cb cb,
			    void *arg) {
  cras_alert_add_callback_map[alert] = reinterpret_cast<void *>(cb);
  return 0;
}

void cras_alert_pending(struct cras_alert *alert) {
  cras_alert_pending_alert_value = alert;
}

void cras_alert_pending_data(struct cras_alert *alert,
			     void *data, size_t data_size) {
  cras_alert_pending_alert_value = alert;
  cras_alert_pending_data_size_value = data_size;
  if (cras_alert_pending_data_value)
    free(cras_alert_pending_data_value);
  if (data) {
    cras_alert_pending_data_value = malloc(data_size);
    memcpy(cras_alert_pending_data_value, data, data_size);
  }
  else
    cras_alert_pending_data_value = NULL;
}

void cras_iodev_list_update_device_list() {
  cras_iodev_list_update_device_list_called++;
}

}  // extern "C"

}  // namespace

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  openlog(NULL, LOG_PERROR, LOG_USER);
  return RUN_ALL_TESTS();
}