• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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