172 lines
6.0 KiB
C++
172 lines
6.0 KiB
C++
// Copyright 2022 The Pigweed Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
|
|
#include "pw_bluetooth_profiles/device_info_service.h"
|
|
|
|
#include <string_view>
|
|
|
|
#include "gtest/gtest.h"
|
|
#include "pw_bluetooth/gatt/server.h"
|
|
|
|
using namespace std::string_view_literals;
|
|
|
|
namespace pw::bluetooth_profiles {
|
|
namespace {
|
|
|
|
class FakeGattServer final : public bluetooth::gatt::Server {
|
|
public:
|
|
// Server overrides:
|
|
void PublishService(
|
|
const bluetooth::gatt::LocalServiceInfo& info,
|
|
bluetooth::gatt::LocalServiceDelegate* delegate,
|
|
Function<void(PublishServiceResult)>&& result_callback) override {
|
|
ASSERT_EQ(delegate_, nullptr);
|
|
delegate_ = delegate;
|
|
ASSERT_EQ(published_info_, nullptr);
|
|
published_info_ = &info;
|
|
local_service_.emplace(this);
|
|
result_callback(
|
|
PublishServiceResult(std::in_place, &local_service_.value()));
|
|
}
|
|
|
|
// PublishService call argument getters:
|
|
const bluetooth::gatt::LocalServiceInfo* published_info() const {
|
|
return published_info_;
|
|
}
|
|
bluetooth::gatt::LocalServiceDelegate* delegate() const { return delegate_; }
|
|
|
|
private:
|
|
class FakeLocalService final : public bluetooth::gatt::LocalService {
|
|
public:
|
|
explicit FakeLocalService(FakeGattServer* fake_server)
|
|
: fake_server_(fake_server) {}
|
|
|
|
// LocalService overrides:
|
|
void NotifyValue(
|
|
const ValueChangedParameters& /* parameters */,
|
|
ValueChangedCallback&& /* completion_callback */) override {
|
|
FAIL(); // Unimplemented
|
|
}
|
|
void IndicateValue(
|
|
const ValueChangedParameters& /* parameters */,
|
|
Function<void(
|
|
bluetooth::Result<bluetooth::gatt::Error>)>&& /* confirmation */)
|
|
override {
|
|
FAIL(); // Unimplemented
|
|
}
|
|
|
|
private:
|
|
void UnpublishService() override { fake_server_->local_service_.reset(); }
|
|
|
|
FakeGattServer* fake_server_;
|
|
};
|
|
|
|
// The LocalServiceInfo passed when PublishService was called.
|
|
const bluetooth::gatt::LocalServiceInfo* published_info_ = nullptr;
|
|
|
|
bluetooth::gatt::LocalServiceDelegate* delegate_ = nullptr;
|
|
|
|
std::optional<FakeLocalService> local_service_;
|
|
};
|
|
|
|
TEST(DeviceInfoServiceTest, PublishAndReadTest) {
|
|
FakeGattServer fake_server;
|
|
|
|
constexpr auto kUsedFields = DeviceInfo::Field::kModelNumber |
|
|
DeviceInfo::Field::kSerialNumber |
|
|
DeviceInfo::Field::kSoftwareRevision;
|
|
DeviceInfo device_info = {};
|
|
const auto kModelNumber = "model"sv;
|
|
device_info.model_number = as_bytes(span{kModelNumber});
|
|
device_info.serial_number = as_bytes(span{"parallel_number"sv});
|
|
device_info.software_revision = as_bytes(span{"rev123"sv});
|
|
|
|
DeviceInfoService<kUsedFields, bluetooth::gatt::Handle{123}>
|
|
device_info_service(device_info);
|
|
|
|
bool called = false;
|
|
device_info_service.PublishService(
|
|
&fake_server,
|
|
[&called](
|
|
bluetooth::Result<bluetooth::gatt::Server::PublishServiceError> res) {
|
|
EXPECT_TRUE(res.ok());
|
|
called = true;
|
|
});
|
|
// The FakeGattServer calls the PublishService callback right away so our
|
|
// callback should have been called already.
|
|
EXPECT_TRUE(called);
|
|
|
|
ASSERT_NE(fake_server.delegate(), nullptr);
|
|
ASSERT_NE(fake_server.published_info(), nullptr);
|
|
|
|
// Test that the published info looks correct.
|
|
EXPECT_EQ(3u, fake_server.published_info()->characteristics.size());
|
|
|
|
// Test that we can read the characteristics.
|
|
for (auto& characteristic : fake_server.published_info()->characteristics) {
|
|
bool read_callback_called = false;
|
|
fake_server.delegate()->ReadValue(
|
|
bluetooth::PeerId{1234},
|
|
characteristic.handle,
|
|
/*offset=*/0,
|
|
[&read_callback_called](bluetooth::Result<bluetooth::gatt::Error,
|
|
span<const std::byte>> res) {
|
|
EXPECT_TRUE(res.ok());
|
|
EXPECT_NE(0u, res.value().size());
|
|
read_callback_called = true;
|
|
});
|
|
// The DeviceInfoService always calls the callback from within ReadValue().
|
|
EXPECT_TRUE(read_callback_called);
|
|
}
|
|
|
|
// Check the actual values.
|
|
// The order of the characteristics in the LocalServiceInfo must be the order
|
|
// in which the fields are listed in kCharacteristicFields, so the first
|
|
// characteristic is the Model Number.
|
|
span<const std::byte> read_value;
|
|
fake_server.delegate()->ReadValue(
|
|
bluetooth::PeerId{1234},
|
|
fake_server.published_info()->characteristics[0].handle,
|
|
/*offset=*/0,
|
|
[&read_value](bluetooth::Result<bluetooth::gatt::Error,
|
|
span<const std::byte>> res) {
|
|
EXPECT_TRUE(res.ok());
|
|
read_value = res.value();
|
|
});
|
|
EXPECT_EQ(read_value.size(), kModelNumber.size()); // "model" string.
|
|
// DeviceInfoService keeps references to the values provides in the
|
|
// DeviceInfo struct, not copies.
|
|
EXPECT_EQ(read_value.data(),
|
|
reinterpret_cast<const std::byte*>(kModelNumber.data()));
|
|
|
|
// Read with an offset.
|
|
const size_t kReadOffset = 3;
|
|
fake_server.delegate()->ReadValue(
|
|
bluetooth::PeerId{1234},
|
|
fake_server.published_info()->characteristics[0].handle,
|
|
kReadOffset,
|
|
[&read_value](bluetooth::Result<bluetooth::gatt::Error,
|
|
span<const std::byte>> res) {
|
|
EXPECT_TRUE(res.ok());
|
|
read_value = res.value();
|
|
});
|
|
EXPECT_EQ(read_value.size(), kModelNumber.size() - kReadOffset);
|
|
EXPECT_EQ(
|
|
read_value.data(),
|
|
reinterpret_cast<const std::byte*>(kModelNumber.data()) + kReadOffset);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace pw::bluetooth_profiles
|