// Copyright 2021 The Pigweed Authors // // Licensed under the Apache License, Version 2.0 (the "License"); you may not // use this file except in compliance with the License. You may obtain a copy of // the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations under // the License. #include "pw_router/static_router.h" #include "gtest/gtest.h" #include "pw_assert/check.h" #include "pw_router/egress_function.h" namespace pw::router { namespace { struct BasicPacket { static constexpr uint32_t kMagic = 0x8badf00d; constexpr BasicPacket(uint32_t addr, uint64_t data) : magic(kMagic), address(addr), priority(0), payload(data) {} constexpr BasicPacket(uint32_t addr, uint32_t prio, uint64_t data) : magic(kMagic), address(addr), priority(prio), payload(data) {} ConstByteSpan data() const { return std::as_bytes(std::span(this, 1)); } uint32_t magic; uint32_t address; uint32_t priority; uint64_t payload; }; class BasicPacketParser : public PacketParser { public: constexpr BasicPacketParser() : packet_(nullptr) {} bool Parse(pw::ConstByteSpan packet) final { packet_ = reinterpret_cast(packet.data()); return packet_->magic == BasicPacket::kMagic; } std::optional GetDestinationAddress() const final { PW_DCHECK_NOTNULL(packet_); return packet_->address; } uint32_t priority() const { PW_DCHECK_NOTNULL(packet_); return packet_->priority; }; private: const BasicPacket* packet_; }; EgressFunction GoodEgress(+[](ConstByteSpan, const PacketParser&) { return OkStatus(); }); EgressFunction BadEgress(+[](ConstByteSpan, const PacketParser&) { return Status::ResourceExhausted(); }); TEST(StaticRouter, RoutePacket_RoutesToAnEgress) { BasicPacketParser parser; constexpr StaticRouter::Route routes[] = {{1, GoodEgress}, {2, BadEgress}}; StaticRouter router(routes); EXPECT_EQ(router.RoutePacket(BasicPacket(1, 0xdddd).data(), parser), OkStatus()); EXPECT_EQ(router.RoutePacket(BasicPacket(2, 0xdddd).data(), parser), Status::Unavailable()); } TEST(StaticRouter, RoutePacket_ForwardsPacketParser) { uint32_t parser_priority = 0xffffffff; EgressFunction parser_egress( [&parser_priority](ConstByteSpan, const PacketParser& parser) { const BasicPacketParser& basic_parser = static_cast(parser); parser_priority = basic_parser.priority(); return OkStatus(); }); StaticRouter::Route routes[] = {{1, parser_egress}}; StaticRouter router(routes); BasicPacketParser parser; EXPECT_EQ(router.RoutePacket(BasicPacket(1, 71, 0xdddd).data(), parser), OkStatus()); EXPECT_EQ(parser_priority, 71u); } TEST(StaticRouter, RoutePacket_ReturnsParserError) { BasicPacketParser parser; constexpr StaticRouter::Route routes[] = {{1, GoodEgress}, {2, BadEgress}}; StaticRouter router(routes); BasicPacket bad_magic(1, 0xdddd); bad_magic.magic = 0x1badda7a; EXPECT_EQ(router.RoutePacket(bad_magic.data(), parser), Status::DataLoss()); } TEST(StaticRouter, RoutePacket_ReturnsNotFoundOnInvalidRoute) { BasicPacketParser parser; constexpr StaticRouter::Route routes[] = {{1, GoodEgress}, {2, BadEgress}}; StaticRouter router(routes); EXPECT_EQ(router.RoutePacket(BasicPacket(42, 0xdddd).data(), parser), Status::NotFound()); } TEST(StaticRouter, RoutePacket_TracksNumberOfDrops) { BasicPacketParser parser; constexpr StaticRouter::Route routes[] = {{1, GoodEgress}, {2, BadEgress}}; StaticRouter router(routes); // Good EXPECT_EQ(router.RoutePacket(BasicPacket(1, 0xdddd).data(), parser), OkStatus()); // Egress error EXPECT_EQ(router.RoutePacket(BasicPacket(2, 0xdddd).data(), parser), Status::Unavailable()); // Parser error BasicPacket bad_magic(1, 0xdddd); bad_magic.magic = 0x1badda7a; EXPECT_EQ(router.RoutePacket(bad_magic.data(), parser), Status::DataLoss()); // Good EXPECT_EQ(router.RoutePacket(BasicPacket(1, 0xdddd).data(), parser), OkStatus()); // Bad route EXPECT_EQ(router.RoutePacket(BasicPacket(42, 0xdddd).data(), parser), Status::NotFound()); EXPECT_EQ(router.dropped_packets(), 3u); } } // namespace } // namespace pw::router