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