Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 1 | // |
| 2 | // Copyright (C) 2015 Google, Inc. |
| 3 | // |
| 4 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | // you may not use this file except in compliance with the License. |
| 6 | // You may obtain a copy of the License at: |
| 7 | // |
| 8 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | // |
| 10 | // Unless required by applicable law or agreed to in writing, software |
| 11 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | // See the License for the specific language governing permissions and |
| 14 | // limitations under the License. |
| 15 | // |
| 16 | |
| 17 | #include <base/macros.h> |
| 18 | #include <gmock/gmock.h> |
| 19 | #include <gtest/gtest.h> |
| 20 | |
Jakub Pawlowski | 60b0e8f | 2016-01-12 13:51:35 -0800 | [diff] [blame] | 21 | #include "service/adapter.h" |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 22 | #include "service/hal/fake_bluetooth_gatt_interface.h" |
| 23 | #include "service/low_energy_client.h" |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 24 | #include "stack/include/bt_types.h" |
| 25 | #include "stack/include/hcidefs.h" |
Jakub Pawlowski | 60b0e8f | 2016-01-12 13:51:35 -0800 | [diff] [blame] | 26 | #include "test/mock_adapter.h" |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 27 | |
| 28 | using ::testing::_; |
| 29 | using ::testing::Return; |
Jakub Pawlowski | a6372e9 | 2016-01-19 16:42:37 -0800 | [diff] [blame] | 30 | using ::testing::Pointee; |
| 31 | using ::testing::DoAll; |
| 32 | using ::testing::Invoke; |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 33 | |
| 34 | namespace bluetooth { |
| 35 | namespace { |
| 36 | |
Arman Uguray | 9e52053 | 2015-09-22 14:12:50 -0700 | [diff] [blame] | 37 | class MockGattHandler |
| 38 | : public hal::FakeBluetoothGattInterface::TestClientHandler { |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 39 | public: |
Jakub Pawlowski | c3f6a51 | 2016-10-27 11:49:40 -0700 | [diff] [blame^] | 40 | MockGattHandler() {}; |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 41 | ~MockGattHandler() override = default; |
| 42 | |
| 43 | MOCK_METHOD1(RegisterClient, bt_status_t(bt_uuid_t*)); |
| 44 | MOCK_METHOD1(UnregisterClient, bt_status_t(int)); |
Jakub Pawlowski | ce021dd | 2016-01-19 16:35:20 -0800 | [diff] [blame] | 45 | MOCK_METHOD4(Connect, bt_status_t(int , const bt_bdaddr_t *, bool, int)); |
| 46 | MOCK_METHOD3(Disconnect, bt_status_t(int , const bt_bdaddr_t *, int)); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 47 | |
| 48 | private: |
| 49 | DISALLOW_COPY_AND_ASSIGN(MockGattHandler); |
| 50 | }; |
| 51 | |
Arman Uguray | 82ea72f | 2015-12-02 17:29:27 -0800 | [diff] [blame] | 52 | class TestDelegate : public LowEnergyClient::Delegate { |
| 53 | public: |
Jakub Pawlowski | c3f6a51 | 2016-10-27 11:49:40 -0700 | [diff] [blame^] | 54 | TestDelegate() : connection_state_count_(0), last_mtu_(0) { |
Arman Uguray | 82ea72f | 2015-12-02 17:29:27 -0800 | [diff] [blame] | 55 | } |
| 56 | |
| 57 | ~TestDelegate() override = default; |
| 58 | |
Jakub Pawlowski | a6372e9 | 2016-01-19 16:42:37 -0800 | [diff] [blame] | 59 | int connection_state_count() const { return connection_state_count_; } |
| 60 | |
| 61 | void OnConnectionState(LowEnergyClient* client, int status, |
| 62 | const char* address, bool connected) { |
| 63 | ASSERT_TRUE(client); |
| 64 | connection_state_count_++; |
| 65 | } |
| 66 | |
Jakub Pawlowski | a655107 | 2016-01-26 12:58:47 -0800 | [diff] [blame] | 67 | void OnMtuChanged(LowEnergyClient* client, int status, const char* address, |
| 68 | int mtu) { |
| 69 | ASSERT_TRUE(client); |
| 70 | last_mtu_ = mtu; |
| 71 | } |
| 72 | |
Arman Uguray | 82ea72f | 2015-12-02 17:29:27 -0800 | [diff] [blame] | 73 | private: |
Jakub Pawlowski | a6372e9 | 2016-01-19 16:42:37 -0800 | [diff] [blame] | 74 | int connection_state_count_; |
| 75 | |
Jakub Pawlowski | a655107 | 2016-01-26 12:58:47 -0800 | [diff] [blame] | 76 | int last_mtu_; |
| 77 | |
Arman Uguray | 82ea72f | 2015-12-02 17:29:27 -0800 | [diff] [blame] | 78 | DISALLOW_COPY_AND_ASSIGN(TestDelegate); |
| 79 | }; |
| 80 | |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 81 | class LowEnergyClientTest : public ::testing::Test { |
| 82 | public: |
| 83 | LowEnergyClientTest() = default; |
| 84 | ~LowEnergyClientTest() override = default; |
| 85 | |
| 86 | void SetUp() override { |
Ajay Panicker | ff651b7 | 2015-09-30 15:49:47 -0700 | [diff] [blame] | 87 | // Only set |mock_handler_| if a test hasn't set it. |
| 88 | if (!mock_handler_) |
| 89 | mock_handler_.reset(new MockGattHandler()); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 90 | fake_hal_gatt_iface_ = new hal::FakeBluetoothGattInterface( |
Jakub Pawlowski | 67d5a25 | 2016-07-13 11:55:16 -0700 | [diff] [blame] | 91 | nullptr, |
Jakub Pawlowski | c3f6a51 | 2016-10-27 11:49:40 -0700 | [diff] [blame^] | 92 | nullptr, |
Arman Uguray | 9e52053 | 2015-09-22 14:12:50 -0700 | [diff] [blame] | 93 | std::static_pointer_cast< |
| 94 | hal::FakeBluetoothGattInterface::TestClientHandler>(mock_handler_), |
| 95 | nullptr); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 96 | hal::BluetoothGattInterface::InitializeForTesting(fake_hal_gatt_iface_); |
Jakub Pawlowski | 60b0e8f | 2016-01-12 13:51:35 -0800 | [diff] [blame] | 97 | ble_factory_.reset(new LowEnergyClientFactory(mock_adapter_)); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 98 | } |
| 99 | |
| 100 | void TearDown() override { |
| 101 | ble_factory_.reset(); |
| 102 | hal::BluetoothGattInterface::CleanUp(); |
| 103 | } |
| 104 | |
| 105 | protected: |
| 106 | hal::FakeBluetoothGattInterface* fake_hal_gatt_iface_; |
Jakub Pawlowski | 60b0e8f | 2016-01-12 13:51:35 -0800 | [diff] [blame] | 107 | testing::MockAdapter mock_adapter_; |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 108 | std::shared_ptr<MockGattHandler> mock_handler_; |
| 109 | std::unique_ptr<LowEnergyClientFactory> ble_factory_; |
| 110 | |
| 111 | private: |
| 112 | DISALLOW_COPY_AND_ASSIGN(LowEnergyClientTest); |
| 113 | }; |
| 114 | |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 115 | // Used for tests that operate on a pre-registered client. |
| 116 | class LowEnergyClientPostRegisterTest : public LowEnergyClientTest { |
| 117 | public: |
Jakub Pawlowski | 53cfa7c | 2016-01-13 15:41:06 -0800 | [diff] [blame] | 118 | LowEnergyClientPostRegisterTest() : next_client_id_(0) { |
| 119 | } |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 120 | ~LowEnergyClientPostRegisterTest() override = default; |
| 121 | |
| 122 | void SetUp() override { |
| 123 | LowEnergyClientTest::SetUp(); |
Jakub Pawlowski | 53cfa7c | 2016-01-13 15:41:06 -0800 | [diff] [blame] | 124 | auto callback = [&](std::unique_ptr<LowEnergyClient> client) { |
| 125 | le_client_ = std::move(client); |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 126 | }; |
Jakub Pawlowski | 53cfa7c | 2016-01-13 15:41:06 -0800 | [diff] [blame] | 127 | RegisterTestClient(callback); |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 128 | } |
| 129 | |
| 130 | void TearDown() override { |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 131 | EXPECT_CALL(*mock_handler_, UnregisterClient(_)) |
| 132 | .Times(1) |
| 133 | .WillOnce(Return(BT_STATUS_SUCCESS)); |
| 134 | le_client_.reset(); |
| 135 | LowEnergyClientTest::TearDown(); |
| 136 | } |
| 137 | |
Jakub Pawlowski | 53cfa7c | 2016-01-13 15:41:06 -0800 | [diff] [blame] | 138 | void RegisterTestClient( |
Jakub Pawlowski | 67d5a25 | 2016-07-13 11:55:16 -0700 | [diff] [blame] | 139 | const std::function<void(std::unique_ptr<LowEnergyClient> client)> |
Jakub Pawlowski | 53cfa7c | 2016-01-13 15:41:06 -0800 | [diff] [blame] | 140 | callback) { |
| 141 | UUID uuid = UUID::GetRandom(); |
| 142 | auto api_callback = [&](BLEStatus status, const UUID& in_uuid, |
| 143 | std::unique_ptr<BluetoothInstance> in_client) { |
| 144 | CHECK(in_uuid == uuid); |
| 145 | CHECK(in_client.get()); |
| 146 | CHECK(status == BLE_STATUS_SUCCESS); |
| 147 | |
| 148 | callback(std::unique_ptr<LowEnergyClient>( |
| 149 | static_cast<LowEnergyClient*>(in_client.release()))); |
| 150 | }; |
| 151 | |
| 152 | EXPECT_CALL(*mock_handler_, RegisterClient(_)) |
| 153 | .Times(1) |
| 154 | .WillOnce(Return(BT_STATUS_SUCCESS)); |
| 155 | |
| 156 | ble_factory_->RegisterInstance(uuid, api_callback); |
| 157 | |
| 158 | bt_uuid_t hal_uuid = uuid.GetBlueDroid(); |
| 159 | fake_hal_gatt_iface_->NotifyRegisterClientCallback( |
| 160 | 0, next_client_id_++, hal_uuid); |
| 161 | ::testing::Mock::VerifyAndClearExpectations(mock_handler_.get()); |
| 162 | } |
| 163 | |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 164 | protected: |
| 165 | std::unique_ptr<LowEnergyClient> le_client_; |
| 166 | |
| 167 | private: |
Jakub Pawlowski | 53cfa7c | 2016-01-13 15:41:06 -0800 | [diff] [blame] | 168 | int next_client_id_; |
| 169 | |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 170 | DISALLOW_COPY_AND_ASSIGN(LowEnergyClientPostRegisterTest); |
| 171 | }; |
| 172 | |
Arman Uguray | bb18c41 | 2015-11-12 13:44:31 -0800 | [diff] [blame] | 173 | TEST_F(LowEnergyClientTest, RegisterInstance) { |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 174 | EXPECT_CALL(*mock_handler_, RegisterClient(_)) |
| 175 | .Times(2) |
| 176 | .WillOnce(Return(BT_STATUS_FAIL)) |
| 177 | .WillOnce(Return(BT_STATUS_SUCCESS)); |
| 178 | |
| 179 | // These will be asynchronously populated with a result when the callback |
| 180 | // executes. |
| 181 | BLEStatus status = BLE_STATUS_SUCCESS; |
| 182 | UUID cb_uuid; |
| 183 | std::unique_ptr<LowEnergyClient> client; |
| 184 | int callback_count = 0; |
| 185 | |
Arman Uguray | 08f80eb | 2015-09-21 11:17:07 -0700 | [diff] [blame] | 186 | auto callback = [&](BLEStatus in_status, const UUID& uuid, |
Arman Uguray | bb18c41 | 2015-11-12 13:44:31 -0800 | [diff] [blame] | 187 | std::unique_ptr<BluetoothInstance> in_client) { |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 188 | status = in_status; |
| 189 | cb_uuid = uuid; |
Arman Uguray | 08f80eb | 2015-09-21 11:17:07 -0700 | [diff] [blame] | 190 | client = std::unique_ptr<LowEnergyClient>( |
| 191 | static_cast<LowEnergyClient*>(in_client.release())); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 192 | callback_count++; |
| 193 | }; |
| 194 | |
| 195 | UUID uuid0 = UUID::GetRandom(); |
| 196 | |
| 197 | // HAL returns failure. |
Arman Uguray | bb18c41 | 2015-11-12 13:44:31 -0800 | [diff] [blame] | 198 | EXPECT_FALSE(ble_factory_->RegisterInstance(uuid0, callback)); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 199 | EXPECT_EQ(0, callback_count); |
| 200 | |
| 201 | // HAL returns success. |
Arman Uguray | bb18c41 | 2015-11-12 13:44:31 -0800 | [diff] [blame] | 202 | EXPECT_TRUE(ble_factory_->RegisterInstance(uuid0, callback)); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 203 | EXPECT_EQ(0, callback_count); |
| 204 | |
| 205 | // Calling twice with the same UUID should fail with no additional call into |
| 206 | // the stack. |
Arman Uguray | bb18c41 | 2015-11-12 13:44:31 -0800 | [diff] [blame] | 207 | EXPECT_FALSE(ble_factory_->RegisterInstance(uuid0, callback)); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 208 | |
Jakub Pawlowski | 60b0e8f | 2016-01-12 13:51:35 -0800 | [diff] [blame] | 209 | ::testing::Mock::VerifyAndClearExpectations(mock_handler_.get()); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 210 | |
| 211 | // Call with a different UUID while one is pending. |
| 212 | UUID uuid1 = UUID::GetRandom(); |
| 213 | EXPECT_CALL(*mock_handler_, RegisterClient(_)) |
| 214 | .Times(1) |
| 215 | .WillOnce(Return(BT_STATUS_SUCCESS)); |
Arman Uguray | bb18c41 | 2015-11-12 13:44:31 -0800 | [diff] [blame] | 216 | EXPECT_TRUE(ble_factory_->RegisterInstance(uuid1, callback)); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 217 | |
| 218 | // Trigger callback with an unknown UUID. This should get ignored. |
| 219 | UUID uuid2 = UUID::GetRandom(); |
| 220 | bt_uuid_t hal_uuid = uuid2.GetBlueDroid(); |
| 221 | fake_hal_gatt_iface_->NotifyRegisterClientCallback(0, 0, hal_uuid); |
| 222 | EXPECT_EQ(0, callback_count); |
| 223 | |
| 224 | // |uuid0| succeeds. |
| 225 | int client_if0 = 2; // Pick something that's not 0. |
| 226 | hal_uuid = uuid0.GetBlueDroid(); |
| 227 | fake_hal_gatt_iface_->NotifyRegisterClientCallback( |
| 228 | BT_STATUS_SUCCESS, client_if0, hal_uuid); |
| 229 | |
| 230 | EXPECT_EQ(1, callback_count); |
| 231 | ASSERT_TRUE(client.get() != nullptr); // Assert to terminate in case of error |
| 232 | EXPECT_EQ(BLE_STATUS_SUCCESS, status); |
Arman Uguray | bb18c41 | 2015-11-12 13:44:31 -0800 | [diff] [blame] | 233 | EXPECT_EQ(client_if0, client->GetInstanceId()); |
Arman Uguray | 08f80eb | 2015-09-21 11:17:07 -0700 | [diff] [blame] | 234 | EXPECT_EQ(uuid0, client->GetAppIdentifier()); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 235 | EXPECT_EQ(uuid0, cb_uuid); |
| 236 | |
| 237 | // The client should unregister itself when deleted. |
| 238 | EXPECT_CALL(*mock_handler_, UnregisterClient(client_if0)) |
| 239 | .Times(1) |
| 240 | .WillOnce(Return(BT_STATUS_SUCCESS)); |
| 241 | client.reset(); |
Jakub Pawlowski | 60b0e8f | 2016-01-12 13:51:35 -0800 | [diff] [blame] | 242 | ::testing::Mock::VerifyAndClearExpectations(mock_handler_.get()); |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 243 | |
| 244 | // |uuid1| fails. |
| 245 | int client_if1 = 3; |
| 246 | hal_uuid = uuid1.GetBlueDroid(); |
| 247 | fake_hal_gatt_iface_->NotifyRegisterClientCallback( |
| 248 | BT_STATUS_FAIL, client_if1, hal_uuid); |
| 249 | |
| 250 | EXPECT_EQ(2, callback_count); |
| 251 | ASSERT_TRUE(client.get() == nullptr); // Assert to terminate in case of error |
| 252 | EXPECT_EQ(BLE_STATUS_FAILURE, status); |
| 253 | EXPECT_EQ(uuid1, cb_uuid); |
Arman Uguray | 1233840 | 2015-09-16 18:00:05 -0700 | [diff] [blame] | 254 | } |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 255 | |
Jakub Pawlowski | a6372e9 | 2016-01-19 16:42:37 -0800 | [diff] [blame] | 256 | MATCHER_P(BitEq, x, std::string(negation ? "isn't" : "is") + |
| 257 | " bitwise equal to " + ::testing::PrintToString(x)) { |
| 258 | static_assert(sizeof(x) == sizeof(arg), "Size mismatch"); |
| 259 | return std::memcmp(&arg, &x, sizeof(x)) == 0; |
| 260 | } |
| 261 | |
| 262 | TEST_F(LowEnergyClientPostRegisterTest, Connect) { |
| 263 | const bt_bdaddr_t kTestAddress = { |
| 264 | { 0x01, 0x02, 0x03, 0x0A, 0x0B, 0x0C } |
| 265 | }; |
| 266 | const char kTestAddressStr[] = "01:02:03:0A:0B:0C"; |
| 267 | const bool kTestDirect = false; |
| 268 | const int connId = 12; |
| 269 | |
| 270 | TestDelegate delegate; |
| 271 | le_client_->SetDelegate(&delegate); |
| 272 | |
| 273 | // TODO(jpawlowski): NotifyConnectCallback should be called after returning |
| 274 | // success, fix it when it becomes important. |
| 275 | // These should succeed and result in a HAL call |
| 276 | EXPECT_CALL(*mock_handler_, Connect(le_client_->GetInstanceId(), |
| 277 | Pointee(BitEq(kTestAddress)), kTestDirect, BT_TRANSPORT_LE)) |
| 278 | .Times(1) |
| 279 | .WillOnce(DoAll( |
| 280 | Invoke([&](int client_id, const bt_bdaddr_t *bd_addr, bool is_direct, |
| 281 | int transport){ |
| 282 | fake_hal_gatt_iface_->NotifyConnectCallback(connId, BT_STATUS_SUCCESS, |
| 283 | client_id, *bd_addr); |
| 284 | }), |
| 285 | Return(BT_STATUS_SUCCESS))); |
| 286 | |
| 287 | EXPECT_TRUE(le_client_->Connect(kTestAddressStr, kTestDirect)); |
| 288 | EXPECT_EQ(1, delegate.connection_state_count()); |
| 289 | |
| 290 | // TODO(jpawlowski): same as above |
| 291 | // These should succeed and result in a HAL call |
| 292 | EXPECT_CALL(*mock_handler_, Disconnect(le_client_->GetInstanceId(), |
| 293 | Pointee(BitEq(kTestAddress)), connId)) |
| 294 | .Times(1) |
| 295 | .WillOnce(DoAll( |
| 296 | Invoke([&](int client_id, const bt_bdaddr_t *bd_addr, int connId){ |
| 297 | fake_hal_gatt_iface_->NotifyDisconnectCallback(connId, |
| 298 | BT_STATUS_SUCCESS, |
| 299 | client_id, *bd_addr); |
| 300 | }), |
| 301 | Return(BT_STATUS_SUCCESS))); |
| 302 | |
| 303 | EXPECT_TRUE(le_client_->Disconnect(kTestAddressStr)); |
| 304 | EXPECT_EQ(2, delegate.connection_state_count()); |
| 305 | |
| 306 | le_client_->SetDelegate(nullptr); |
| 307 | ::testing::Mock::VerifyAndClearExpectations(mock_handler_.get()); |
| 308 | } |
| 309 | |
| 310 | |
Arman Uguray | c2fc0f2 | 2015-09-03 15:09:41 -0700 | [diff] [blame] | 311 | } // namespace |
| 312 | } // namespace bluetooth |