blob: bfbc0354fe1924c74411333d1130955089a29674 [file] [log] [blame]
#include "gatt_remote_service_server.h"
#include "gtest/gtest.h"
#include "helpers.h"
#include "lib/gtest/test_loop_fixture.h"
#include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/fake_client.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/fake_layer.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/remote_service.h"
#include "src/connectivity/bluetooth/core/bt-host/gatt/remote_service_manager.h"
namespace bthost {
namespace {
namespace fbgatt = fuchsia::bluetooth::gatt;
constexpr bt::att::Handle kServiceStartHandle = 0x0021;
constexpr bt::att::Handle kServiceEndHandle = 0x002C;
const bt::UUID kServiceUuid((uint16_t)0x180D);
void NopStatusCallback(bt::att::Status) {}
}; // namespace
using TestingBase = ::gtest::TestLoopFixture;
class FIDL_GattRemoteServiceServerTest : public TestingBase {
public:
void SetUp() override {
auto client = std::make_unique<bt::gatt::testing::FakeClient>(dispatcher());
fake_client_ = client.get();
mgr_ =
std::make_unique<bt::gatt::internal::RemoteServiceManager>(std::move(client), dispatcher());
service_ = SetUpFakeService(
bt::gatt::ServiceData(kServiceStartHandle, kServiceEndHandle, kServiceUuid));
fidl::InterfaceHandle<fbgatt::RemoteService> handle;
server_ = std::make_unique<GattRemoteServiceServer>(
service_, /*gatt=*/bt::gatt::testing::FakeLayer::Create(), handle.NewRequest());
proxy_.Bind(std::move(handle));
}
void TearDown() override {
// Clear any previous expectations that are based on the ATT Write Request,
// so that write requests sent during RemoteService::ShutDown() are ignored.
fake_client()->set_write_request_callback({});
}
protected:
// Initializes a RemoteService based on |data|.
fbl::RefPtr<bt::gatt::RemoteService> SetUpFakeService(const bt::gatt::ServiceData& data) {
std::vector<bt::gatt::ServiceData> fake_services{{data}};
fake_client()->set_primary_services(std::move(fake_services));
mgr_->Initialize(NopStatusCallback);
bt::gatt::ServiceList services;
mgr_->ListServices(std::vector<bt::UUID>(),
[&services](auto status, bt::gatt::ServiceList cb_services) {
services = std::move(cb_services);
});
RunLoopUntilIdle();
ZX_DEBUG_ASSERT(services.size() == 1u);
return services[0];
}
bt::gatt::testing::FakeClient* fake_client() const { return fake_client_; }
fbgatt::RemoteServicePtr& service_proxy() { return proxy_; }
private:
std::unique_ptr<GattRemoteServiceServer> server_;
fbgatt::RemoteServicePtr proxy_;
std::unique_ptr<bt::gatt::internal::RemoteServiceManager> mgr_;
// The memory is owned by |mgr_|.
bt::gatt::testing::FakeClient* fake_client_;
fbl::RefPtr<bt::gatt::RemoteService> service_;
};
TEST_F(FIDL_GattRemoteServiceServerTest, ReadByTypeSuccess) {
constexpr bt::UUID kCharUuid((uint16_t)0xfefe);
constexpr bt::att::Handle kHandle = kServiceStartHandle;
const auto kValue = bt::StaticByteBuffer(0x00, 0x01, 0x02);
const std::vector<bt::gatt::Client::ReadByTypeValue> kValues = {{kHandle, kValue.view()}};
size_t read_count = 0;
fake_client()->set_read_by_type_request_callback(
[&](const bt::UUID& type, bt::att::Handle start, bt::att::Handle end, auto callback) {
switch (read_count++) {
case 0:
callback(fit::ok(kValues));
break;
case 1:
callback(fit::error(bt::gatt::Client::ReadByTypeError{
bt::att::Status(bt::att::ErrorCode::kAttributeNotFound), start}));
break;
default:
FAIL();
}
});
std::optional<fbgatt::RemoteService_ReadByType_Result> fidl_result;
service_proxy()->ReadByType(fidl_helpers::UuidToFidl(kCharUuid),
[&](auto cb_result) { fidl_result = std::move(cb_result); });
RunLoopUntilIdle();
ASSERT_TRUE(fidl_result.has_value());
ASSERT_TRUE(fidl_result->is_response());
const auto& response = fidl_result->response();
ASSERT_EQ(1u, response.results.size());
const fbgatt::ReadByTypeResult& result0 = response.results[0];
ASSERT_TRUE(result0.has_id());
EXPECT_EQ(result0.id(), static_cast<uint64_t>(kHandle));
ASSERT_TRUE(result0.has_value());
EXPECT_TRUE(
ContainersEqual(bt::BufferView(result0.value().data(), result0.value().size()), kValue));
EXPECT_FALSE(result0.has_error());
}
TEST_F(FIDL_GattRemoteServiceServerTest, ReadByTypeResultWithError) {
constexpr bt::UUID kCharUuid((uint16_t)0xfefe);
size_t read_count = 0;
fake_client()->set_read_by_type_request_callback(
[&](const bt::UUID& type, bt::att::Handle start, bt::att::Handle end, auto callback) {
ASSERT_EQ(0u, read_count++);
callback(fit::error(bt::gatt::Client::ReadByTypeError{
bt::att::Status(bt::att::ErrorCode::kInsufficientAuthorization), kServiceEndHandle}));
});
std::optional<fbgatt::RemoteService_ReadByType_Result> fidl_result;
service_proxy()->ReadByType(fidl_helpers::UuidToFidl(kCharUuid),
[&](auto cb_result) { fidl_result = std::move(cb_result); });
RunLoopUntilIdle();
ASSERT_TRUE(fidl_result.has_value());
ASSERT_TRUE(fidl_result->is_response());
const auto& response = fidl_result->response();
ASSERT_EQ(1u, response.results.size());
const fbgatt::ReadByTypeResult& result0 = response.results[0];
ASSERT_TRUE(result0.has_id());
EXPECT_EQ(result0.id(), static_cast<uint64_t>(kServiceEndHandle));
EXPECT_FALSE(result0.has_value());
ASSERT_TRUE(result0.has_error());
EXPECT_EQ(fbgatt::Error::INSUFFICIENT_AUTHORIZATION, result0.error());
}
TEST_F(FIDL_GattRemoteServiceServerTest, ReadByTypeError) {
constexpr bt::UUID kCharUuid((uint16_t)0xfefe);
size_t read_count = 0;
fake_client()->set_read_by_type_request_callback(
[&](const bt::UUID& type, bt::att::Handle start, bt::att::Handle end, auto callback) {
switch (read_count++) {
case 0:
callback(fit::error(bt::gatt::Client::ReadByTypeError{
bt::att::Status(bt::HostError::kPacketMalformed), std::nullopt}));
break;
default:
FAIL();
}
});
std::optional<fbgatt::RemoteService_ReadByType_Result> fidl_result;
service_proxy()->ReadByType(fidl_helpers::UuidToFidl(kCharUuid),
[&](auto cb_result) { fidl_result = std::move(cb_result); });
RunLoopUntilIdle();
ASSERT_TRUE(fidl_result.has_value());
ASSERT_TRUE(fidl_result->is_err());
const auto& err = fidl_result->err();
EXPECT_EQ(fbgatt::Error::INVALID_RESPONSE, err);
}
TEST_F(FIDL_GattRemoteServiceServerTest, ReadByTypeInvalidParametersErrorClosesChannel) {
constexpr bt::UUID kCharUuid = bt::gatt::types::kCharacteristicDeclaration;
std::optional<zx_status_t> error_status;
service_proxy().set_error_handler([&](zx_status_t status) { error_status = status; });
std::optional<fbgatt::RemoteService_ReadByType_Result> fidl_result;
service_proxy()->ReadByType(fidl_helpers::UuidToFidl(kCharUuid),
[&](auto cb_result) { fidl_result = std::move(cb_result); });
RunLoopUntilIdle();
EXPECT_FALSE(fidl_result.has_value());
EXPECT_FALSE(service_proxy().is_bound());
EXPECT_TRUE(error_status.has_value());
EXPECT_EQ(ZX_ERR_INVALID_ARGS, error_status.value());
}
}; // namespace bthost