• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 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_sapphire/internal/host/gap/bredr_connection_request.h"
16 
17 #include <gmock/gmock.h>
18 #include <gtest/gtest.h>
19 #include <pw_async/fake_dispatcher_fixture.h>
20 
21 #include "pw_bluetooth_sapphire/internal/host/common/device_address.h"
22 #include "pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
23 #include "pw_bluetooth_sapphire/internal/host/testing/inspect.h"
24 
25 namespace bt::gap {
26 namespace {
27 
28 using namespace inspect::testing;
29 
30 const DeviceAddress kTestAddr(DeviceAddress::Type::kBREDR, {1});
31 const PeerId kPeerId;
32 constexpr hci::Error RetryableError =
33     ToResult(pw::bluetooth::emboss::StatusCode::PAGE_TIMEOUT).error_value();
34 
35 using BrEdrConnectionRequestTests = pw::async::test::FakeDispatcherFixture;
36 
TEST_F(BrEdrConnectionRequestTests,IncomingRequestStatusTracked)37 TEST_F(BrEdrConnectionRequestTests, IncomingRequestStatusTracked) {
38   // A freshly created request is not yet incoming
39   auto req = BrEdrConnectionRequest(dispatcher(),
40                                     kTestAddr,
41                                     kPeerId,
42                                     Peer::InitializingConnectionToken([] {}));
43   EXPECT_FALSE(req.HasIncoming());
44 
45   req.BeginIncoming();
46   // We should now have an incoming request, but still not an outgoing
47   EXPECT_TRUE(req.HasIncoming());
48   EXPECT_FALSE(req.AwaitingOutgoing());
49 
50   // A completed request is no longer incoming
51   req.CompleteIncoming();
52   EXPECT_FALSE(req.HasIncoming());
53 }
54 
TEST_F(BrEdrConnectionRequestTests,CallbacksExecuted)55 TEST_F(BrEdrConnectionRequestTests, CallbacksExecuted) {
56   bool callback_called = false;
57   bool token_destroyed = false;
58   auto req = BrEdrConnectionRequest(
59       dispatcher(),
60       kTestAddr,
61       kPeerId,
62       Peer::InitializingConnectionToken(
63           [&token_destroyed] { token_destroyed = true; }),
64       [&callback_called](auto, auto) { callback_called = true; });
65 
66   // A freshly created request with a callback is awaiting outgoing
67   EXPECT_TRUE(req.AwaitingOutgoing());
68   // Notifying callbacks triggers the callback
69   req.NotifyCallbacks(fit::ok(), [&]() {
70     EXPECT_TRUE(token_destroyed);
71     return nullptr;
72   });
73   EXPECT_TRUE(token_destroyed);
74   EXPECT_TRUE(callback_called);
75 }
76 
77 #ifndef NINSPECT
TEST_F(BrEdrConnectionRequestTests,Inspect)78 TEST_F(BrEdrConnectionRequestTests, Inspect) {
79   // inspector must outlive request
80   inspect::Inspector inspector;
81   BrEdrConnectionRequest req(dispatcher(),
82                              kTestAddr,
83                              kPeerId,
84                              Peer::InitializingConnectionToken([] {}),
85                              [](auto, auto) {});
86   req.BeginIncoming();
87   req.AttachInspect(inspector.GetRoot(), "request_name");
88 
89   auto hierarchy = inspect::ReadFromVmo(inspector.DuplicateVmo());
90   EXPECT_THAT(
91       hierarchy.value(),
92       ChildrenMatch(ElementsAre(NodeMatches(AllOf(
93           NameMatches("request_name"),
94           PropertyList(UnorderedElementsAre(
95               StringIs("peer_id", kPeerId.ToString()),
96               UintIs("callbacks", 1u),
97               BoolIs("has_incoming", true),
98               IntIs("first_create_connection_request_timestamp", -1))))))));
99 }
100 #endif  // NINSPECT
101 
102 class BrEdrConnectionRequestLoopTest
103     : public pw::async::test::FakeDispatcherFixture {
104  protected:
105   using OnComplete = BrEdrConnectionRequest::OnComplete;
106 
BrEdrConnectionRequestLoopTest()107   BrEdrConnectionRequestLoopTest()
108       : req_(dispatcher(),
109              kTestAddr,
110              kPeerId,
111              Peer::InitializingConnectionToken([] {}),
__anon435208cc0902(hci::Result<> res, BrEdrConnection* conn) 112              [this](hci::Result<> res, BrEdrConnection* conn) {
113                if (handler_) {
114                  handler_(res, conn);
115                }
116              }) {
117     // By default, an outbound ConnectionRequest with a complete handler that
118     // just logs the result.
__anon435208cc0a02(hci::Result<> res, auto ) 119     handler_ = [](hci::Result<> res, auto /*ignore*/) {
120       bt_log(INFO,
121              "gap-bredr-test",
122              "outbound connection request complete: %s",
123              bt_str(res));
124     };
125   }
126 
set_on_complete(BrEdrConnectionRequest::OnComplete handler)127   void set_on_complete(BrEdrConnectionRequest::OnComplete handler) {
128     handler_ = std::move(handler);
129   }
130 
connection_req()131   BrEdrConnectionRequest& connection_req() { return req_; }
132 
133  private:
134   BrEdrConnectionRequest req_;
135   OnComplete handler_;
136 };
137 using BrEdrConnectionRequestLoopDeathTest = BrEdrConnectionRequestLoopTest;
138 
TEST_F(BrEdrConnectionRequestLoopTest,RetryableErrorCodeShouldRetryAfterFirstCreateConnection)139 TEST_F(BrEdrConnectionRequestLoopTest,
140        RetryableErrorCodeShouldRetryAfterFirstCreateConnection) {
141   connection_req().RecordHciCreateConnectionAttempt();
142   RunFor(std::chrono::seconds(1));
143   EXPECT_TRUE(connection_req().ShouldRetry(RetryableError));
144 }
145 
TEST_F(BrEdrConnectionRequestLoopTest,ShouldntRetryBeforeFirstCreateConnection)146 TEST_F(BrEdrConnectionRequestLoopTest,
147        ShouldntRetryBeforeFirstCreateConnection) {
148   EXPECT_FALSE(connection_req().ShouldRetry(RetryableError));
149 }
150 
TEST_F(BrEdrConnectionRequestLoopTest,ShouldntRetryWithNonRetriableErrorCode)151 TEST_F(BrEdrConnectionRequestLoopTest, ShouldntRetryWithNonRetriableErrorCode) {
152   connection_req().RecordHciCreateConnectionAttempt();
153   RunFor(std::chrono::seconds(1));
154   EXPECT_FALSE(connection_req().ShouldRetry(hci::Error(HostError::kCanceled)));
155 }
156 
TEST_F(BrEdrConnectionRequestLoopTest,ShouldntRetryAfterThirtySeconds)157 TEST_F(BrEdrConnectionRequestLoopTest, ShouldntRetryAfterThirtySeconds) {
158   connection_req().RecordHciCreateConnectionAttempt();
159   RunFor(std::chrono::seconds(15));
160   // Should be OK to retry after 15 seconds
161   EXPECT_TRUE(connection_req().ShouldRetry(RetryableError));
162   connection_req().RecordHciCreateConnectionAttempt();
163 
164   // Should still be OK to retry, even though we've already retried
165   RunFor(std::chrono::seconds(14));
166   EXPECT_TRUE(connection_req().ShouldRetry(RetryableError));
167   connection_req().RecordHciCreateConnectionAttempt();
168 
169   RunFor(std::chrono::seconds(1));
170   EXPECT_FALSE(connection_req().ShouldRetry(RetryableError));
171 }
172 
173 }  // namespace
174 }  // namespace bt::gap
175