blob: 153027e6526acda3df12498ca7bbbc626ad8196a [file] [log] [blame]
// 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 */,
Closure&& /* 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