// Copyright 2018 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>

extern "C" {
#include "cras_audio_thread_monitor.c"
#include "cras_main_message.h"
}

// Function call counters
static int cras_system_state_add_snapshot_called;
static int audio_thread_dump_thread_info_called;

// Stub data
static enum CRAS_MAIN_MESSAGE_TYPE type_set;
struct cras_audio_thread_event_message message;

void ResetStubData() {
  cras_system_state_add_snapshot_called = 0;
  audio_thread_dump_thread_info_called = 0;
  type_set = (enum CRAS_MAIN_MESSAGE_TYPE) 999;
  message.event_type = (enum CRAS_AUDIO_THREAD_EVENT_TYPE)999;
}

namespace {

class AudioThreadMonitorTestSuite: public testing::Test {
  protected:
    virtual void SetUp() {
      ResetStubData();
    }

    virtual void TearDown() {
    }
};

TEST_F(AudioThreadMonitorTestSuite, Init) {
  cras_audio_thread_monitor_init();
  EXPECT_EQ(type_set, CRAS_MAIN_AUDIO_THREAD_EVENT);
}

TEST_F(AudioThreadMonitorTestSuite, Busyloop) {
  cras_audio_thread_busyloop();
  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_BUSYLOOP);
}

TEST_F(AudioThreadMonitorTestSuite, Debug) {
  cras_audio_thread_debug();
  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_DEBUG);
}

TEST_F(AudioThreadMonitorTestSuite, Underrun) {
  cras_audio_thread_underrun();
  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_UNDERRUN);
}

TEST_F(AudioThreadMonitorTestSuite, SevereUnderrun) {
  cras_audio_thread_severe_underrun();
  EXPECT_EQ(message.event_type, AUDIO_THREAD_EVENT_SEVERE_UNDERRUN);
}

TEST_F(AudioThreadMonitorTestSuite, TakeSnapshot) {
  take_snapshot(AUDIO_THREAD_EVENT_DEBUG);
  EXPECT_EQ(cras_system_state_add_snapshot_called, 1);
  EXPECT_EQ(audio_thread_dump_thread_info_called, 1);
}

TEST_F(AudioThreadMonitorTestSuite, EventHandlerDoubleCall) {
  struct cras_audio_thread_event_message msg;
  msg.event_type = AUDIO_THREAD_EVENT_DEBUG;
  handle_audio_thread_event_message((struct cras_main_message *)&msg, NULL);
  EXPECT_EQ(cras_system_state_add_snapshot_called, 1);
  EXPECT_EQ(audio_thread_dump_thread_info_called, 1);

  // take_snapshot shouldn't be called since the time interval is short
  handle_audio_thread_event_message((struct cras_main_message *)&msg, NULL);
  EXPECT_EQ(cras_system_state_add_snapshot_called, 1);
  EXPECT_EQ(audio_thread_dump_thread_info_called, 1);
}

TEST_F(AudioThreadMonitorTestSuite, EventHandlerIgnoreInvalidEvent) {
  struct cras_audio_thread_event_message msg;
  msg.event_type = (enum CRAS_AUDIO_THREAD_EVENT_TYPE)999;
  handle_audio_thread_event_message((struct cras_main_message *)&msg, NULL);
  EXPECT_EQ(cras_system_state_add_snapshot_called, 0);
  EXPECT_EQ(audio_thread_dump_thread_info_called, 0);
}

extern "C" {

void cras_system_state_add_snapshot(
  struct cras_audio_thread_snapshot *snapshot) {
  cras_system_state_add_snapshot_called ++;
}

struct audio_thread* cras_iodev_list_get_audio_thread() {
  return reinterpret_cast <struct audio_thread*>(0xff);
}

int audio_thread_dump_thread_info(struct audio_thread *thread,
                                  struct audio_debug_info *info) {
  audio_thread_dump_thread_info_called ++;
  return 0;
}

int cras_main_message_add_handler(enum CRAS_MAIN_MESSAGE_TYPE type,
                                  cras_message_callback callback,
                                  void *callback_data) {
  type_set = type;
  return 0;
}

int cras_main_message_send(struct cras_main_message *msg) {
  message = *(struct cras_audio_thread_event_message*)msg;
  return 0;
}

}  // extern "C"
}  // namespace

int main(int argc, char **argv) {
  ::testing::InitGoogleTest(&argc, argv);
  int rc = RUN_ALL_TESTS();

  return rc;
}