From 94d44ada63e217ae3d2a43a612622a0c46642206 Mon Sep 17 00:00:00 2001 From: "Daniel R. Carvalho" Date: Fri, 12 Mar 2021 21:35:19 -0300 Subject: [PATCH] sim: Add unit test for sim/port Add a unit test for sim/port. The fact that binding a port does not automatically bind its peer is error-prone, and should likely be revisited in the future. Change-Id: Iee91fc9bfa80a527d9a1902529722833b061dec9 Signed-off-by: Daniel R. Carvalho Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/43003 Tested-by: kokoro Reviewed-by: Bobby R. Bruce Maintainer: Bobby R. Bruce --- src/sim/SConscript | 1 + src/sim/port.hh | 2 + src/sim/port.test.cc | 239 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 src/sim/port.test.cc diff --git a/src/sim/SConscript b/src/sim/SConscript index cf51fb5112..20f8d6b2dd 100644 --- a/src/sim/SConscript +++ b/src/sim/SConscript @@ -89,6 +89,7 @@ Source('mem_pool.cc') GTest('byteswap.test', 'byteswap.test.cc', '../base/types.cc') GTest('guest_abi.test', 'guest_abi.test.cc') +GTest('port.test', 'port.test.cc', 'port.cc') GTest('proxy_ptr.test', 'proxy_ptr.test.cc') if env['TARGET_ISA'] != 'null': diff --git a/src/sim/port.hh b/src/sim/port.hh index 85472d09bb..f26566683b 100644 --- a/src/sim/port.hh +++ b/src/sim/port.hh @@ -46,6 +46,8 @@ #ifndef __SIM_PORT_HH__ #define __SIM_PORT_HH__ +#include +#include #include #include "base/types.hh" diff --git a/src/sim/port.test.cc b/src/sim/port.test.cc new file mode 100644 index 0000000000..25436733d1 --- /dev/null +++ b/src/sim/port.test.cc @@ -0,0 +1,239 @@ +/* + * Copyright 2021 Daniel R. Carvalho + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer; + * redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution; + * neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "base/gtest/logging.hh" +#include "sim/port.hh" + +class TestPort : public Port +{ + public: + TestPort(PortID _id) : Port("TestPort", _id) {} +}; + +/** Test getting the port id. */ +TEST(PortTest, GetId) +{ + TestPort port(0), port2(2); + ASSERT_EQ(port.getId(), 0); + ASSERT_EQ(port2.getId(), 2); +} + +/** Test connecting one of the ports. */ +TEST(PortTest, OneSidedConnection) +{ + TestPort port(0), port2(2); + + // Both ports start unconnected + ASSERT_FALSE(port.isConnected()); + ASSERT_FALSE(port2.isConnected()); + + // Bind one of the ports to the other + port.bind(port2); + + // Binding a single side does not bind both ports automatically + ASSERT_TRUE(port.isConnected()); + ASSERT_EQ(&port.getPeer(), &port2); + ASSERT_FALSE(port2.isConnected()); + + // Unbind the port + port.unbind(); + ASSERT_FALSE(port.isConnected()); + ASSERT_FALSE(port2.isConnected()); +} + +/** Test connecting both ports. */ +TEST(PortTest, TwoSidedConnection) +{ + TestPort port(0), port2(2); + + // Both ports start unconnected + ASSERT_FALSE(port.isConnected()); + ASSERT_FALSE(port2.isConnected()); + + // Bind the ports + port.bind(port2); + port2.bind(port); + ASSERT_TRUE(port.isConnected()); + ASSERT_EQ(&port.getPeer(), &port2); + ASSERT_TRUE(port2.isConnected()); + ASSERT_EQ(&port2.getPeer(), &port); + + // Unbinding one port does not automatically unbind the other + port.unbind(); + ASSERT_FALSE(port.isConnected()); + ASSERT_TRUE(port2.isConnected()); + ASSERT_EQ(&port2.getPeer(), &port); + + // Finish unbinding + port2.unbind(); + ASSERT_FALSE(port.isConnected()); + ASSERT_FALSE(port2.isConnected()); +} + +/** Test that manually overwriting a bind is possible. */ +TEST(PortTest, OverwriteConnection) +{ + TestPort port(0), port2(2), port3(6); + + // All ports start unconnected + ASSERT_FALSE(port.isConnected()); + ASSERT_FALSE(port2.isConnected()); + ASSERT_FALSE(port3.isConnected()); + + // Bind one of the ports to the other + port.bind(port2); + ASSERT_TRUE(port.isConnected()); + ASSERT_EQ(&port.getPeer(), &port2); + ASSERT_FALSE(port2.isConnected()); + ASSERT_FALSE(port3.isConnected()); + + // Bind one of the ports to a third + port.bind(port3); + ASSERT_TRUE(port.isConnected()); + ASSERT_EQ(&port.getPeer(), &port3); + ASSERT_FALSE(port2.isConnected()); + ASSERT_FALSE(port3.isConnected()); +} + +/** Test that a take over must have a valid port. */ +TEST(PortDeathTest, TakeOverNoPort) +{ +#ifdef NDEBUG + GTEST_SKIP() << "Skipping as assertions are " + "stripped out of fast builds"; +#endif + TestPort port(0); + ASSERT_DEATH(port.takeOverFrom(nullptr), ""); +} + +/** Test that a port that is not connected cannot be taken over from. */ +TEST(PortDeathTest, TakeOverDisconnected) +{ +#ifdef NDEBUG + GTEST_SKIP() << "Skipping as assertions are " + "stripped out of fast builds"; +#endif + TestPort port(0), port2(2); + ASSERT_DEATH(port.takeOverFrom(&port2), ""); +} + +/** + * Test that a port that is already connected cannot take over another port. + * + * Before the take over the connections are: port -> port2 + */ +TEST(PortDeathTest, TakeOverConnected) +{ +#ifdef NDEBUG + GTEST_SKIP() << "Skipping as assertions are " + "stripped out of fast builds"; +#endif + TestPort port(0), port2(2); + port.bind(port2); + ASSERT_DEATH(port.takeOverFrom(&port2), ""); +} + +/** + * Test that the peer of the port being taken over from must also be connected. + * + * Before the take over the connections are: port2 -> port3 + */ +TEST(PortDeathTest, TakeOverOneSided) +{ +#ifdef NDEBUG + GTEST_SKIP() << "Skipping as assertions are " + "stripped out of fast builds"; +#endif + TestPort port(0), port2(2), port3(6); + port2.bind(port3); + ASSERT_DEATH(port.takeOverFrom(&port2), ""); +} + +/** + * Test one-sided take over. This might be a bug caused by the fact that a + * bind() does not automatically bind both ports. + * + * Before the take over the connections are: port2 -> port3 -> port4 + * After the take over the connections are: port <-> port3 + */ +TEST(PortTest, TakeOverOneSided) +{ + TestPort port(0), port2(2), port3(6), port4(10); + port2.bind(port3); + port3.bind(port4); + port.takeOverFrom(&port2); + + ASSERT_TRUE(port.isConnected()); + ASSERT_EQ(&port.getPeer(), &port3); + ASSERT_FALSE(port2.isConnected()); + ASSERT_TRUE(port3.isConnected()); + ASSERT_EQ(&port3.getPeer(), &port); + ASSERT_FALSE(port4.isConnected()); +} + +/** + * Test proper take over. + * + * Before the take over the connections are: port2 <-> port3 + * After the take over the connections are: port <-> port3 +*/ +TEST(PortTest, TakeOver) +{ + TestPort port(0), port2(2), port3(6); + port2.bind(port3); + port3.bind(port2); + port.takeOverFrom(&port2); + + ASSERT_TRUE(port.isConnected()); + ASSERT_EQ(&port.getPeer(), &port3); + ASSERT_TRUE(port3.isConnected()); + ASSERT_EQ(&port3.getPeer(), &port); +} + +/** Test that the ostream operator prints the port's name. */ +TEST(PortTest, Print) +{ + // Temporarily redirect cout + std::streambuf *old; + std::ostringstream buffer; + old = std::cout.rdbuf(buffer.rdbuf()); + + // Must use EXPECT, so that cout is restored + TestPort port(0); + std::cout << port << std::endl; + EXPECT_EQ(buffer.str(), "TestPort\n"); + + // Restore cout's streambuf + std::cout.rdbuf(old); +}