Commit 3833c387 authored by ortuno's avatar ortuno Committed by Commit bot

bluetooth: Implement simulateCentral

FakeCentral allows clients to simulate events that a device
in the Central/Observer role would receive as well as monitor the operations
performed by the device in the Central/Observer role.

BUG=569709

Review-Url: https://codereview.chromium.org/2853433002
Cr-Commit-Position: refs/heads/master@{#469289}
parent 265b2b33
...@@ -45,6 +45,8 @@ source_set("fake_bluetooth") { ...@@ -45,6 +45,8 @@ source_set("fake_bluetooth") {
sources = [ sources = [
"test/fake_bluetooth.cc", "test/fake_bluetooth.cc",
"test/fake_bluetooth.h", "test/fake_bluetooth.h",
"test/fake_central.cc",
"test/fake_central.h",
] ]
deps = [ deps = [
......
...@@ -9,6 +9,13 @@ module bluetooth.mojom; ...@@ -9,6 +9,13 @@ module bluetooth.mojom;
// devices, simulating GATT attributes and its descendants, and simulating // devices, simulating GATT attributes and its descendants, and simulating
// success and error responses. // success and error responses.
// Indicates the various states of Central.
enum CentralState {
ABSENT,
POWERED_ON,
POWERED_OFF,
};
// FakeBluetooth allows clients to control the global Bluetooth state. // FakeBluetooth allows clients to control the global Bluetooth state.
interface FakeBluetooth { interface FakeBluetooth {
// Set it to indicate whether the platform supports BLE. For example, Windows // Set it to indicate whether the platform supports BLE. For example, Windows
...@@ -16,4 +23,19 @@ interface FakeBluetooth { ...@@ -16,4 +23,19 @@ interface FakeBluetooth {
// 10 is a platform that does support LE, even if there is no Bluetooth radio // 10 is a platform that does support LE, even if there is no Bluetooth radio
// available. // available.
SetLESupported(bool available) => (); SetLESupported(bool available) => ();
// Initializes a fake Central with |state| as the initial state.
SimulateCentral(CentralState state) => (FakeCentral fake_central);
};
// FakeCentral allows clients to simulate events that a device in the
// Central/Observer role would receive as well as monitor the operations
// performed by the device in the Central/Observer role.
//
// A "Central" interface would allow its clients to receive advertising events
// and initiate connections to peripherals i.e. operations of two roles
// defined by the Bluetooth Spec: Observer and Central.
// See Bluetooth 4.2 Vol 3 Part C 2.2.2 "Roles when Operating over an
// LE Physical Transport".
interface FakeCentral {
}; };
...@@ -33,4 +33,13 @@ void FakeBluetooth::SetLESupported(bool supported, ...@@ -33,4 +33,13 @@ void FakeBluetooth::SetLESupported(bool supported,
callback.Run(); callback.Run();
} }
void FakeBluetooth::SimulateCentral(mojom::CentralState state,
const SimulateCentralCallback& callback) {
mojom::FakeCentralPtr fake_central_ptr;
fake_central_ = base::MakeShared<FakeCentral>(
state, mojo::MakeRequest(&fake_central_ptr));
device::BluetoothAdapterFactory::SetAdapterForTesting(fake_central_);
callback.Run(std::move(fake_central_ptr));
}
} // namespace bluetooth } // namespace bluetooth
...@@ -4,10 +4,12 @@ ...@@ -4,10 +4,12 @@
#ifndef DEVICE_BLUETOOTH_TEST_FAKE_BLUETOOTH_H_ #ifndef DEVICE_BLUETOOTH_TEST_FAKE_BLUETOOTH_H_
#define DEVICE_BLUETOOTH_TEST_FAKE_BLUETOOTH_H_ #define DEVICE_BLUETOOTH_TEST_FAKE_BLUETOOTH_H_
#include <memory>
#include "base/compiler_specific.h" #include "base/compiler_specific.h"
#include "device/bluetooth/bluetooth_adapter_factory.h" #include "device/bluetooth/bluetooth_adapter_factory.h"
#include "device/bluetooth/public/interfaces/test/fake_bluetooth.mojom.h" #include "device/bluetooth/public/interfaces/test/fake_bluetooth.mojom.h"
#include "mojo/public/cpp/bindings/binding.h" #include "device/bluetooth/test/fake_central.h"
namespace service_manager { namespace service_manager {
struct BindSourceInfo; struct BindSourceInfo;
...@@ -17,7 +19,8 @@ namespace bluetooth { ...@@ -17,7 +19,8 @@ namespace bluetooth {
// Implementation of FakeBluetooth in // Implementation of FakeBluetooth in
// src/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom. // src/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom.
// Implemented on top of the C++ device/bluetooth API. // Implemented on top of the C++ device/bluetooth API, mainly
// device/bluetooth/bluetooth_adapter_factory.h.
class FakeBluetooth : NON_EXPORTED_BASE(public mojom::FakeBluetooth) { class FakeBluetooth : NON_EXPORTED_BASE(public mojom::FakeBluetooth) {
public: public:
FakeBluetooth(); FakeBluetooth();
...@@ -28,10 +31,13 @@ class FakeBluetooth : NON_EXPORTED_BASE(public mojom::FakeBluetooth) { ...@@ -28,10 +31,13 @@ class FakeBluetooth : NON_EXPORTED_BASE(public mojom::FakeBluetooth) {
void SetLESupported(bool available, void SetLESupported(bool available,
const SetLESupportedCallback& callback) override; const SetLESupportedCallback& callback) override;
void SimulateCentral(mojom::CentralState state,
const SimulateCentralCallback& callback) override;
private: private:
std::unique_ptr<device::BluetoothAdapterFactory::GlobalValuesForTesting> std::unique_ptr<device::BluetoothAdapterFactory::GlobalValuesForTesting>
global_factory_values_; global_factory_values_;
scoped_refptr<FakeCentral> fake_central_;
}; };
} // namespace bluetooth } // namespace bluetooth
......
// Copyright 2017 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 "device/bluetooth/test/fake_central.h"
#include <memory>
#include <string>
#include <utility>
#include "device/bluetooth/bluetooth_discovery_filter.h"
#include "device/bluetooth/public/interfaces/test/fake_bluetooth.mojom.h"
namespace bluetooth {
FakeCentral::FakeCentral(mojom::CentralState state,
mojom::FakeCentralRequest request)
: state_(state), binding_(this, std::move(request)) {}
FakeCentral::~FakeCentral() {}
std::string FakeCentral::GetAddress() const {
NOTREACHED();
return std::string();
}
std::string FakeCentral::GetName() const {
NOTREACHED();
return std::string();
}
void FakeCentral::SetName(const std::string& name,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTREACHED();
}
bool FakeCentral::IsInitialized() const {
return true;
}
bool FakeCentral::IsPresent() const {
switch (state_) {
case mojom::CentralState::ABSENT:
return false;
case mojom::CentralState::POWERED_OFF:
case mojom::CentralState::POWERED_ON:
return true;
}
NOTREACHED();
return false;
}
bool FakeCentral::IsPowered() const {
switch (state_) {
case mojom::CentralState::POWERED_OFF:
return false;
case mojom::CentralState::POWERED_ON:
return true;
case mojom::CentralState::ABSENT:
// Clients shouldn't call IsPowered() when the adapter is not present.
NOTREACHED();
return false;
}
NOTREACHED();
return false;
}
void FakeCentral::SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTREACHED();
}
bool FakeCentral::IsDiscoverable() const {
NOTREACHED();
return false;
}
void FakeCentral::SetDiscoverable(bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) {
NOTREACHED();
}
bool FakeCentral::IsDiscovering() const {
NOTREACHED();
return false;
}
FakeCentral::UUIDList FakeCentral::GetUUIDs() const {
NOTREACHED();
return UUIDList();
}
void FakeCentral::CreateRfcommService(
const device::BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) {
NOTREACHED();
}
void FakeCentral::CreateL2capService(
const device::BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) {
NOTREACHED();
}
void FakeCentral::RegisterAdvertisement(
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement_data,
const CreateAdvertisementCallback& callback,
const AdvertisementErrorCallback& error_callback) {
NOTREACHED();
}
#if defined(OS_CHROMEOS) || defined(OS_LINUX)
void FakeCentral::SetAdvertisingInterval(
const base::TimeDelta& min,
const base::TimeDelta& max,
const base::Closure& callback,
const AdvertisementErrorCallback& error_callback) {
NOTREACHED();
}
#endif
device::BluetoothLocalGattService* FakeCentral::GetGattService(
const std::string& identifier) const {
NOTREACHED();
return nullptr;
}
void FakeCentral::AddDiscoverySession(
device::BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) {
NOTREACHED();
}
void FakeCentral::RemoveDiscoverySession(
device::BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) {
NOTREACHED();
}
void FakeCentral::SetDiscoveryFilter(
std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) {
NOTREACHED();
}
void FakeCentral::RemovePairingDelegateInternal(
device::BluetoothDevice::PairingDelegate* pairing_delegate) {
NOTREACHED();
}
} // namespace bluetooth
// Copyright 2017 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.
#ifndef DEVICE_BLUETOOTH_TEST_FAKE_CENTRAL_H_
#define DEVICE_BLUETOOTH_TEST_FAKE_CENTRAL_H_
#include <memory>
#include <string>
#include "base/compiler_specific.h"
#include "device/bluetooth/bluetooth_adapter.h"
#include "device/bluetooth/public/interfaces/test/fake_bluetooth.mojom.h"
#include "mojo/public/cpp/bindings/binding.h"
namespace bluetooth {
// Implementation of FakeCentral in
// src/device/bluetooth/public/interfaces/test/fake_bluetooth.mojom.
// Implemented on top of the C++ device/bluetooth API, mainly
// device/bluetooth/bluetooth_adapter.h.
class FakeCentral : NON_EXPORTED_BASE(public mojom::FakeCentral),
public device::BluetoothAdapter {
public:
FakeCentral(mojom::CentralState state, mojom::FakeCentralRequest request);
// BluetoothAdapter overrides:
std::string GetAddress() const override;
std::string GetName() const override;
void SetName(const std::string& name,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
bool IsInitialized() const override;
bool IsPresent() const override;
bool IsPowered() const override;
void SetPowered(bool powered,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
bool IsDiscoverable() const override;
void SetDiscoverable(bool discoverable,
const base::Closure& callback,
const ErrorCallback& error_callback) override;
bool IsDiscovering() const override;
UUIDList GetUUIDs() const override;
void CreateRfcommService(
const device::BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) override;
void CreateL2capService(
const device::BluetoothUUID& uuid,
const ServiceOptions& options,
const CreateServiceCallback& callback,
const CreateServiceErrorCallback& error_callback) override;
void RegisterAdvertisement(
std::unique_ptr<device::BluetoothAdvertisement::Data> advertisement_data,
const CreateAdvertisementCallback& callback,
const AdvertisementErrorCallback& error_callback) override;
#if defined(OS_CHROMEOS) || defined(OS_LINUX)
void SetAdvertisingInterval(
const base::TimeDelta& min,
const base::TimeDelta& max,
const base::Closure& callback,
const AdvertisementErrorCallback& error_callback) override;
#endif
device::BluetoothLocalGattService* GetGattService(
const std::string& identifier) const override;
void AddDiscoverySession(
device::BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) override;
void RemoveDiscoverySession(
device::BluetoothDiscoveryFilter* discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) override;
void SetDiscoveryFilter(
std::unique_ptr<device::BluetoothDiscoveryFilter> discovery_filter,
const base::Closure& callback,
const DiscoverySessionErrorCallback& error_callback) override;
void RemovePairingDelegateInternal(
device::BluetoothDevice::PairingDelegate* pairing_delegate) override;
private:
~FakeCentral() override;
mojom::CentralState state_;
mojo::Binding<mojom::FakeCentral> binding_;
};
} // namespace bluetooth
#endif // DEVICE_BLUETOOTH_TEST_FAKE_CENTRAL_H_
...@@ -2,13 +2,15 @@ ...@@ -2,13 +2,15 @@
<script src="../../resources/testharness.js"></script> <script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script> <script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/bluetooth/bluetooth-helpers.js"></script> <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
<script src="../../resources/mojo-helpers.js"></script>
<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
<script> <script>
'use strict'; 'use strict';
promise_test(() => { promise_test(() => {
return setBluetoothFakeAdapter('NotPresentAdapter') return navigator.bluetooth.test.simulateCentral({state: 'absent'})
.then(() => assert_promise_rejects_with_message( .then(() => assert_promise_rejects_with_message(
requestDeviceWithKeyDown({filters: [{services: ['generic_access']}]}), requestDeviceWithKeyDown({filters: [{services: ['generic_access']}]}),
new DOMException('Bluetooth adapter not available.', 'NotFoundError'), new DOMException('Bluetooth adapter not available.', 'NotFoundError'),
'Bluetooth adapter is not present.')); 'Bluetooth adapter is not present.'));
}, 'Reject with NotFoundError if the adapter is not present.'); }, 'Reject with NotFoundError if there is no BT radio present.');
</script> </script>
...@@ -2,12 +2,14 @@ ...@@ -2,12 +2,14 @@
<script src="../../resources/testharness.js"></script> <script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script> <script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/bluetooth/bluetooth-helpers.js"></script> <script src="../../resources/bluetooth/bluetooth-helpers.js"></script>
<script src="../../resources/mojo-helpers.js"></script>
<script src="../../resources/bluetooth/web-bluetooth-test.js"></script>
<script> <script>
'use strict'; 'use strict';
promise_test(() => { promise_test(() => {
setBluetoothManualChooser(true); setBluetoothManualChooser(true);
let requestDevicePromise = let requestDevicePromise =
setBluetoothFakeAdapter('NotPoweredAdapter') navigator.bluetooth.test.simulateCentral({state: 'powered-off'})
.then(() => requestDeviceWithKeyDown({ .then(() => requestDeviceWithKeyDown({
filters: [{services: ['generic_access']}]})); filters: [{services: ['generic_access']}]}));
return getBluetoothManualChooserEvents(2) return getBluetoothManualChooserEvents(2)
...@@ -23,5 +25,5 @@ promise_test(() => { ...@@ -23,5 +25,5 @@ promise_test(() => {
'NotFoundError'), 'NotFoundError'),
'Bluetooth adapter is not powered.'); 'Bluetooth adapter is not powered.');
}); });
}, 'Reject with NotFoundError if the adapter is off.'); }, 'Reject with NotFoundError if the BT radio is off.');
</script> </script>
...@@ -15,7 +15,14 @@ ...@@ -15,7 +15,14 @@
'device/bluetooth/public/interfaces/test/fake_bluetooth.mojom' 'device/bluetooth/public/interfaces/test/fake_bluetooth.mojom'
]); ]);
[mojo_.bindings, mojo_.FakeBluetooth] = mojo_.modules; [mojo_.bindings, {
CentralState: mojo_.CentralState,
FakeBluetooth: mojo_.FakeBluetooth,
FakeBluetoothPtr: mojo_.FakeBluetoothPtr,
FakeCentral: mojo_.FakeCentral,
FakeCentralPtr: mojo_.FakeCentralPtr,
}] = mojo_.modules;
return mojo_; return mojo_;
} }
...@@ -43,6 +50,53 @@ ...@@ -43,6 +50,53 @@
await (await this.getFakeBluetoothInterface_()).setLESupported(supported); await (await this.getFakeBluetoothInterface_()).setLESupported(supported);
} }
// Returns a promise that resolves with a FakeCentral that clients can use
// to simulate events that a device in the Central/Observer role would
// receive as well as monitor the operations performed by the device in the
// Central/Observer role.
// Calls sets LE as supported.
//
// A "Central" object would allow its clients to receive advertising events
// and initiate connections to peripherals i.e. operations of two roles
// defined by the Bluetooth Spec: Observer and Central.
// See Bluetooth 4.2 Vol 3 Part C 2.2.2 "Roles when Operating over an
// LE Physical Transport".
async simulateCentral({state}) {
// Call setBluetoothFakeAdapter() to clean up any fake adapters left over
// by legacy tests.
// Legacy tests that use setBluetoothFakeAdapter() sometimes fail to clean
// their fake adapter. This is not a problem for these tests because the
// next setBluetoothFakeAdapter() will clean it up anyway but it is a
// problem for the new tests that do not use setBluetoothFakeAdapter().
// TODO(crbug.com/569709): Remove once setBluetoothFakeAdapter is no
// longer used.
await setBluetoothFakeAdapter('');
await this.setLESupported(true);
let mojo = await loadFakeBluetoothInterfaces();
let mojo_manager_state;
switch (state) {
case 'absent':
mojo_manager_state = mojo.CentralState.ABSENT;
break;
case 'powered-off':
mojo_manager_state = mojo.CentralState.POWERED_OFF;
break;
case 'powered-on':
mojo_manager_state = mojo.CentralState.POWERED_ON;
break;
default:
throw `Unsupported value ${state} for state.`;
}
let {fake_central:fake_central_ptr} =
await (await this.getFakeBluetoothInterface_()).simulateCentral(
mojo_manager_state);
return new FakeCentral(fake_central_ptr);
}
async getFakeBluetoothInterface_() { async getFakeBluetoothInterface_() {
if (typeof this.fake_bluetooth_ptr_ !== 'undefined') { if (typeof this.fake_bluetooth_ptr_ !== 'undefined') {
return this.fake_bluetooth_ptr_; return this.fake_bluetooth_ptr_;
...@@ -50,13 +104,21 @@ ...@@ -50,13 +104,21 @@
let mojo = await loadFakeBluetoothInterfaces(); let mojo = await loadFakeBluetoothInterfaces();
this.fake_bluetooth_ptr_ = new mojo.FakeBluetooth.FakeBluetoothPtr( this.fake_bluetooth_ptr_ = new mojo.FakeBluetoothPtr(
mojo.interfaces.getInterface( mojo.interfaces.getInterface(mojo.FakeBluetooth.name));
mojo.FakeBluetooth.FakeBluetooth.name));
return this.fake_bluetooth_ptr_; return this.fake_bluetooth_ptr_;
} }
} }
// FakeCentral allows clients to simulate events that a device in the
// Central/Observer role would receive as well as monitor the operations
// performed by the device in the Central/Observer role.
class FakeCentral {
constructor(fake_central_ptr) {
this.fake_central_ptr = fake_central_ptr;
}
}
navigator.bluetooth.test = new FakeBluetooth(); navigator.bluetooth.test = new FakeBluetooth();
})(); })();
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment