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