root/device/bluetooth/bluetooth_chromeos_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. adapter_
  2. AdapterPresentChanged
  3. AdapterPoweredChanged
  4. AdapterDiscoverableChanged
  5. AdapterDiscoveringChanged
  6. DeviceAdded
  7. DeviceChanged
  8. DeviceRemoved
  9. QuitMessageLoop
  10. last_entered_
  11. RequestPinCode
  12. RequestPasskey
  13. DisplayPinCode
  14. DisplayPasskey
  15. KeysEntered
  16. ConfirmPasskey
  17. AuthorizePairing
  18. QuitMessageLoop
  19. SetUp
  20. TearDown
  21. Callback
  22. DiscoverySessionCallback
  23. ErrorCallback
  24. DBusErrorCallback
  25. ConnectErrorCallback
  26. GetAdapter
  27. DiscoverDevice
  28. DiscoverDevices
  29. QuitMessageLoop
  30. TEST_F
  31. TEST_F
  32. TEST_F
  33. TEST_F
  34. TEST_F
  35. TEST_F
  36. TEST_F
  37. TEST_F
  38. TEST_F
  39. TEST_F
  40. TEST_F
  41. TEST_F
  42. TEST_F
  43. TEST_F
  44. TEST_F
  45. TEST_F
  46. TEST_F
  47. TEST_F
  48. TEST_F
  49. TEST_F
  50. TEST_F
  51. TEST_F
  52. TEST_F
  53. TEST_F
  54. TEST_F
  55. TEST_F
  56. TEST_F
  57. TEST_F
  58. TEST_F
  59. TEST_F
  60. TEST_F
  61. TEST_F
  62. TEST_F
  63. TEST_F
  64. TEST_F
  65. TEST_F
  66. TEST_F
  67. TEST_F
  68. TEST_F
  69. TEST_F
  70. TEST_F
  71. TEST_F
  72. TEST_F
  73. TEST_F
  74. TEST_F
  75. TEST_F
  76. TEST_F
  77. TEST_F
  78. TEST_F
  79. TEST_F
  80. TEST_F
  81. TEST_F
  82. TEST_F
  83. TEST_F
  84. TEST_F
  85. TEST_F

// Copyright 2013 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 "base/memory/scoped_vector.h"
#include "base/message_loop/message_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "chromeos/dbus/fake_bluetooth_adapter_client.h"
#include "chromeos/dbus/fake_bluetooth_agent_manager_client.h"
#include "chromeos/dbus/fake_bluetooth_device_client.h"
#include "chromeos/dbus/fake_bluetooth_input_client.h"
#include "chromeos/dbus/fake_dbus_thread_manager.h"
#include "dbus/object_path.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/bluetooth_adapter_chromeos.h"
#include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_device_chromeos.h"
#include "device/bluetooth/bluetooth_discovery_session.h"
#include "device/bluetooth/bluetooth_pairing_chromeos.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

using device::BluetoothAdapter;
using device::BluetoothAdapterFactory;
using device::BluetoothDevice;
using device::BluetoothDiscoverySession;
using device::BluetoothUUID;

namespace chromeos {

class TestObserver : public BluetoothAdapter::Observer {
 public:
  TestObserver(scoped_refptr<BluetoothAdapter> adapter)
      : present_changed_count_(0),
        powered_changed_count_(0),
        discoverable_changed_count_(0),
        discovering_changed_count_(0),
        last_present_(false),
        last_powered_(false),
        last_discovering_(false),
        device_added_count_(0),
        device_changed_count_(0),
        device_removed_count_(0),
        last_device_(NULL),
        adapter_(adapter) {
    adapter_->AddObserver(this);
  }

  virtual ~TestObserver() {
    adapter_->RemoveObserver(this);
  }

  virtual void AdapterPresentChanged(BluetoothAdapter* adapter,
                                     bool present) OVERRIDE {
    EXPECT_EQ(adapter_, adapter);

    ++present_changed_count_;
    last_present_ = present;
  }

  virtual void AdapterPoweredChanged(BluetoothAdapter* adapter,
                                     bool powered) OVERRIDE {
    EXPECT_EQ(adapter_, adapter);

    ++powered_changed_count_;
    last_powered_ = powered;
  }

  virtual void AdapterDiscoverableChanged(BluetoothAdapter* adapter,
                                          bool discoverable) OVERRIDE {
    EXPECT_EQ(adapter_, adapter);

    ++discoverable_changed_count_;
  }

  virtual void AdapterDiscoveringChanged(BluetoothAdapter* adapter,
                                         bool discovering) OVERRIDE {
    EXPECT_EQ(adapter_, adapter);

    ++discovering_changed_count_;
    last_discovering_ = discovering;
  }

  virtual void DeviceAdded(BluetoothAdapter* adapter,
                           BluetoothDevice* device) OVERRIDE {
    EXPECT_EQ(adapter_, adapter);

    ++device_added_count_;
    last_device_ = device;
    last_device_address_ = device->GetAddress();

    QuitMessageLoop();
  }

  virtual void DeviceChanged(BluetoothAdapter* adapter,
                             BluetoothDevice* device) OVERRIDE {
    EXPECT_EQ(adapter_, adapter);

    ++device_changed_count_;
    last_device_ = device;
    last_device_address_ = device->GetAddress();

    QuitMessageLoop();
  }

  virtual void DeviceRemoved(BluetoothAdapter* adapter,
                             BluetoothDevice* device) OVERRIDE {
    EXPECT_EQ(adapter_, adapter);

    ++device_removed_count_;
    // Can't save device, it may be freed
    last_device_address_ = device->GetAddress();

    QuitMessageLoop();
  }

  int present_changed_count_;
  int powered_changed_count_;
  int discoverable_changed_count_;
  int discovering_changed_count_;
  bool last_present_;
  bool last_powered_;
  bool last_discovering_;
  int device_added_count_;
  int device_changed_count_;
  int device_removed_count_;
  BluetoothDevice* last_device_;
  std::string last_device_address_;

 private:
  // Some tests use a message loop since background processing is simulated;
  // break out of those loops.
  void QuitMessageLoop() {
    if (base::MessageLoop::current() &&
        base::MessageLoop::current()->is_running())
      base::MessageLoop::current()->Quit();
  }

  scoped_refptr<BluetoothAdapter> adapter_;
};

class TestPairingDelegate : public BluetoothDevice::PairingDelegate {
 public:
  TestPairingDelegate()
      : call_count_(0),
        request_pincode_count_(0),
        request_passkey_count_(0),
        display_pincode_count_(0),
        display_passkey_count_(0),
        keys_entered_count_(0),
        confirm_passkey_count_(0),
        authorize_pairing_count_(0),
        last_passkey_(9999999U),
        last_entered_(999U) {}
  virtual ~TestPairingDelegate() {}

  virtual void RequestPinCode(BluetoothDevice* device) OVERRIDE {
    ++call_count_;
    ++request_pincode_count_;
    QuitMessageLoop();
  }

  virtual void RequestPasskey(BluetoothDevice* device) OVERRIDE {
    ++call_count_;
    ++request_passkey_count_;
    QuitMessageLoop();
  }

  virtual void DisplayPinCode(BluetoothDevice* device,
                              const std::string& pincode) OVERRIDE {
    ++call_count_;
    ++display_pincode_count_;
    last_pincode_ = pincode;
    QuitMessageLoop();
  }

  virtual void DisplayPasskey(BluetoothDevice* device,
                              uint32 passkey) OVERRIDE {
    ++call_count_;
    ++display_passkey_count_;
    last_passkey_ = passkey;
    QuitMessageLoop();
  }

  virtual void KeysEntered(BluetoothDevice* device, uint32 entered) OVERRIDE {
    ++call_count_;
    ++keys_entered_count_;
    last_entered_ = entered;
    QuitMessageLoop();
  }

  virtual void ConfirmPasskey(BluetoothDevice* device,
                              uint32 passkey) OVERRIDE {
    ++call_count_;
    ++confirm_passkey_count_;
    last_passkey_ = passkey;
    QuitMessageLoop();
  }

  virtual void AuthorizePairing(BluetoothDevice* device) OVERRIDE {
    ++call_count_;
    ++authorize_pairing_count_;
    QuitMessageLoop();
  }

  int call_count_;
  int request_pincode_count_;
  int request_passkey_count_;
  int display_pincode_count_;
  int display_passkey_count_;
  int keys_entered_count_;
  int confirm_passkey_count_;
  int authorize_pairing_count_;
  uint32 last_passkey_;
  uint32 last_entered_;
  std::string last_pincode_;

  private:
   // Some tests use a message loop since background processing is simulated;
   // break out of those loops.
   void QuitMessageLoop() {
     if (base::MessageLoop::current() &&
         base::MessageLoop::current()->is_running())
       base::MessageLoop::current()->Quit();
   }
};

class BluetoothChromeOSTest : public testing::Test {
 public:
  virtual void SetUp() {
    FakeDBusThreadManager* fake_dbus_thread_manager = new FakeDBusThreadManager;
    fake_bluetooth_adapter_client_ = new FakeBluetoothAdapterClient;
    fake_dbus_thread_manager->SetBluetoothAdapterClient(
        scoped_ptr<BluetoothAdapterClient>(fake_bluetooth_adapter_client_));
    fake_bluetooth_device_client_ = new FakeBluetoothDeviceClient;
    fake_dbus_thread_manager->SetBluetoothDeviceClient(
        scoped_ptr<BluetoothDeviceClient>(fake_bluetooth_device_client_));
    fake_dbus_thread_manager->SetBluetoothInputClient(
        scoped_ptr<BluetoothInputClient>(new FakeBluetoothInputClient));
    fake_dbus_thread_manager->SetBluetoothAgentManagerClient(
        scoped_ptr<BluetoothAgentManagerClient>(
            new FakeBluetoothAgentManagerClient));
    DBusThreadManager::InitializeForTesting(fake_dbus_thread_manager);

    fake_bluetooth_adapter_client_->SetSimulationIntervalMs(10);

    callback_count_ = 0;
    error_callback_count_ = 0;
    last_connect_error_ = BluetoothDevice::ERROR_UNKNOWN;
    last_client_error_ = "";
  }

  virtual void TearDown() {
    for (ScopedVector<BluetoothDiscoverySession>::iterator iter =
            discovery_sessions_.begin();
         iter != discovery_sessions_.end();
         ++iter) {
      BluetoothDiscoverySession* session = *iter;
      if (!session->IsActive())
        continue;
      callback_count_ = 0;
      session->Stop(
          base::Bind(&BluetoothChromeOSTest::Callback,
                     base::Unretained(this)),
          base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                     base::Unretained(this)));
      message_loop_.Run();
      ASSERT_EQ(1, callback_count_);
    }
    discovery_sessions_.clear();
    adapter_ = NULL;
    DBusThreadManager::Shutdown();
  }

  // Generic callbacks
  void Callback() {
    ++callback_count_;
    QuitMessageLoop();
  }

  void DiscoverySessionCallback(
      scoped_ptr<BluetoothDiscoverySession> discovery_session) {
    ++callback_count_;
    discovery_sessions_.push_back(discovery_session.release());
    QuitMessageLoop();
  }

  void ErrorCallback() {
    ++error_callback_count_;
    QuitMessageLoop();
  }

  void DBusErrorCallback(const std::string& error_name,
                         const std::string& error_message) {
    ++error_callback_count_;
    last_client_error_ = error_name;
    QuitMessageLoop();
  }

  void ConnectErrorCallback(BluetoothDevice::ConnectErrorCode error) {
    ++error_callback_count_;
    last_connect_error_ = error;
  }

  // Call to fill the adapter_ member with a BluetoothAdapter instance.
  void GetAdapter() {
    adapter_ = new BluetoothAdapterChromeOS();
    ASSERT_TRUE(adapter_.get() != NULL);
    ASSERT_TRUE(adapter_->IsInitialized());
  }

  // Run a discovery phase until the named device is detected, or if the named
  // device is not created, the discovery process ends without finding it.
  //
  // The correct behavior of discovery is tested by the "Discovery" test case
  // without using this function.
  void DiscoverDevice(const std::string& address) {
    ASSERT_TRUE(adapter_.get() != NULL);
    ASSERT_TRUE(base::MessageLoop::current() != NULL);
    fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

    TestObserver observer(adapter_);

    adapter_->SetPowered(
        true,
        base::Bind(&BluetoothChromeOSTest::Callback,
                   base::Unretained(this)),
        base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                   base::Unretained(this)));
    adapter_->StartDiscoverySession(
        base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                   base::Unretained(this)),
        base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                   base::Unretained(this)));
    base::MessageLoop::current()->Run();
    ASSERT_EQ(2, callback_count_);
    ASSERT_EQ(0, error_callback_count_);
    ASSERT_EQ((size_t)1, discovery_sessions_.size());
    ASSERT_TRUE(discovery_sessions_[0]->IsActive());
    callback_count_ = 0;

    ASSERT_TRUE(adapter_->IsPowered());
    ASSERT_TRUE(adapter_->IsDiscovering());

    while (!observer.device_removed_count_ &&
           observer.last_device_address_ != address)
      base::MessageLoop::current()->Run();

    discovery_sessions_[0]->Stop(
        base::Bind(&BluetoothChromeOSTest::Callback,
                   base::Unretained(this)),
        base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                   base::Unretained(this)));
    base::MessageLoop::current()->Run();
    ASSERT_EQ(1, callback_count_);
    ASSERT_EQ(0, error_callback_count_);
    callback_count_ = 0;

    ASSERT_FALSE(adapter_->IsDiscovering());
  }

  // Run a discovery phase so we have devices that can be paired with.
  void DiscoverDevices() {
    // Pass an invalid address for the device so that the discovery process
    // completes with all devices.
    DiscoverDevice("does not exist");
  }

 protected:
  base::MessageLoop message_loop_;
  FakeBluetoothAdapterClient* fake_bluetooth_adapter_client_;
  FakeBluetoothDeviceClient* fake_bluetooth_device_client_;
  scoped_refptr<BluetoothAdapter> adapter_;

  int callback_count_;
  int error_callback_count_;
  enum BluetoothDevice::ConnectErrorCode last_connect_error_;
  std::string last_client_error_;
  ScopedVector<BluetoothDiscoverySession> discovery_sessions_;

 private:
  // Some tests use a message loop since background processing is simulated;
  // break out of those loops.
  void QuitMessageLoop() {
    if (base::MessageLoop::current() &&
        base::MessageLoop::current()->is_running())
      base::MessageLoop::current()->Quit();
  }
};

TEST_F(BluetoothChromeOSTest, AlreadyPresent) {
  GetAdapter();

  // This verifies that the class gets the list of adapters when created;
  // and initializes with an existing adapter if there is one.
  EXPECT_TRUE(adapter_->IsPresent());
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress,
            adapter_->GetAddress());
  EXPECT_FALSE(adapter_->IsDiscovering());

  // There should be a device
  BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
  EXPECT_EQ(1U, devices.size());
  EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            devices[0]->GetAddress());
}

TEST_F(BluetoothChromeOSTest, BecomePresent) {
  fake_bluetooth_adapter_client_->SetVisible(false);
  GetAdapter();
  ASSERT_FALSE(adapter_->IsPresent());

  // Install an observer; expect the AdapterPresentChanged to be called
  // with true, and IsPresent() to return true.
  TestObserver observer(adapter_);

  fake_bluetooth_adapter_client_->SetVisible(true);

  EXPECT_EQ(1, observer.present_changed_count_);
  EXPECT_TRUE(observer.last_present_);

  EXPECT_TRUE(adapter_->IsPresent());

  // We should have had a device announced.
  EXPECT_EQ(1, observer.device_added_count_);
  EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            observer.last_device_address_);

  // Other callbacks shouldn't be called if the values are false.
  EXPECT_EQ(0, observer.powered_changed_count_);
  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_FALSE(adapter_->IsDiscovering());
}

TEST_F(BluetoothChromeOSTest, BecomeNotPresent) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  // Install an observer; expect the AdapterPresentChanged to be called
  // with false, and IsPresent() to return false.
  TestObserver observer(adapter_);

  fake_bluetooth_adapter_client_->SetVisible(false);

  EXPECT_EQ(1, observer.present_changed_count_);
  EXPECT_FALSE(observer.last_present_);

  EXPECT_FALSE(adapter_->IsPresent());

  // We should have had a device removed.
  EXPECT_EQ(1, observer.device_removed_count_);
  EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            observer.last_device_address_);

  // Other callbacks shouldn't be called since the values are false.
  EXPECT_EQ(0, observer.powered_changed_count_);
  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_FALSE(adapter_->IsDiscovering());
}

TEST_F(BluetoothChromeOSTest, SecondAdapter) {
  GetAdapter();
  ASSERT_TRUE(adapter_->IsPresent());

  // Install an observer, then add a second adapter. Nothing should change,
  // we ignore the second adapter.
  TestObserver observer(adapter_);

  fake_bluetooth_adapter_client_->SetSecondVisible(true);

  EXPECT_EQ(0, observer.present_changed_count_);

  EXPECT_TRUE(adapter_->IsPresent());
  EXPECT_EQ(FakeBluetoothAdapterClient::kAdapterAddress,
            adapter_->GetAddress());

  // Try removing the first adapter, we should now act as if the adapter
  // is no longer present rather than fall back to the second.
  fake_bluetooth_adapter_client_->SetVisible(false);

  EXPECT_EQ(1, observer.present_changed_count_);
  EXPECT_FALSE(observer.last_present_);

  EXPECT_FALSE(adapter_->IsPresent());

  // We should have had a device removed.
  EXPECT_EQ(1, observer.device_removed_count_);
  EXPECT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            observer.last_device_address_);

  // Other callbacks shouldn't be called since the values are false.
  EXPECT_EQ(0, observer.powered_changed_count_);
  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(adapter_->IsPowered());
  EXPECT_FALSE(adapter_->IsDiscovering());

  observer.device_removed_count_ = 0;

  // Removing the second adapter shouldn't set anything either.
  fake_bluetooth_adapter_client_->SetSecondVisible(false);

  EXPECT_EQ(0, observer.device_removed_count_);
  EXPECT_EQ(0, observer.powered_changed_count_);
  EXPECT_EQ(0, observer.discovering_changed_count_);
}

TEST_F(BluetoothChromeOSTest, BecomePowered) {
  GetAdapter();
  ASSERT_FALSE(adapter_->IsPowered());

  // Install an observer; expect the AdapterPoweredChanged to be called
  // with true, and IsPowered() to return true.
  TestObserver observer(adapter_);

  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.powered_changed_count_);
  EXPECT_TRUE(observer.last_powered_);

  EXPECT_TRUE(adapter_->IsPowered());
}

TEST_F(BluetoothChromeOSTest, BecomeNotPowered) {
  GetAdapter();
  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  callback_count_ = 0;

  ASSERT_TRUE(adapter_->IsPowered());

  // Install an observer; expect the AdapterPoweredChanged to be called
  // with false, and IsPowered() to return false.
  TestObserver observer(adapter_);

  adapter_->SetPowered(
      false,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.powered_changed_count_);
  EXPECT_FALSE(observer.last_powered_);

  EXPECT_FALSE(adapter_->IsPowered());
}

TEST_F(BluetoothChromeOSTest, ChangeAdapterName) {
  GetAdapter();

  static const std::string new_name(".__.");

  adapter_->SetName(
      new_name,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(new_name, adapter_->GetName());
}

TEST_F(BluetoothChromeOSTest, BecomeDiscoverable) {
  GetAdapter();
  ASSERT_FALSE(adapter_->IsDiscoverable());

  // Install an observer; expect the AdapterDiscoverableChanged to be called
  // with true, and IsDiscoverable() to return true.
  TestObserver observer(adapter_);

  adapter_->SetDiscoverable(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.discoverable_changed_count_);

  EXPECT_TRUE(adapter_->IsDiscoverable());
}

TEST_F(BluetoothChromeOSTest, BecomeNotDiscoverable) {
  GetAdapter();
  adapter_->SetDiscoverable(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  callback_count_ = 0;

  ASSERT_TRUE(adapter_->IsDiscoverable());

  // Install an observer; expect the AdapterDiscoverableChanged to be called
  // with false, and IsDiscoverable() to return false.
  TestObserver observer(adapter_);

  adapter_->SetDiscoverable(
      false,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.discoverable_changed_count_);

  EXPECT_FALSE(adapter_->IsDiscoverable());
}

TEST_F(BluetoothChromeOSTest, StopDiscovery) {
  GetAdapter();

  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(2, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  callback_count_ = 0;

  ASSERT_TRUE(adapter_->IsPowered());
  ASSERT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  ASSERT_TRUE(discovery_sessions_[0]->IsActive());

  // Install an observer; aside from the callback, expect the
  // AdapterDiscoveringChanged method to be called and no longer to be
  // discovering,
  TestObserver observer(adapter_);

  discovery_sessions_[0]->Stop(
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);

  EXPECT_FALSE(adapter_->IsDiscovering());
}

TEST_F(BluetoothChromeOSTest, Discovery) {
  // Test a simulated discovery session.
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);
  GetAdapter();

  TestObserver observer(adapter_);

  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(2, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  callback_count_ = 0;

  ASSERT_TRUE(adapter_->IsPowered());
  ASSERT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  ASSERT_TRUE(discovery_sessions_[0]->IsActive());

  // First two devices to appear.
  message_loop_.Run();

  EXPECT_EQ(2, observer.device_added_count_);
  EXPECT_EQ(FakeBluetoothDeviceClient::kLowEnergyAddress,
            observer.last_device_address_);

  // Next we should get another two devices...
  message_loop_.Run();
  EXPECT_EQ(4, observer.device_added_count_);

  // Okay, let's run forward until a device is actually removed...
  while (!observer.device_removed_count_)
    message_loop_.Run();

  EXPECT_EQ(1, observer.device_removed_count_);
  EXPECT_EQ(FakeBluetoothDeviceClient::kVanishingDeviceAddress,
            observer.last_device_address_);
}

TEST_F(BluetoothChromeOSTest, PoweredAndDiscovering) {
  GetAdapter();
  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(2, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  callback_count_ = 0;
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  ASSERT_TRUE(discovery_sessions_[0]->IsActive());

  // Stop the timers that the simulation uses
  fake_bluetooth_device_client_->EndDiscoverySimulation(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));

  ASSERT_TRUE(adapter_->IsPowered());
  ASSERT_TRUE(adapter_->IsDiscovering());

  fake_bluetooth_adapter_client_->SetVisible(false);
  ASSERT_FALSE(adapter_->IsPresent());
  ASSERT_FALSE(discovery_sessions_[0]->IsActive());

  // Install an observer; expect the AdapterPresentChanged,
  // AdapterPoweredChanged and AdapterDiscoveringChanged methods to be called
  // with true, and IsPresent(), IsPowered() and IsDiscovering() to all
  // return true.
  TestObserver observer(adapter_);

  fake_bluetooth_adapter_client_->SetVisible(true);

  EXPECT_EQ(1, observer.present_changed_count_);
  EXPECT_TRUE(observer.last_present_);
  EXPECT_TRUE(adapter_->IsPresent());

  EXPECT_EQ(1, observer.powered_changed_count_);
  EXPECT_TRUE(observer.last_powered_);
  EXPECT_TRUE(adapter_->IsPowered());

  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());

  observer.present_changed_count_ = 0;
  observer.powered_changed_count_ = 0;
  observer.discovering_changed_count_ = 0;

  // Now mark the adapter not present again. Expect the methods to be called
  // again, to reset the properties back to false
  fake_bluetooth_adapter_client_->SetVisible(false);

  EXPECT_EQ(1, observer.present_changed_count_);
  EXPECT_FALSE(observer.last_present_);
  EXPECT_FALSE(adapter_->IsPresent());

  EXPECT_EQ(1, observer.powered_changed_count_);
  EXPECT_FALSE(observer.last_powered_);
  EXPECT_FALSE(adapter_->IsPowered());

  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
}

// This unit test asserts that the basic reference counting logic works
// correctly for discovery requests done via the BluetoothAdapter.
TEST_F(BluetoothChromeOSTest, MultipleDiscoverySessions) {
  GetAdapter();
  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsPowered());
  callback_count_ = 0;

  TestObserver observer(adapter_);

  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());

  // Request device discovery 3 times.
  for (int i = 0; i < 3; i++) {
    adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }
  // Run only once, as there should have been one D-Bus call.
  message_loop_.Run();

  // The observer should have received the discovering changed event exactly
  // once, the success callback should have been called 3 times and the adapter
  // should be discovering.
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(3, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());

  // Request to stop discovery twice.
  for (int i = 0; i < 2; i++) {
    discovery_sessions_[i]->Stop(
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }

  // The observer should have received no additional discovering changed events,
  // the success callback should have been called 2 times and the adapter should
  // still be discovering.
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(5, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
  EXPECT_FALSE(discovery_sessions_[1]->IsActive());
  EXPECT_TRUE(discovery_sessions_[2]->IsActive());

  // Request device discovery 3 times.
  for (int i = 0; i < 3; i++) {
    adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }

  // The observer should have received no additional discovering changed events,
  // the success callback should have been called 3 times and the adapter should
  // still be discovering.
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(8, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)6, discovery_sessions_.size());

  // Request to stop discovery 4 times.
  for (int i = 2; i < 6; i++) {
    discovery_sessions_[i]->Stop(
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }
  // Run only once, as there should have been one D-Bus call.
  message_loop_.Run();

  // The observer should have received the discovering changed event exactly
  // once, the success callback should have been called 4 times and the adapter
  // should no longer be discovering.
  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_EQ(12, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());

  // All discovery sessions should be inactive.
  for (int i = 0; i < 6; i++)
    EXPECT_FALSE(discovery_sessions_[i]->IsActive());

  // Request to stop discovery on of the inactive sessions.
  discovery_sessions_[0]->Stop(
    base::Bind(&BluetoothChromeOSTest::Callback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));

  // The call should have failed.
  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_EQ(12, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
}

// This unit test asserts that the reference counting logic works correctly in
// the cases when the adapter gets reset and D-Bus calls are made outside of
// the BluetoothAdapter.
TEST_F(BluetoothChromeOSTest,
       UnexpectedChangesDuringMultipleDiscoverySessions) {
  GetAdapter();
  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsPowered());
  callback_count_ = 0;

  TestObserver observer(adapter_);

  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());

  // Request device discovery 3 times.
  for (int i = 0; i < 3; i++) {
    adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }
  // Run only once, as there should have been one D-Bus call.
  message_loop_.Run();

  // The observer should have received the discovering changed event exactly
  // once, the success callback should have been called 3 times and the adapter
  // should be discovering.
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(3, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());

  for (int i = 0; i < 3; i++)
    EXPECT_TRUE(discovery_sessions_[i]->IsActive());

  // Stop the timers that the simulation uses
  fake_bluetooth_device_client_->EndDiscoverySimulation(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));

  ASSERT_TRUE(adapter_->IsPowered());
  ASSERT_TRUE(adapter_->IsDiscovering());

  // Stop device discovery behind the adapter. The adapter and the observer
  // should be notified of the change and the reference count should be reset.
  // Even though FakeBluetoothAdapterClient does its own reference counting and
  // we called 3 BluetoothAdapter::StartDiscoverySession 3 times, the
  // FakeBluetoothAdapterClient's count should be only 1 and a single call to
  // FakeBluetoothAdapterClient::StopDiscovery should work.
  fake_bluetooth_adapter_client_->StopDiscovery(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_EQ(4, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());

  // All discovery session instances should have been updated.
  for (int i = 0; i < 3; i++)
    EXPECT_FALSE(discovery_sessions_[i]->IsActive());
  discovery_sessions_.clear();

  // It should be possible to successfully start discovery.
  for (int i = 0; i < 2; i++) {
    adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }
  // Run only once, as there should have been one D-Bus call.
  message_loop_.Run();
  EXPECT_EQ(3, observer.discovering_changed_count_);
  EXPECT_EQ(6, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)2, discovery_sessions_.size());

  for (int i = 0; i < 2; i++)
    EXPECT_TRUE(discovery_sessions_[i]->IsActive());

  fake_bluetooth_device_client_->EndDiscoverySimulation(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));

  // Make the adapter disappear and appear. This will make it come back as
  // discovering. When this happens, the reference count should become and
  // remain 0 as no new request was made through the BluetoothAdapter.
  fake_bluetooth_adapter_client_->SetVisible(false);
  ASSERT_FALSE(adapter_->IsPresent());
  EXPECT_EQ(4, observer.discovering_changed_count_);
  EXPECT_EQ(6, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());

  for (int i = 0; i < 2; i++)
    EXPECT_FALSE(discovery_sessions_[i]->IsActive());
  discovery_sessions_.clear();

  fake_bluetooth_adapter_client_->SetVisible(true);
  ASSERT_TRUE(adapter_->IsPresent());
  EXPECT_EQ(5, observer.discovering_changed_count_);
  EXPECT_EQ(6, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());

  // Start and stop discovery. At this point, FakeBluetoothAdapterClient has
  // a reference count that is equal to 1. Pretend that this was done by an
  // application other than us. Starting and stopping discovery will succeed
  // but it won't cause the discovery state to change.
  adapter_->StartDiscoverySession(
    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));
  message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
  EXPECT_EQ(5, observer.discovering_changed_count_);
  EXPECT_EQ(7, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[0]->IsActive());

  discovery_sessions_[0]->Stop(
    base::Bind(&BluetoothChromeOSTest::Callback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));
  message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
  EXPECT_EQ(5, observer.discovering_changed_count_);
  EXPECT_EQ(8, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
  discovery_sessions_.clear();

  // Start discovery again.
  adapter_->StartDiscoverySession(
    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));
  message_loop_.Run();  // Run the loop, as there should have been a D-Bus call.
  EXPECT_EQ(5, observer.discovering_changed_count_);
  EXPECT_EQ(9, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[0]->IsActive());

  // Stop discovery via D-Bus. The fake client's reference count will drop but
  // the discovery state won't change since our BluetoothAdapter also just
  // requested it via D-Bus.
  fake_bluetooth_adapter_client_->StopDiscovery(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(5, observer.discovering_changed_count_);
  EXPECT_EQ(10, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());

  // Now end the discovery session. This should change the adapter's discovery
  // state.
  discovery_sessions_[0]->Stop(
    base::Bind(&BluetoothChromeOSTest::Callback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(6, observer.discovering_changed_count_);
  EXPECT_EQ(11, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
}

TEST_F(BluetoothChromeOSTest, InvalidatedDiscoverySessions) {
  GetAdapter();
  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsPowered());
  callback_count_ = 0;

  TestObserver observer(adapter_);

  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());

  // Request device discovery 3 times.
  for (int i = 0; i < 3; i++) {
    adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }
  // Run only once, as there should have been one D-Bus call.
  message_loop_.Run();

  // The observer should have received the discovering changed event exactly
  // once, the success callback should have been called 3 times and the adapter
  // should be discovering.
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(3, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());

  for (int i = 0; i < 3; i++)
    EXPECT_TRUE(discovery_sessions_[i]->IsActive());

  // Stop the timers that the simulation uses
  fake_bluetooth_device_client_->EndDiscoverySimulation(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));

  ASSERT_TRUE(adapter_->IsPowered());
  ASSERT_TRUE(adapter_->IsDiscovering());

  // Delete all but one discovery session.
  discovery_sessions_.pop_back();
  discovery_sessions_.pop_back();
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[0]->IsActive());
  EXPECT_TRUE(adapter_->IsDiscovering());

  // Stop device discovery behind the adapter. The one active discovery session
  // should become inactive, but more importantly, we shouldn't run into any
  // memory errors as the sessions that we explicitly deleted should get
  // cleaned up.
  fake_bluetooth_adapter_client_->StopDiscovery(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_EQ(4, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
}

TEST_F(BluetoothChromeOSTest, QueuedDiscoveryRequests) {
  GetAdapter();

  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsPowered());
  callback_count_ = 0;

  TestObserver observer(adapter_);

  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());

  // Request to start discovery. The call should be pending.
  adapter_->StartDiscoverySession(
    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));
  EXPECT_EQ(0, callback_count_);

  fake_bluetooth_device_client_->EndDiscoverySimulation(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath));

  // The underlying adapter has started discovery, but our call hasn't returned
  // yet.
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_TRUE(discovery_sessions_.empty());

  // Request to start discovery twice. These should get queued and there should
  // be no change in state.
  for (int i = 0; i < 2; i++) {
    adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }
  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  EXPECT_TRUE(discovery_sessions_.empty());

  // Process the pending call. The queued calls should execute and the discovery
  // session reference count should increase.
  message_loop_.Run();
  EXPECT_EQ(3, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());

  // Verify the reference count by removing sessions 3 times. The last request
  // should remain pending.
  for (int i = 0; i < 3; i++) {
    discovery_sessions_[i]->Stop(
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  }
  EXPECT_EQ(5, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
  EXPECT_FALSE(discovery_sessions_[1]->IsActive());
  EXPECT_TRUE(discovery_sessions_[2]->IsActive());

  // Request to stop the session whose call is pending should fail.
  discovery_sessions_[2]->Stop(
    base::Bind(&BluetoothChromeOSTest::Callback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));
  EXPECT_EQ(5, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_TRUE(discovery_sessions_[2]->IsActive());

  // Request to start should get queued.
  adapter_->StartDiscoverySession(
    base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
               base::Unretained(this)),
    base::Bind(&BluetoothChromeOSTest::ErrorCallback,
               base::Unretained(this)));
  EXPECT_EQ(5, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());

  // Run the pending request.
  message_loop_.Run();
  EXPECT_EQ(6, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(3, observer.discovering_changed_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());
  EXPECT_FALSE(discovery_sessions_[2]->IsActive());

  // The queued request to start discovery should have been issued but is still
  // pending. Run the loop and verify.
  message_loop_.Run();
  EXPECT_EQ(7, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(3, observer.discovering_changed_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)4, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[3]->IsActive());
}

TEST_F(BluetoothChromeOSTest, StartDiscoverySession) {
  GetAdapter();

  adapter_->SetPowered(
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(adapter_->IsPowered());
  callback_count_ = 0;

  TestObserver observer(adapter_);

  EXPECT_EQ(0, observer.discovering_changed_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
  EXPECT_TRUE(discovery_sessions_.empty());

  // Request a new discovery session.
  adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)1, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[0]->IsActive());

  // Start another session. A new one should be returned in the callback, which
  // in turn will destroy the previous session. Adapter should still be
  // discovering and the reference count should be 1.
  adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(2, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)2, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[0]->IsActive());

  // Request a new session.
  adapter_->StartDiscoverySession(
      base::Bind(&BluetoothChromeOSTest::DiscoverySessionCallback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(3, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());
  EXPECT_TRUE(discovery_sessions_[1]->IsActive());
  EXPECT_NE(discovery_sessions_[0], discovery_sessions_[1]);

  // Stop the previous discovery session. The session should end but discovery
  // should continue.
  discovery_sessions_[0]->Stop(
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  message_loop_.Run();
  EXPECT_EQ(1, observer.discovering_changed_count_);
  EXPECT_EQ(4, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_TRUE(observer.last_discovering_);
  EXPECT_TRUE(adapter_->IsDiscovering());
  ASSERT_EQ((size_t)3, discovery_sessions_.size());
  EXPECT_FALSE(discovery_sessions_[0]->IsActive());
  EXPECT_TRUE(discovery_sessions_[1]->IsActive());

  // Delete the current active session. Discovery should eventually stop.
  discovery_sessions_.clear();
  while (observer.last_discovering_)
    message_loop_.RunUntilIdle();

  EXPECT_EQ(2, observer.discovering_changed_count_);
  EXPECT_EQ(4, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_FALSE(observer.last_discovering_);
  EXPECT_FALSE(adapter_->IsDiscovering());
}

TEST_F(BluetoothChromeOSTest, DeviceProperties) {
  GetAdapter();

  BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
  ASSERT_EQ(1U, devices.size());
  ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            devices[0]->GetAddress());

  // Verify the other device properties.
  EXPECT_EQ(base::UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
            devices[0]->GetName());
  EXPECT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType());
  EXPECT_TRUE(devices[0]->IsPaired());
  EXPECT_FALSE(devices[0]->IsConnected());
  EXPECT_FALSE(devices[0]->IsConnecting());

  // Non HID devices are always connectable.
  EXPECT_TRUE(devices[0]->IsConnectable());

  BluetoothDevice::UUIDList uuids = devices[0]->GetUUIDs();
  ASSERT_EQ(2U, uuids.size());
  EXPECT_EQ(uuids[0], BluetoothUUID("1800"));
  EXPECT_EQ(uuids[1], BluetoothUUID("1801"));

  EXPECT_EQ(BluetoothDevice::VENDOR_ID_USB, devices[0]->GetVendorIDSource());
  EXPECT_EQ(0x05ac, devices[0]->GetVendorID());
  EXPECT_EQ(0x030d, devices[0]->GetProductID());
  EXPECT_EQ(0x0306, devices[0]->GetDeviceID());
}

TEST_F(BluetoothChromeOSTest, DeviceClassChanged) {
  // Simulate a change of class of a device, as sometimes occurs
  // during discovery.
  GetAdapter();

  BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
  ASSERT_EQ(1U, devices.size());
  ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            devices[0]->GetAddress());
  ASSERT_EQ(BluetoothDevice::DEVICE_COMPUTER, devices[0]->GetDeviceType());

  // Install an observer; expect the DeviceChanged method to be called when
  // we change the class of the device.
  TestObserver observer(adapter_);

  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));

  properties->bluetooth_class.ReplaceValue(0x002580);

  EXPECT_EQ(1, observer.device_changed_count_);
  EXPECT_EQ(devices[0], observer.last_device_);

  EXPECT_EQ(BluetoothDevice::DEVICE_MOUSE, devices[0]->GetDeviceType());
}

TEST_F(BluetoothChromeOSTest, DeviceNameChanged) {
  // Simulate a change of name of a device.
  GetAdapter();

  BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
  ASSERT_EQ(1U, devices.size());
  ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            devices[0]->GetAddress());
  ASSERT_EQ(base::UTF8ToUTF16(FakeBluetoothDeviceClient::kPairedDeviceName),
            devices[0]->GetName());

  // Install an observer; expect the DeviceChanged method to be called when
  // we change the alias of the device.
  TestObserver observer(adapter_);

  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));

  static const std::string new_name("New Device Name");
  properties->alias.ReplaceValue(new_name);

  EXPECT_EQ(1, observer.device_changed_count_);
  EXPECT_EQ(devices[0], observer.last_device_);

  EXPECT_EQ(base::UTF8ToUTF16(new_name), devices[0]->GetName());
}

TEST_F(BluetoothChromeOSTest, DeviceUuidsChanged) {
  // Simulate a change of advertised services of a device.
  GetAdapter();

  BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
  ASSERT_EQ(1U, devices.size());
  ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            devices[0]->GetAddress());

  BluetoothDevice::UUIDList uuids = devices[0]->GetUUIDs();
  ASSERT_EQ(2U, uuids.size());
  ASSERT_EQ(uuids[0], BluetoothUUID("1800"));
  ASSERT_EQ(uuids[1], BluetoothUUID("1801"));

  // Install an observer; expect the DeviceChanged method to be called when
  // we change the class of the device.
  TestObserver observer(adapter_);

  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));

  std::vector<std::string> new_uuids;
  new_uuids.push_back(uuids[0].canonical_value());
  new_uuids.push_back(uuids[1].canonical_value());
  new_uuids.push_back("0000110c-0000-1000-8000-00805f9b34fb");
  new_uuids.push_back("0000110e-0000-1000-8000-00805f9b34fb");
  new_uuids.push_back("0000110a-0000-1000-8000-00805f9b34fb");

  properties->uuids.ReplaceValue(new_uuids);

  EXPECT_EQ(1, observer.device_changed_count_);
  EXPECT_EQ(devices[0], observer.last_device_);

  // Fetching the value should give the new one.
  uuids = devices[0]->GetUUIDs();
  ASSERT_EQ(5U, uuids.size());
  EXPECT_EQ(uuids[0], BluetoothUUID("1800"));
  EXPECT_EQ(uuids[1], BluetoothUUID("1801"));
  EXPECT_EQ(uuids[2], BluetoothUUID("110c"));
  EXPECT_EQ(uuids[3], BluetoothUUID("110e"));
  EXPECT_EQ(uuids[4], BluetoothUUID("110a"));
}

TEST_F(BluetoothChromeOSTest, ForgetDevice) {
  GetAdapter();

  BluetoothAdapter::DeviceList devices = adapter_->GetDevices();
  ASSERT_EQ(1U, devices.size());
  ASSERT_EQ(FakeBluetoothDeviceClient::kPairedDeviceAddress,
            devices[0]->GetAddress());

  std::string address = devices[0]->GetAddress();

  // Install an observer; expect the DeviceRemoved method to be called
  // with the device we remove.
  TestObserver observer(adapter_);

  devices[0]->Forget(
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.device_removed_count_);
  EXPECT_EQ(address, observer.last_device_address_);

  // GetDevices shouldn't return the device either.
  devices = adapter_->GetDevices();
  ASSERT_EQ(0U, devices.size());
}

TEST_F(BluetoothChromeOSTest, ForgetUnpairedDevice) {
  GetAdapter();
  DiscoverDevices();

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConnectUnpairableAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  // Connect the device so it becomes trusted and remembered.
  device->Connect(
      NULL,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  ASSERT_EQ(1, callback_count_);
  ASSERT_EQ(0, error_callback_count_);
  callback_count_ = 0;

  ASSERT_TRUE(device->IsConnected());
  ASSERT_FALSE(device->IsConnecting());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kConnectUnpairablePath));
  ASSERT_TRUE(properties->trusted.value());

  // Install an observer; expect the DeviceRemoved method to be called
  // with the device we remove.
  TestObserver observer(adapter_);

  device->Forget(
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.device_removed_count_);
  EXPECT_EQ(FakeBluetoothDeviceClient::kConnectUnpairableAddress,
            observer.last_device_address_);

  // GetDevices shouldn't return the device either.
  device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConnectUnpairableAddress);
  EXPECT_FALSE(device != NULL);
}

TEST_F(BluetoothChromeOSTest, ConnectPairedDevice) {
  GetAdapter();

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kPairedDeviceAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_TRUE(device->IsPaired());

  TestObserver observer(adapter_);

  // Connect without a pairing delegate; since the device is already Paired
  // this should succeed and the device should become connected.
  device->Connect(
      NULL,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one for connected and one for for trusted
  // after connecting.
  EXPECT_EQ(4, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
}

TEST_F(BluetoothChromeOSTest, ConnectUnpairableDevice) {
  GetAdapter();
  DiscoverDevices();

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConnectUnpairableAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  // Connect without a pairing delegate; since the device does not require
  // pairing, this should succeed and the device should become connected.
  device->Connect(
      NULL,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one for connected, one for for trusted after
  // connection, and one for the reconnect mode (IsConnectable).
  EXPECT_EQ(5, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kConnectUnpairablePath));
  EXPECT_TRUE(properties->trusted.value());

  // Verify is a HID device and is not connectable.
  BluetoothDevice::UUIDList uuids = device->GetUUIDs();
  ASSERT_EQ(1U, uuids.size());
  EXPECT_EQ(uuids[0], BluetoothUUID("1124"));
  EXPECT_FALSE(device->IsConnectable());
}

TEST_F(BluetoothChromeOSTest, ConnectConnectedDevice) {
  GetAdapter();

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kPairedDeviceAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_TRUE(device->IsPaired());

  device->Connect(
      NULL,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  ASSERT_EQ(1, callback_count_);
  ASSERT_EQ(0, error_callback_count_);
  callback_count_ = 0;

  ASSERT_TRUE(device->IsConnected());

  // Connect again; since the device is already Connected, this shouldn't do
  // anything to initiate the connection.
  TestObserver observer(adapter_);

  device->Connect(
      NULL,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // The observer will be called because Connecting will toggle true and false,
  // and the trusted property will be updated to true.
  EXPECT_EQ(3, observer.device_changed_count_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
}

TEST_F(BluetoothChromeOSTest, ConnectDeviceFails) {
  GetAdapter();
  DiscoverDevices();

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLegacyAutopairAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  // Connect without a pairing delegate; since the device requires pairing,
  // this should fail with an error.
  device->Connect(
      NULL,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_);

  EXPECT_EQ(2, observer.device_changed_count_);

  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
}

TEST_F(BluetoothChromeOSTest, DisconnectDevice) {
  GetAdapter();

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kPairedDeviceAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_TRUE(device->IsPaired());

  device->Connect(
      NULL,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  ASSERT_EQ(1, callback_count_);
  ASSERT_EQ(0, error_callback_count_);
  callback_count_ = 0;

  ASSERT_TRUE(device->IsConnected());
  ASSERT_FALSE(device->IsConnecting());

  // Disconnect the device, we should see the observer method fire and the
  // device get dropped.
  TestObserver observer(adapter_);

  device->Disconnect(
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  EXPECT_EQ(1, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_FALSE(device->IsConnected());
}

TEST_F(BluetoothChromeOSTest, DisconnectUnconnectedDevice) {
  GetAdapter();

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kPairedDeviceAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_TRUE(device->IsPaired());
  ASSERT_FALSE(device->IsConnected());

  // Disconnect the device, we should see the observer method fire and the
  // device get dropped.
  TestObserver observer(adapter_);

  device->Disconnect(
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);

  EXPECT_EQ(0, observer.device_changed_count_);

  EXPECT_FALSE(device->IsConnected());
}

TEST_F(BluetoothChromeOSTest, PairLegacyAutopair) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // The Legacy Autopair device requires no PIN or Passkey to pair because
  // the daemon provides 0000 to the device for us.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLegacyAutopairAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, pairing_delegate.call_count_);
  EXPECT_TRUE(device->IsConnecting());

  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one change for connected, one for paired,
  // two for trusted (after pairing and connection), and one for the reconnect
  // mode (IsConnectable).
  EXPECT_EQ(7, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Verify is a HID device and is connectable.
  BluetoothDevice::UUIDList uuids = device->GetUUIDs();
  ASSERT_EQ(1U, uuids.size());
  EXPECT_EQ(uuids[0], BluetoothUUID("1124"));
  EXPECT_TRUE(device->IsConnectable());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kLegacyAutopairPath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairDisplayPinCode) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Requires that we display a randomly generated PIN on the screen.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kDisplayPinCodeAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.display_pincode_count_);
  EXPECT_EQ("123456", pairing_delegate.last_pincode_);
  EXPECT_TRUE(device->IsConnecting());

  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one change for connected, one for paired,
  // two for trusted (after pairing and connection), and one for the reconnect
  // mode (IsConnectable).
  EXPECT_EQ(7, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Verify is a HID device and is connectable.
  BluetoothDevice::UUIDList uuids = device->GetUUIDs();
  ASSERT_EQ(1U, uuids.size());
  EXPECT_EQ(uuids[0], BluetoothUUID("1124"));
  EXPECT_TRUE(device->IsConnectable());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kDisplayPinCodePath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairDisplayPasskey) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Requires that we display a randomly generated Passkey on the screen,
  // and notifies us as it's typed in.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kDisplayPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  // One call for DisplayPasskey() and one for KeysEntered().
  EXPECT_EQ(2, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.display_passkey_count_);
  EXPECT_EQ(123456U, pairing_delegate.last_passkey_);
  EXPECT_EQ(1, pairing_delegate.keys_entered_count_);
  EXPECT_EQ(0U, pairing_delegate.last_entered_);

  EXPECT_TRUE(device->IsConnecting());

  // One call to KeysEntered() for each key, including [enter].
  for(int i = 1; i <= 7; ++i) {
    message_loop_.Run();

    EXPECT_EQ(2 + i, pairing_delegate.call_count_);
    EXPECT_EQ(1 + i, pairing_delegate.keys_entered_count_);
    EXPECT_EQ(static_cast<uint32_t>(i), pairing_delegate.last_entered_);
  }

  message_loop_.Run();

  // 8 KeysEntered notifications (0 to 7, inclusive) and one aditional call for
  // DisplayPasskey().
  EXPECT_EQ(9, pairing_delegate.call_count_);
  EXPECT_EQ(8, pairing_delegate.keys_entered_count_);
  EXPECT_EQ(7U, pairing_delegate.last_entered_);

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one change for connected, one for paired,
  // two for trusted (after pairing and connection), and one for the reconnect
  // mode (IsConnectable).
  EXPECT_EQ(7, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Verify is a HID device.
  BluetoothDevice::UUIDList uuids = device->GetUUIDs();
  ASSERT_EQ(1U, uuids.size());
  EXPECT_EQ(uuids[0], BluetoothUUID("1124"));

  // And usually not connectable.
  EXPECT_FALSE(device->IsConnectable());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kDisplayPasskeyPath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairRequestPinCode) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Requires that the user enters a PIN for them.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Set the PIN.
  device->SetPinCode("1234");
  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one change for connected, one for paired and
  // two for trusted (after pairing and connection).
  EXPECT_EQ(6, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Verify is not a HID device.
  BluetoothDevice::UUIDList uuids = device->GetUUIDs();
  ASSERT_EQ(0U, uuids.size());

  // Non HID devices are always connectable.
  EXPECT_TRUE(device->IsConnectable());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairConfirmPasskey) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Requests that we confirm a displayed passkey.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
  EXPECT_EQ(123456U, pairing_delegate.last_passkey_);
  EXPECT_TRUE(device->IsConnecting());

  // Confirm the passkey.
  device->ConfirmPairing();
  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one change for connected, one for paired and
  // two for trusted (after pairing and connection).
  EXPECT_EQ(6, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Non HID devices are always connectable.
  EXPECT_TRUE(device->IsConnectable());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairRequestPasskey) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Requires that the user enters a Passkey, this would be some kind of
  // device that has a display, but doesn't use "just works" - maybe a car?
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Set the Passkey.
  device->SetPasskey(1234);
  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one change for connected, one for paired and
  // two for trusted (after pairing and connection).
  EXPECT_EQ(6, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Non HID devices are always connectable.
  EXPECT_TRUE(device->IsConnectable());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairJustWorks) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Uses just-works pairing, since this is an outgoing pairing, no delegate
  // interaction is required.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kJustWorksAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, pairing_delegate.call_count_);

  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // Two changes for connecting, one change for connected, one for paired and
  // two for trusted (after pairing and connection).
  EXPECT_EQ(6, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Non HID devices are always connectable.
  EXPECT_TRUE(device->IsConnectable());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairUnpairableDeviceFails) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevice(FakeBluetoothDeviceClient::kUnconnectableDeviceAddress);

  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kUnpairableDeviceAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, pairing_delegate.call_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Run the loop to get the error..
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);

  EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_);

  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingFails) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevice(FakeBluetoothDeviceClient::kVanishingDeviceAddress);

  // The vanishing device times out during pairing
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kVanishingDeviceAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, pairing_delegate.call_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Run the loop to get the error..
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);

  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_TIMEOUT, last_connect_error_);

  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingFailsAtConnection) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Everything seems to go according to plan with the unconnectable device;
  // it pairs, but then you can't make connections to it after.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kUnconnectableDeviceAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, pairing_delegate.call_count_);
  EXPECT_TRUE(device->IsConnecting());

  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_FAILED, last_connect_error_);

  // Two changes for connecting, one for paired and one for trusted after
  // pairing. The device should not be connected.
  EXPECT_EQ(4, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());

  EXPECT_TRUE(device->IsPaired());

  // Make sure the trusted property has been set to true still (since pairing
  // worked).
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(
              FakeBluetoothDeviceClient::kUnconnectableDevicePath));
  EXPECT_TRUE(properties->trusted.value());
}

TEST_F(BluetoothChromeOSTest, PairingRejectedAtPinCode) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Reject the pairing after we receive a request for the PIN code.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Reject the pairing.
  device->RejectPairing();
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_);

  // Should be no changes except connecting going true and false.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingCancelledAtPinCode) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Cancel the pairing after we receive a request for the PIN code.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_pincode_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Cancel the pairing.
  device->CancelPairing();
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);

  // Should be no changes except connecting going true and false.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingRejectedAtPasskey) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Reject the pairing after we receive a request for the passkey.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Reject the pairing.
  device->RejectPairing();
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_);

  // Should be no changes except connecting going true and false.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingCancelledAtPasskey) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Cancel the pairing after we receive a request for the passkey.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_passkey_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Cancel the pairing.
  device->CancelPairing();
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);

  // Should be no changes except connecting going true and false.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingRejectedAtConfirmation) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Reject the pairing after we receive a request for passkey confirmation.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Reject the pairing.
  device->RejectPairing();
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_REJECTED, last_connect_error_);

  // Should be no changes except connecting going true and false.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingCancelledAtConfirmation) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Cancel the pairing after we receive a request for the passkey.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Cancel the pairing.
  device->CancelPairing();
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);

  // Should be no changes except connecting going true and false.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, PairingCancelledInFlight) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();
  DiscoverDevices();

  // Cancel the pairing while we're waiting for the remote host.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kLegacyAutopairAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  TestPairingDelegate pairing_delegate;
  device->Connect(
      &pairing_delegate,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::ConnectErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(0, pairing_delegate.call_count_);
  EXPECT_TRUE(device->IsConnecting());

  // Cancel the pairing.
  device->CancelPairing();
  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(BluetoothDevice::ERROR_AUTH_CANCELED, last_connect_error_);

  // Should be no changes except connecting going true and false.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_FALSE(device->IsConnected());
  EXPECT_FALSE(device->IsConnecting());
  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, IncomingPairRequestPinCode) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  TestPairingDelegate pairing_delegate;
  adapter_->AddPairingDelegate(
      &pairing_delegate,
      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);

  // Requires that we provide a PIN code.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_pincode_count_);

  // Set the PIN.
  device->SetPinCode("1234");
  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // One change for paired, and one for trusted.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsPaired());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
  ASSERT_TRUE(properties->trusted.value());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, IncomingPairConfirmPasskey) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  TestPairingDelegate pairing_delegate;
  adapter_->AddPairingDelegate(
      &pairing_delegate,
      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);

  // Requests that we confirm a displayed passkey.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.confirm_passkey_count_);
  EXPECT_EQ(123456U, pairing_delegate.last_passkey_);

  // Confirm the passkey.
  device->ConfirmPairing();
  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // One change for paired, and one for trusted.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsPaired());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
  ASSERT_TRUE(properties->trusted.value());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, IncomingPairRequestPasskey) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  TestPairingDelegate pairing_delegate;
  adapter_->AddPairingDelegate(
      &pairing_delegate,
      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);

  // Requests that we provide a Passkey.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_passkey_count_);

  // Set the Passkey.
  device->SetPasskey(1234);
  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // One change for paired, and one for trusted.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsPaired());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
  ASSERT_TRUE(properties->trusted.value());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, IncomingPairJustWorks) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  TestPairingDelegate pairing_delegate;
  adapter_->AddPairingDelegate(
      &pairing_delegate,
      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);

  // Uses just-works pairing so, sinec this an incoming pairing, require
  // authorization from the user.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kJustWorksAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.authorize_pairing_count_);

  // Confirm the pairing.
  device->ConfirmPairing();
  message_loop_.Run();

  EXPECT_EQ(1, callback_count_);
  EXPECT_EQ(0, error_callback_count_);

  // One change for paired, and one for trusted.
  EXPECT_EQ(2, observer.device_changed_count_);
  EXPECT_EQ(device, observer.last_device_);

  EXPECT_TRUE(device->IsPaired());

  // Make sure the trusted property has been set to true.
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
  ASSERT_TRUE(properties->trusted.value());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, IncomingPairRequestPinCodeWithoutDelegate) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  // Requires that we provide a PIN Code, without a pairing delegate,
  // that will be rejected.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPinCodeAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPinCodePath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);

  // No changes should be observer.
  EXPECT_EQ(0, observer.device_changed_count_);

  EXPECT_FALSE(device->IsPaired());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, IncomingPairConfirmPasskeyWithoutDelegate) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  // Requests that we confirm a displayed passkey, without a pairing delegate,
  // that will be rejected.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kConfirmPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kConfirmPasskeyPath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);

  // No changes should be observer.
  EXPECT_EQ(0, observer.device_changed_count_);

  EXPECT_FALSE(device->IsPaired());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, IncomingPairRequestPasskeyWithoutDelegate) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  // Requests that we provide a displayed passkey, without a pairing delegate,
  // that will be rejected.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);

  // No changes should be observer.
  EXPECT_EQ(0, observer.device_changed_count_);

  EXPECT_FALSE(device->IsPaired());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, IncomingPairJustWorksWithoutDelegate) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  // Uses just-works pairing and thus requires authorization for incoming
  // pairings, without a pairing delegate, that will be rejected.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kJustWorksAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kJustWorksPath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  message_loop_.Run();

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(1, error_callback_count_);
  EXPECT_EQ(bluetooth_device::kErrorAuthenticationRejected, last_client_error_);

  // No changes should be observer.
  EXPECT_EQ(0, observer.device_changed_count_);

  EXPECT_FALSE(device->IsPaired());

  // No pairing context should remain on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);
}

TEST_F(BluetoothChromeOSTest, RemovePairingDelegateDuringPairing) {
  fake_bluetooth_device_client_->SetSimulationIntervalMs(10);

  GetAdapter();

  TestPairingDelegate pairing_delegate;
  adapter_->AddPairingDelegate(
      &pairing_delegate,
      BluetoothAdapter::PAIRING_DELEGATE_PRIORITY_HIGH);

  // Requests that we provide a Passkey.
  fake_bluetooth_device_client_->CreateDevice(
      dbus::ObjectPath(FakeBluetoothAdapterClient::kAdapterPath),
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath));
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kRequestPasskeyAddress);
  ASSERT_TRUE(device != NULL);
  ASSERT_FALSE(device->IsPaired());

  TestObserver observer(adapter_);

  fake_bluetooth_device_client_->SimulatePairing(
      dbus::ObjectPath(FakeBluetoothDeviceClient::kRequestPasskeyPath),
      true,
      base::Bind(&BluetoothChromeOSTest::Callback,
                 base::Unretained(this)),
      base::Bind(&BluetoothChromeOSTest::DBusErrorCallback,
                 base::Unretained(this)));

  EXPECT_EQ(1, pairing_delegate.call_count_);
  EXPECT_EQ(1, pairing_delegate.request_passkey_count_);

  // A pairing context should now be set on the device.
  BluetoothDeviceChromeOS* device_chromeos =
      static_cast<BluetoothDeviceChromeOS*>(device);
  ASSERT_TRUE(device_chromeos->GetPairing() != NULL);

  // Removing the pairing delegate should remove that pairing context.
  adapter_->RemovePairingDelegate(&pairing_delegate);

  EXPECT_TRUE(device_chromeos->GetPairing() == NULL);

  // Set the Passkey, this should now have no effect since the pairing has
  // been, in-effect, cancelled
  device->SetPasskey(1234);

  EXPECT_EQ(0, callback_count_);
  EXPECT_EQ(0, error_callback_count_);
  EXPECT_EQ(0, observer.device_changed_count_);

  EXPECT_FALSE(device->IsPaired());
}

TEST_F(BluetoothChromeOSTest, DeviceId) {
  GetAdapter();

  // Use the built-in paired device for this test, grab its Properties
  // structure so we can adjust the underlying modalias property.
  BluetoothDevice* device = adapter_->GetDevice(
      FakeBluetoothDeviceClient::kPairedDeviceAddress);
  FakeBluetoothDeviceClient::Properties* properties =
      fake_bluetooth_device_client_->GetProperties(
          dbus::ObjectPath(FakeBluetoothDeviceClient::kPairedDevicePath));

  ASSERT_TRUE(device != NULL);
  ASSERT_TRUE(properties != NULL);

  // Valid USB IF-assigned identifier.
  ASSERT_EQ("usb:v05ACp030Dd0306", properties->modalias.value());

  EXPECT_EQ(BluetoothDevice::VENDOR_ID_USB, device->GetVendorIDSource());
  EXPECT_EQ(0x05ac, device->GetVendorID());
  EXPECT_EQ(0x030d, device->GetProductID());
  EXPECT_EQ(0x0306, device->GetDeviceID());

  // Valid Bluetooth SIG-assigned identifier.
  properties->modalias.ReplaceValue("bluetooth:v00E0p2400d0400");

  EXPECT_EQ(BluetoothDevice::VENDOR_ID_BLUETOOTH, device->GetVendorIDSource());
  EXPECT_EQ(0x00e0, device->GetVendorID());
  EXPECT_EQ(0x2400, device->GetProductID());
  EXPECT_EQ(0x0400, device->GetDeviceID());

  // Invalid USB IF-assigned identifier.
  properties->modalias.ReplaceValue("usb:x00E0p2400d0400");

  EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
  EXPECT_EQ(0, device->GetVendorID());
  EXPECT_EQ(0, device->GetProductID());
  EXPECT_EQ(0, device->GetDeviceID());

  // Invalid Bluetooth SIG-assigned identifier.
  properties->modalias.ReplaceValue("bluetooth:x00E0p2400d0400");

  EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
  EXPECT_EQ(0, device->GetVendorID());
  EXPECT_EQ(0, device->GetProductID());
  EXPECT_EQ(0, device->GetDeviceID());

  // Unknown vendor specification identifier.
  properties->modalias.ReplaceValue("chrome:v00E0p2400d0400");

  EXPECT_EQ(BluetoothDevice::VENDOR_ID_UNKNOWN, device->GetVendorIDSource());
  EXPECT_EQ(0, device->GetVendorID());
  EXPECT_EQ(0, device->GetProductID());
  EXPECT_EQ(0, device->GetDeviceID());
}

}  // namespace chromeos

/* [<][>][^][v][top][bottom][index][help] */