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 #pragma once
16 #include <pw_async/fake_dispatcher_fixture.h>
17 
18 #include "pw_bluetooth_sapphire/internal/host/l2cap/fake_channel.h"
19 
20 namespace bt::l2cap::testing {
21 
22 // Provides a GTest harness base class for tests that operate over an L2CAP
23 // channel. Outgoing packet expectations are written using the
24 // EXPECT_PACKET_OUT() macro, and may specify 0 or more responses that should be
25 // sent when the outgoing packet is received by MockChannelTest:
26 //
27 // StaticByteBuffer kRequest_0(...);
28 // StaticByteBuffer kRequest_1(...);
29 // ...
30 // EXPECT_PACKET_OUT(kRequest_0);
31 // EXPECT_PACKET_OUT(kRequest_1, kResponse_0, kResponse_1);
32 // foo.Start();
33 // EXPECT_TRUE(AllExpectedPacketsSent());
34 // fake_chan()->Receive(kRequest_2); // Simulate inbound packet
35 class MockChannelTest : public pw::async::test::FakeDispatcherFixture {
36  public:
37   struct ExpectationMetadata {
38     const char* file;
39     int line;
40     const char* expectation;
41   };
42 
43   struct PacketExpectation {
44     DynamicByteBuffer data;
45     ExpectationMetadata meta;
46   };
47 
48   class Transaction {
49    public:
50     // The |expected| buffer and the buffers in |replies| will be copied, so
51     // their lifetime does not need to extend past Transaction construction.
52     Transaction(const ByteBuffer& expected,
53                 const std::vector<const ByteBuffer*>& replies,
54                 ExpectationMetadata meta);
55     virtual ~Transaction() = default;
56     Transaction(Transaction&& other) = default;
57     Transaction& operator=(Transaction&& other) = default;
58 
59     // Returns true if the transaction matches the given packet.
60     bool Match(const ByteBuffer& packet);
61 
expected()62     const PacketExpectation& expected() { return expected_; }
set_expected(const PacketExpectation & expected)63     void set_expected(const PacketExpectation& expected) {
64       expected_ = PacketExpectation{.data = DynamicByteBuffer(expected.data),
65                                     .meta = expected.meta};
66     }
67 
replies()68     std::queue<DynamicByteBuffer>& replies() { return replies_; }
69 
70    private:
71     PacketExpectation expected_;
72     std::queue<DynamicByteBuffer> replies_;
73   };
74 
75   struct ChannelOptions {
76     explicit ChannelOptions(ChannelId id, uint16_t mtu = kDefaultMTU)
ChannelOptionsChannelOptions77         : ChannelOptions(id, id, mtu) {}
ChannelOptionsChannelOptions78     ChannelOptions(ChannelId id, ChannelId remote_id, uint16_t mtu)
79         : id(id), remote_id(remote_id), mtu(mtu) {}
80 
81     ChannelId id;
82     ChannelId remote_id;
83     uint16_t mtu;
84     hci_spec::ConnectionHandle conn_handle = 0x0001;
85     bt::LinkType link_type = bt::LinkType::kLE;
86   };
87 
88   MockChannelTest() = default;
89   ~MockChannelTest() override = default;
90 
91   void TearDown() override;
92 
heap_dispatcher()93   pw::async::HeapDispatcher& heap_dispatcher() { return heap_dispatcher_; }
94 
95   // Queues a transaction into the MockChannelTest's expected packet queue. Each
96   // packet received through the channel will be verified against the next
97   // expected transaction in the queue. A mismatch will cause a fatal assertion.
98   // On a match, MockChannelTest will send back the replies provided in the
99   // transaction. NOTE: It is recommended to use the EXPECT_PACKET_OUT macro
100   // instead of calling this method directly.
101   void QueueTransaction(const ByteBuffer& expected,
102                         const std::vector<const ByteBuffer*>& replies,
103                         ExpectationMetadata meta);
104 
105   // Create a FakeChannel owned by this test fixture. Replaces any existing
106   // channel.
107   FakeChannel::WeakPtr CreateFakeChannel(const ChannelOptions& options);
108 
109   using PacketCallback = fit::function<void(const ByteBuffer& packet)>;
SetPacketCallback(PacketCallback callback)110   void SetPacketCallback(PacketCallback callback) {
111     packet_callback_ = std::move(callback);
112   }
113 
AllExpectedPacketsSent()114   bool AllExpectedPacketsSent() const { return transactions_.empty(); }
115 
fake_chan()116   FakeChannel::WeakPtr fake_chan() const { return fake_chan_->AsWeakPtr(); }
117 
118  private:
119   void OnPacketSent(std::unique_ptr<ByteBuffer> packet);
120 
121   std::queue<Transaction> transactions_;
122   std::unique_ptr<FakeChannel> fake_chan_;
123   PacketCallback packet_callback_;
124   pw::async::HeapDispatcher heap_dispatcher_{dispatcher()};
125 };
126 
127 // Helper macro for expecting a packet and receiving a variable number of
128 // responses.
129 #define EXPECT_PACKET_OUT(expected, ...)                                     \
130   QueueTransaction((expected),                                               \
131                    {__VA_ARGS__},                                            \
132                    bt::l2cap::testing::MockChannelTest::ExpectationMetadata{ \
133                        __FILE__, __LINE__, #expected})
134 
135 }  // namespace bt::l2cap::testing
136