1 // Copyright 2021 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_multisink/multisink.h"
16
17 #include "gtest/gtest.h"
18 #include "pw_multisink/drain.h"
19
20 namespace pw::multisink {
21
22 class MultiSinkTest : public ::testing::Test {
23 protected:
24 static constexpr std::byte kMessage[] = {
25 (std::byte)0xDE, (std::byte)0xAD, (std::byte)0xBE, (std::byte)0xEF};
26 static constexpr size_t kMaxDrains = 3;
27 static constexpr size_t kEntryBufferSize = 1024;
28 static constexpr size_t kBufferSize = 5 * kEntryBufferSize;
29
MultiSinkTest()30 MultiSinkTest() : multisink_(buffer_) {}
31
ExpectMessageAndDropCount(Drain & drain,std::span<const std::byte> expected_message,uint32_t expected_drop_count)32 void ExpectMessageAndDropCount(Drain& drain,
33 std::span<const std::byte> expected_message,
34 uint32_t expected_drop_count) {
35 uint32_t drop_count = 0;
36 Result<ConstByteSpan> result = drain.GetEntry(entry_buffer_, drop_count);
37 if (expected_message.empty()) {
38 EXPECT_EQ(Status::OutOfRange(), result.status());
39 } else {
40 ASSERT_TRUE(result.ok());
41 EXPECT_EQ(memcmp(result.value().data(),
42 expected_message.data(),
43 expected_message.size_bytes()),
44 0);
45 }
46 EXPECT_EQ(drop_count, expected_drop_count);
47 }
48
49 std::byte buffer_[kBufferSize];
50 std::byte entry_buffer_[kEntryBufferSize];
51 Drain drains_[kMaxDrains];
52 MultiSink multisink_;
53 };
54
TEST_F(MultiSinkTest,SingleDrain)55 TEST_F(MultiSinkTest, SingleDrain) {
56 EXPECT_EQ(OkStatus(), multisink_.AttachDrain(drains_[0]));
57 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
58
59 // Single entry push and pop.
60 ExpectMessageAndDropCount(drains_[0], kMessage, 0u);
61
62 // Multiple entries with intermittent drops.
63 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
64 multisink_.HandleDropped();
65 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
66 ExpectMessageAndDropCount(drains_[0], kMessage, 0u);
67 ExpectMessageAndDropCount(drains_[0], kMessage, 1u);
68
69 // Send drops only.
70 multisink_.HandleDropped();
71 ExpectMessageAndDropCount(drains_[0], {}, 1u);
72
73 // Confirm out-of-range if no entries are expected.
74 ExpectMessageAndDropCount(drains_[0], {}, 0u);
75 }
76
TEST_F(MultiSinkTest,MultipleDrain)77 TEST_F(MultiSinkTest, MultipleDrain) {
78 EXPECT_EQ(OkStatus(), multisink_.AttachDrain(drains_[0]));
79 EXPECT_EQ(OkStatus(), multisink_.AttachDrain(drains_[1]));
80
81 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
82 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
83 multisink_.HandleDropped();
84 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
85 multisink_.HandleDropped();
86
87 // Drain one drain entirely.
88 ExpectMessageAndDropCount(drains_[0], kMessage, 0u);
89 ExpectMessageAndDropCount(drains_[0], kMessage, 0u);
90 ExpectMessageAndDropCount(drains_[0], kMessage, 1u);
91 ExpectMessageAndDropCount(drains_[0], {}, 1u);
92 ExpectMessageAndDropCount(drains_[0], {}, 0u);
93
94 // Confirm the other drain can be drained separately.
95 ExpectMessageAndDropCount(drains_[1], kMessage, 0u);
96 ExpectMessageAndDropCount(drains_[1], kMessage, 0u);
97 ExpectMessageAndDropCount(drains_[1], kMessage, 1u);
98 ExpectMessageAndDropCount(drains_[1], {}, 1u);
99 ExpectMessageAndDropCount(drains_[1], {}, 0u);
100 }
101
TEST_F(MultiSinkTest,LateRegistration)102 TEST_F(MultiSinkTest, LateRegistration) {
103 // Confirm that entries pushed before attaching a drain are not seen by the
104 // drain.
105 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
106
107 // The drain does not observe 'drops' as it did not see entries, and only sees
108 // the one entry that was added after attach.
109 EXPECT_EQ(OkStatus(), multisink_.AttachDrain(drains_[0]));
110 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
111 ExpectMessageAndDropCount(drains_[0], kMessage, 0u);
112 ExpectMessageAndDropCount(drains_[0], {}, 0u);
113 }
114
TEST_F(MultiSinkTest,DynamicDrainRegistration)115 TEST_F(MultiSinkTest, DynamicDrainRegistration) {
116 EXPECT_EQ(OkStatus(), multisink_.AttachDrain(drains_[0]));
117
118 multisink_.HandleDropped();
119 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
120 multisink_.HandleDropped();
121 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
122
123 // Drain out one message and detach it.
124 ExpectMessageAndDropCount(drains_[0], kMessage, 1u);
125 EXPECT_EQ(OkStatus(), multisink_.DetachDrain(drains_[0]));
126
127 // Reattach the drain and confirm that you only see events after attaching.
128 EXPECT_EQ(OkStatus(), multisink_.AttachDrain(drains_[0]));
129 ExpectMessageAndDropCount(drains_[0], {}, 0u);
130
131 EXPECT_EQ(OkStatus(), multisink_.HandleEntry(kMessage));
132 ExpectMessageAndDropCount(drains_[0], kMessage, 0u);
133 ExpectMessageAndDropCount(drains_[0], {}, 0u);
134 }
135
136 } // namespace pw::multisink
137