// Copyright (c) 2012 The Chromium 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 <string>

#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/test/test_simple_task_runner.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_win.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_task_manager_win.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {

const char kAdapterAddress[] = "A1:B2:C3:D4:E5:F6";
const char kAdapterName[] = "Bluetooth Adapter Name";


void MakeDeviceState(const std::string& name,
                     const std::string& address,
                     device::BluetoothTaskManagerWin::DeviceState* state) {
  state->name = name;
  state->address = address;
  state->bluetooth_class = 0;
  state->authenticated = false;
  state->connected = false;
}

class AdapterObserver : public device::BluetoothAdapter::Observer {
 public:
  AdapterObserver()
      : num_present_changed_(0),
        num_powered_changed_(0),
        num_discovering_changed_(0),
        num_device_added_(0) {
  }

  virtual void AdapterPresentChanged(
      device::BluetoothAdapter* adapter, bool present) OVERRIDE {
    num_present_changed_++;
  }

  virtual void AdapterPoweredChanged(
      device::BluetoothAdapter* adapter, bool powered) OVERRIDE {
    num_powered_changed_++;
  }

  virtual void AdapterDiscoveringChanged(
      device::BluetoothAdapter* adapter, bool discovering) OVERRIDE {
    num_discovering_changed_++;
  }

  virtual void DeviceAdded(
      device::BluetoothAdapter* adapter,
      device::BluetoothDevice* device) OVERRIDE {
    num_device_added_++;
  }

  int num_present_changed() const {
    return num_present_changed_;
  }

  int num_powered_changed() const {
    return num_powered_changed_;
  }

  int num_discovering_changed() const {
    return num_discovering_changed_;
  }

  int num_device_added() const {
    return num_device_added_;
  }

 private:
  int num_present_changed_;
  int num_powered_changed_;
  int num_discovering_changed_;
  int num_device_added_;
};

}  // namespace

namespace device {

class BluetoothAdapterWinTest : public testing::Test {
 public:
  BluetoothAdapterWinTest()
      : ui_task_runner_(new base::TestSimpleTaskRunner()),
        bluetooth_task_runner_(new base::TestSimpleTaskRunner()),
        adapter_(new BluetoothAdapterWin(
          base::Bind(&BluetoothAdapterWinTest::RunInitCallback,
                     base::Unretained(this)))),
        adapter_win_(static_cast<BluetoothAdapterWin*>(adapter_.get())),
        init_callback_called_(false) {
    adapter_win_->InitForTest(ui_task_runner_, bluetooth_task_runner_);
  }

  virtual void SetUp() OVERRIDE {
    adapter_win_->AddObserver(&adapter_observer_);
    num_start_discovery_callbacks_ = 0;
    num_start_discovery_error_callbacks_ = 0;
    num_stop_discovery_callbacks_ = 0;
    num_stop_discovery_error_callbacks_ = 0;
  }

  virtual void TearDown() OVERRIDE {
    adapter_win_->RemoveObserver(&adapter_observer_);
  }

  void RunInitCallback() {
    init_callback_called_ = true;
  }

  void IncrementNumStartDiscoveryCallbacks() {
    num_start_discovery_callbacks_++;
  }

  void IncrementNumStartDiscoveryErrorCallbacks() {
    num_start_discovery_error_callbacks_++;
  }

  void IncrementNumStopDiscoveryCallbacks() {
    num_stop_discovery_callbacks_++;
  }

  void IncrementNumStopDiscoveryErrorCallbacks() {
    num_stop_discovery_error_callbacks_++;
  }

  void CallAddDiscoverySession(
      const base::Closure& callback,
      const BluetoothAdapter::ErrorCallback& error_callback) {
    adapter_win_->AddDiscoverySession(callback, error_callback);
  }

  void CallRemoveDiscoverySession(
      const base::Closure& callback,
      const BluetoothAdapter::ErrorCallback& error_callback) {
    adapter_win_->RemoveDiscoverySession(callback, error_callback);
  }

 protected:
  scoped_refptr<base::TestSimpleTaskRunner> ui_task_runner_;
  scoped_refptr<base::TestSimpleTaskRunner> bluetooth_task_runner_;
  scoped_refptr<BluetoothAdapter> adapter_;
  BluetoothAdapterWin* adapter_win_;
  AdapterObserver adapter_observer_;
  bool init_callback_called_;
  int num_start_discovery_callbacks_;
  int num_start_discovery_error_callbacks_;
  int num_stop_discovery_callbacks_;
  int num_stop_discovery_error_callbacks_;
};

TEST_F(BluetoothAdapterWinTest, AdapterNotPresent) {
  BluetoothTaskManagerWin::AdapterState state;
  adapter_win_->AdapterStateChanged(state);
  EXPECT_FALSE(adapter_win_->IsPresent());
}

TEST_F(BluetoothAdapterWinTest, AdapterPresent) {
  BluetoothTaskManagerWin::AdapterState state;
  state.address = kAdapterAddress;
  state.name = kAdapterName;
  adapter_win_->AdapterStateChanged(state);
  EXPECT_TRUE(adapter_win_->IsPresent());
}

TEST_F(BluetoothAdapterWinTest, AdapterPresentChanged) {
  BluetoothTaskManagerWin::AdapterState state;
  state.address = kAdapterAddress;
  state.name = kAdapterName;
  adapter_win_->AdapterStateChanged(state);
  EXPECT_EQ(1, adapter_observer_.num_present_changed());
  adapter_win_->AdapterStateChanged(state);
  EXPECT_EQ(1, adapter_observer_.num_present_changed());
  BluetoothTaskManagerWin::AdapterState empty_state;
  adapter_win_->AdapterStateChanged(empty_state);
  EXPECT_EQ(2, adapter_observer_.num_present_changed());
}

TEST_F(BluetoothAdapterWinTest, AdapterPoweredChanged) {
  BluetoothTaskManagerWin::AdapterState state;
  state.powered = true;
  adapter_win_->AdapterStateChanged(state);
  EXPECT_EQ(1, adapter_observer_.num_powered_changed());
  adapter_win_->AdapterStateChanged(state);
  EXPECT_EQ(1, adapter_observer_.num_powered_changed());
  state.powered = false;
  adapter_win_->AdapterStateChanged(state);
  EXPECT_EQ(2, adapter_observer_.num_powered_changed());
}

TEST_F(BluetoothAdapterWinTest, AdapterInitialized) {
  EXPECT_FALSE(adapter_win_->IsInitialized());
  EXPECT_FALSE(init_callback_called_);
  BluetoothTaskManagerWin::AdapterState state;
  adapter_win_->AdapterStateChanged(state);
  EXPECT_TRUE(adapter_win_->IsInitialized());
  EXPECT_TRUE(init_callback_called_);
}

TEST_F(BluetoothAdapterWinTest, SingleStartDiscovery) {
  bluetooth_task_runner_->ClearPendingTasks();
  CallAddDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  EXPECT_TRUE(ui_task_runner_->GetPendingTasks().empty());
  EXPECT_EQ(1, bluetooth_task_runner_->GetPendingTasks().size());
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_EQ(0, num_start_discovery_callbacks_);
  adapter_win_->DiscoveryStarted(true);
  ui_task_runner_->RunPendingTasks();
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_EQ(1, num_start_discovery_callbacks_);
  EXPECT_EQ(1, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest, SingleStartDiscoveryFailure) {
  CallAddDiscoverySession(
      base::Closure(),
      base::Bind(
          &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
          base::Unretained(this)));
  adapter_win_->DiscoveryStarted(false);
  ui_task_runner_->RunPendingTasks();
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_EQ(1, num_start_discovery_error_callbacks_);
  EXPECT_EQ(0, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveries) {
  bluetooth_task_runner_->ClearPendingTasks();
  int num_discoveries = 5;
  for (int i = 0; i < num_discoveries; i++) {
    CallAddDiscoverySession(
        base::Bind(
            &BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
            base::Unretained(this)),
        BluetoothAdapter::ErrorCallback());
    EXPECT_EQ(1, bluetooth_task_runner_->GetPendingTasks().size());
  }
  EXPECT_TRUE(ui_task_runner_->GetPendingTasks().empty());
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_EQ(0, num_start_discovery_callbacks_);
  adapter_win_->DiscoveryStarted(true);
  ui_task_runner_->RunPendingTasks();
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_EQ(num_discoveries, num_start_discovery_callbacks_);
  EXPECT_EQ(1, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesFailure) {
  int num_discoveries = 5;
  for (int i = 0; i < num_discoveries; i++) {
    CallAddDiscoverySession(
        base::Closure(),
        base::Bind(
            &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
            base::Unretained(this)));
  }
  adapter_win_->DiscoveryStarted(false);
  ui_task_runner_->RunPendingTasks();
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_EQ(num_discoveries, num_start_discovery_error_callbacks_);
  EXPECT_EQ(0, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest, MultipleStartDiscoveriesAfterDiscovering) {
  CallAddDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStarted(true);
  ui_task_runner_->RunPendingTasks();
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_EQ(1, num_start_discovery_callbacks_);

  bluetooth_task_runner_->ClearPendingTasks();
  for (int i = 0; i < 5; i++) {
    int num_start_discovery_callbacks = num_start_discovery_callbacks_;
    CallAddDiscoverySession(
        base::Bind(
           &BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
           base::Unretained(this)),
        BluetoothAdapter::ErrorCallback());
    EXPECT_TRUE(adapter_->IsDiscovering());
    EXPECT_TRUE(bluetooth_task_runner_->GetPendingTasks().empty());
    EXPECT_TRUE(ui_task_runner_->GetPendingTasks().empty());
    EXPECT_EQ(num_start_discovery_callbacks + 1,
              num_start_discovery_callbacks_);
  }
  EXPECT_EQ(1, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest, StartDiscoveryAfterDiscoveringFailure) {
  CallAddDiscoverySession(
      base::Closure(),
      base::Bind(
          &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
          base::Unretained(this)));
  adapter_win_->DiscoveryStarted(false);
  ui_task_runner_->RunPendingTasks();
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_EQ(1, num_start_discovery_error_callbacks_);

  CallAddDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStarted(true);
  ui_task_runner_->RunPendingTasks();
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_EQ(1, num_start_discovery_callbacks_);
}

TEST_F(BluetoothAdapterWinTest, SingleStopDiscovery) {
  CallAddDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStarted(true);
  ui_task_runner_->ClearPendingTasks();
  CallRemoveDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_EQ(0, num_stop_discovery_callbacks_);
  bluetooth_task_runner_->ClearPendingTasks();
  adapter_win_->DiscoveryStopped();
  ui_task_runner_->RunPendingTasks();
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_EQ(1, num_stop_discovery_callbacks_);
  EXPECT_TRUE(bluetooth_task_runner_->GetPendingTasks().empty());
  EXPECT_EQ(2, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest, MultipleStopDiscoveries) {
  int num_discoveries = 5;
  for (int i = 0; i < num_discoveries; i++) {
    CallAddDiscoverySession(
        base::Closure(), BluetoothAdapter::ErrorCallback());
  }
  adapter_win_->DiscoveryStarted(true);
  ui_task_runner_->ClearPendingTasks();
  bluetooth_task_runner_->ClearPendingTasks();
  for (int i = 0; i < num_discoveries - 1; i++) {
    CallRemoveDiscoverySession(
        base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                   base::Unretained(this)),
        BluetoothAdapter::ErrorCallback());
    EXPECT_TRUE(bluetooth_task_runner_->GetPendingTasks().empty());
    ui_task_runner_->RunPendingTasks();
    EXPECT_EQ(i + 1, num_stop_discovery_callbacks_);
  }
  CallRemoveDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  EXPECT_EQ(1, bluetooth_task_runner_->GetPendingTasks().size());
  EXPECT_TRUE(adapter_->IsDiscovering());
  adapter_win_->DiscoveryStopped();
  ui_task_runner_->RunPendingTasks();
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_EQ(num_discoveries, num_stop_discovery_callbacks_);
  EXPECT_EQ(2, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest,
       StartDiscoveryAndStartDiscoveryAndStopDiscoveries) {
  CallAddDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStarted(true);
  CallAddDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  ui_task_runner_->ClearPendingTasks();
  bluetooth_task_runner_->ClearPendingTasks();
  CallRemoveDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  EXPECT_TRUE(bluetooth_task_runner_->GetPendingTasks().empty());
  CallRemoveDiscoverySession(
      base::Bind(&BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
                 base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  EXPECT_EQ(1, bluetooth_task_runner_->GetPendingTasks().size());
}

TEST_F(BluetoothAdapterWinTest,
       StartDiscoveryAndStopDiscoveryAndStartDiscovery) {
  CallAddDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStarted(true);
  EXPECT_TRUE(adapter_->IsDiscovering());
  CallRemoveDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStopped();
  EXPECT_FALSE(adapter_->IsDiscovering());
  CallAddDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStarted(true);
  EXPECT_TRUE(adapter_->IsDiscovering());
}

TEST_F(BluetoothAdapterWinTest, StartDiscoveryBeforeDiscoveryStopped) {
  CallAddDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  adapter_win_->DiscoveryStarted(true);
  CallRemoveDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  CallAddDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  bluetooth_task_runner_->ClearPendingTasks();
  adapter_win_->DiscoveryStopped();
  EXPECT_EQ(1, bluetooth_task_runner_->GetPendingTasks().size());
}

TEST_F(BluetoothAdapterWinTest, StopDiscoveryWithoutStartDiscovery) {
  CallRemoveDiscoverySession(
      base::Closure(),
      base::Bind(
          &BluetoothAdapterWinTest::IncrementNumStopDiscoveryErrorCallbacks,
          base::Unretained(this)));
  EXPECT_EQ(1, num_stop_discovery_error_callbacks_);
}

TEST_F(BluetoothAdapterWinTest, StopDiscoveryBeforeDiscoveryStarted) {
  CallAddDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  CallRemoveDiscoverySession(
      base::Closure(), BluetoothAdapter::ErrorCallback());
  bluetooth_task_runner_->ClearPendingTasks();
  adapter_win_->DiscoveryStarted(true);
  EXPECT_EQ(1, bluetooth_task_runner_->GetPendingTasks().size());
}

TEST_F(BluetoothAdapterWinTest, StartAndStopBeforeDiscoveryStarted) {
  int num_expected_start_discoveries = 3;
  int num_expected_stop_discoveries = 2;
  for (int i = 0; i < num_expected_start_discoveries; i++) {
    CallAddDiscoverySession(
        base::Bind(
            &BluetoothAdapterWinTest::IncrementNumStartDiscoveryCallbacks,
            base::Unretained(this)),
        BluetoothAdapter::ErrorCallback());
  }
  for (int i = 0; i < num_expected_stop_discoveries; i++) {
    CallRemoveDiscoverySession(
        base::Bind(
            &BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
            base::Unretained(this)),
        BluetoothAdapter::ErrorCallback());
  }
  bluetooth_task_runner_->ClearPendingTasks();
  adapter_win_->DiscoveryStarted(true);
  EXPECT_TRUE(bluetooth_task_runner_->GetPendingTasks().empty());
  ui_task_runner_->RunPendingTasks();
  EXPECT_EQ(num_expected_start_discoveries, num_start_discovery_callbacks_);
  EXPECT_EQ(num_expected_stop_discoveries, num_stop_discovery_callbacks_);
}

TEST_F(BluetoothAdapterWinTest, StopDiscoveryBeforeDiscoveryStartedAndFailed) {
  CallAddDiscoverySession(
      base::Closure(),
      base::Bind(
          &BluetoothAdapterWinTest::IncrementNumStartDiscoveryErrorCallbacks,
          base::Unretained(this)));
  CallRemoveDiscoverySession(
      base::Bind(
          &BluetoothAdapterWinTest::IncrementNumStopDiscoveryCallbacks,
          base::Unretained(this)),
      BluetoothAdapter::ErrorCallback());
  ui_task_runner_->ClearPendingTasks();
  adapter_win_->DiscoveryStarted(false);
  ui_task_runner_->RunPendingTasks();
  EXPECT_EQ(1, num_start_discovery_error_callbacks_);
  EXPECT_EQ(1, num_stop_discovery_callbacks_);
  EXPECT_EQ(0, adapter_observer_.num_discovering_changed());
}

TEST_F(BluetoothAdapterWinTest, DevicesDiscovered) {
  BluetoothTaskManagerWin::DeviceState* android_phone_state =
      new BluetoothTaskManagerWin::DeviceState();
  MakeDeviceState("phone", "android phone address", android_phone_state);
  BluetoothTaskManagerWin::DeviceState* laptop_state =
      new BluetoothTaskManagerWin::DeviceState();
  MakeDeviceState("laptop", "laptop address", laptop_state);
  BluetoothTaskManagerWin::DeviceState* iphone_state =
      new BluetoothTaskManagerWin::DeviceState();
  MakeDeviceState("phone", "iphone address", iphone_state);
  ScopedVector<BluetoothTaskManagerWin::DeviceState> devices;
  devices.push_back(android_phone_state);
  devices.push_back(laptop_state);
  devices.push_back(iphone_state);

  adapter_win_->DevicesDiscovered(devices);
  EXPECT_EQ(3, adapter_observer_.num_device_added());
}

}  // namespace device