1 // Copyright 2022 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_profiles/device_info_service.h"
16
17 #include <string_view>
18
19 #include "gtest/gtest.h"
20 #include "pw_bluetooth/gatt/server.h"
21
22 using namespace std::string_view_literals;
23
24 namespace pw::bluetooth_profiles {
25 namespace {
26
27 class FakeGattServer final : public bluetooth::gatt::Server {
28 public:
29 // Server overrides:
PublishService(const bluetooth::gatt::LocalServiceInfo & info,bluetooth::gatt::LocalServiceDelegate * delegate,Function<void (PublishServiceResult)> && result_callback)30 void PublishService(
31 const bluetooth::gatt::LocalServiceInfo& info,
32 bluetooth::gatt::LocalServiceDelegate* delegate,
33 Function<void(PublishServiceResult)>&& result_callback) override {
34 ASSERT_EQ(delegate_, nullptr);
35 delegate_ = delegate;
36 ASSERT_EQ(published_info_, nullptr);
37 published_info_ = &info;
38 local_service_.emplace(this);
39 result_callback(
40 PublishServiceResult(std::in_place, &local_service_.value()));
41 }
42
43 // PublishService call argument getters:
published_info() const44 const bluetooth::gatt::LocalServiceInfo* published_info() const {
45 return published_info_;
46 }
delegate() const47 bluetooth::gatt::LocalServiceDelegate* delegate() const { return delegate_; }
48
49 private:
50 class FakeLocalService final : public bluetooth::gatt::LocalService {
51 public:
FakeLocalService(FakeGattServer * fake_server)52 explicit FakeLocalService(FakeGattServer* fake_server)
53 : fake_server_(fake_server) {}
54
55 // LocalService overrides:
NotifyValue(const ValueChangedParameters &,ValueChangedCallback &&)56 void NotifyValue(
57 const ValueChangedParameters& /* parameters */,
58 ValueChangedCallback&& /* completion_callback */) override {
59 FAIL(); // Unimplemented
60 }
IndicateValue(const ValueChangedParameters &,Function<void (bluetooth::Result<bluetooth::gatt::Error>)> &&)61 void IndicateValue(
62 const ValueChangedParameters& /* parameters */,
63 Function<void(
64 bluetooth::Result<bluetooth::gatt::Error>)>&& /* confirmation */)
65 override {
66 FAIL(); // Unimplemented
67 }
68
69 private:
UnpublishService()70 void UnpublishService() override { fake_server_->local_service_.reset(); }
71
72 FakeGattServer* fake_server_;
73 };
74
75 // The LocalServiceInfo passed when PublishService was called.
76 const bluetooth::gatt::LocalServiceInfo* published_info_ = nullptr;
77
78 bluetooth::gatt::LocalServiceDelegate* delegate_ = nullptr;
79
80 std::optional<FakeLocalService> local_service_;
81 };
82
TEST(DeviceInfoServiceTest,PublishAndReadTest)83 TEST(DeviceInfoServiceTest, PublishAndReadTest) {
84 FakeGattServer fake_server;
85
86 constexpr auto kUsedFields = DeviceInfo::Field::kModelNumber |
87 DeviceInfo::Field::kSerialNumber |
88 DeviceInfo::Field::kSoftwareRevision;
89 DeviceInfo device_info = {};
90 const auto kModelNumber = "model"sv;
91 device_info.model_number = as_bytes(span{kModelNumber});
92 device_info.serial_number = as_bytes(span{"parallel_number"sv});
93 device_info.software_revision = as_bytes(span{"rev123"sv});
94
95 DeviceInfoService<kUsedFields, bluetooth::gatt::Handle{123}>
96 device_info_service(device_info);
97
98 bool called = false;
99 device_info_service.PublishService(
100 &fake_server,
101 [&called](
102 bluetooth::Result<bluetooth::gatt::Server::PublishServiceError> res) {
103 EXPECT_TRUE(res.ok());
104 called = true;
105 });
106 // The FakeGattServer calls the PublishService callback right away so our
107 // callback should have been called already.
108 EXPECT_TRUE(called);
109
110 ASSERT_NE(fake_server.delegate(), nullptr);
111 ASSERT_NE(fake_server.published_info(), nullptr);
112
113 // Test that the published info looks correct.
114 EXPECT_EQ(3u, fake_server.published_info()->characteristics.size());
115
116 // Test that we can read the characteristics.
117 for (auto& characteristic : fake_server.published_info()->characteristics) {
118 bool read_callback_called = false;
119 fake_server.delegate()->ReadValue(
120 bluetooth::PeerId{1234},
121 characteristic.handle,
122 /*offset=*/0,
123 [&read_callback_called](bluetooth::Result<bluetooth::gatt::Error,
124 span<const std::byte>> res) {
125 EXPECT_TRUE(res.ok());
126 EXPECT_NE(0u, res.value().size());
127 read_callback_called = true;
128 });
129 // The DeviceInfoService always calls the callback from within ReadValue().
130 EXPECT_TRUE(read_callback_called);
131 }
132
133 // Check the actual values.
134 // The order of the characteristics in the LocalServiceInfo must be the order
135 // in which the fields are listed in kCharacteristicFields, so the first
136 // characteristic is the Model Number.
137 span<const std::byte> read_value;
138 fake_server.delegate()->ReadValue(
139 bluetooth::PeerId{1234},
140 fake_server.published_info()->characteristics[0].handle,
141 /*offset=*/0,
142 [&read_value](bluetooth::Result<bluetooth::gatt::Error,
143 span<const std::byte>> res) {
144 EXPECT_TRUE(res.ok());
145 read_value = res.value();
146 });
147 EXPECT_EQ(read_value.size(), kModelNumber.size()); // "model" string.
148 // DeviceInfoService keeps references to the values provides in the
149 // DeviceInfo struct, not copies.
150 EXPECT_EQ(read_value.data(),
151 reinterpret_cast<const std::byte*>(kModelNumber.data()));
152
153 // Read with an offset.
154 const size_t kReadOffset = 3;
155 fake_server.delegate()->ReadValue(
156 bluetooth::PeerId{1234},
157 fake_server.published_info()->characteristics[0].handle,
158 kReadOffset,
159 [&read_value](bluetooth::Result<bluetooth::gatt::Error,
160 span<const std::byte>> res) {
161 EXPECT_TRUE(res.ok());
162 read_value = res.value();
163 });
164 EXPECT_EQ(read_value.size(), kModelNumber.size() - kReadOffset);
165 EXPECT_EQ(
166 read_value.data(),
167 reinterpret_cast<const std::byte*>(kModelNumber.data()) + kReadOffset);
168 }
169
170 } // namespace
171 } // namespace pw::bluetooth_profiles
172