/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * Copyright the Collabora Online contributors. * * SPDX-License-Identifier: MPL-2.0 * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef MOBILEAPP #define MOBILEAPP 1 // A bit ugly, but currently FakeSocket.hpp is surrounded by a MOBILEAPP ifdef, // and probably it is not a good idea to remove that? #include "FakeSocket.hpp" bool EnableExperimental = false; class FakeSocketTest : public CPPUNIT_NS::TestFixture { CPPUNIT_TEST_SUITE(FakeSocketTest); CPPUNIT_TEST(testBasic); CPPUNIT_TEST_SUITE_END(); void testBasic(); public: FakeSocketTest() { } void setUp() { fakeSocketSetLoggingCallback([](const std::string& line) { std::cerr << line << '\n'; }); } void tearDown() { } }; void FakeSocketTest::testBasic() { constexpr auto testname = __func__; int rc; char buf[100]; // First check invalid fds. rc = fakeSocketListen(10); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EBADF); rc = fakeSocketWrite(20, "hah", 3); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EBADF); rc = fakeSocketRead(30, buf, 3); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EBADF); // Create three sockets: s0, s1 and s2. int s0 = fakeSocketSocket(); LOK_ASSERT(s0 >= 0); int s1 = fakeSocketSocket(); LOK_ASSERT(s1 >= 0); int s2 = fakeSocketSocket(); LOK_ASSERT(s2 >= 0); LOK_ASSERT(s0 != s1); LOK_ASSERT(s1 != s2); // Close s1 and create it anew fakeSocketClose(s1); s1 = fakeSocketSocket(); LOK_ASSERT(s1 >= 0); // Listen on s0 rc = fakeSocketListen(s0); LOK_ASSERT(rc != -1); // Start a thread that accepts two connections to s0, producing sockets s3 and s4. int s3 = -1, s4 = -1; std::thread t0([&] { // Cannot use LOK_ASSERT here as that throws and this thread has no Cppunit // exception handler. We check below after joining this thread. s3 = fakeSocketAccept4(s0); s4 = fakeSocketAccept4(s0); }); // Connect s1 and s2 to s0 (that is, to the sockets produced by accepting connections to // s0). rc = fakeSocketConnect(s1, s0); LOK_ASSERT(rc != -1); rc = fakeSocketConnect(s2, s0); LOK_ASSERT(rc != -1); // Verify that we got the accepts. t0.join(); LOK_ASSERT(s3 != -1); LOK_ASSERT(s4 != -1); // s1 should now be connected to s3, and s2 to s4. LOK_ASSERT(fakeSocketPeer(s1) == s3); LOK_ASSERT(fakeSocketPeer(s3) == s1); LOK_ASSERT(fakeSocketPeer(s2) == s4); LOK_ASSERT(fakeSocketPeer(s4) == s2); // Some writing and reading rc = fakeSocketWrite(s1, "hello", 5); LOK_ASSERT(rc != -1); rc = fakeSocketWrite(s1, "greetings", 9); LOK_ASSERT(rc != -1); rc = fakeSocketWrite(s2, "moin", 4); LOK_ASSERT(rc != -1); rc = fakeSocketAvailableDataLength(s3); LOK_ASSERT(rc == 5); rc = fakeSocketRead(s3, buf, 100); LOK_ASSERT(rc == 5); rc = fakeSocketAvailableDataLength(s3); LOK_ASSERT(rc == 9); rc = fakeSocketRead(s4, buf, 100); LOK_ASSERT(rc == 4); rc = fakeSocketWrite(s3, "goodbye", 7); LOK_ASSERT(rc > 0); rc = fakeSocketRead(s1, buf, 4); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EAGAIN); // Note: not really the right errno, but what else? See // FakeSocket.cpp. rc = fakeSocketRead(s1, buf, 100); LOK_ASSERT(rc > 0); // Close s3. Reading from s1 should then return an EOF indication (0). fakeSocketClose(s3); rc = fakeSocketAvailableDataLength(s1); LOK_ASSERT(rc == 0); rc = fakeSocketRead(s1, buf, 100); LOK_ASSERT(rc == 0); rc = fakeSocketAvailableDataLength(s1); LOK_ASSERT(rc == 0); rc = fakeSocketRead(s1, buf, 100); LOK_ASSERT(rc == 0); // Test the "pipe" functionality, that creates an already connected socket pair. int pipe[2]; rc = fakeSocketPipe2(pipe); LOK_ASSERT(rc == 0); rc = fakeSocketWrite(pipe[0], "x", 1); LOK_ASSERT(rc == 1); rc = fakeSocketAvailableDataLength(pipe[1]); LOK_ASSERT(rc == 1); rc = fakeSocketRead(pipe[1], buf, 1); LOK_ASSERT(rc == 1); LOK_ASSERT(buf[0] == 'x'); rc = fakeSocketWrite(pipe[1], "y", 1); LOK_ASSERT(rc == 1); rc = fakeSocketRead(pipe[0], buf, 1); LOK_ASSERT(rc == 1); LOK_ASSERT(buf[0] == 'y'); rc = fakeSocketWrite(pipe[0], "z", 1); LOK_ASSERT(rc == 1); rc = fakeSocketShutdown(pipe[0]); LOK_ASSERT(rc == 0); rc = fakeSocketRead(pipe[1], buf, 1); LOK_ASSERT(rc == 1); LOK_ASSERT(buf[0] == 'z'); rc = fakeSocketWrite(pipe[0], "a", 1); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EPIPE); rc = fakeSocketRead(pipe[0], buf, 1); LOK_ASSERT(rc == 0); rc = fakeSocketRead(pipe[0], buf, 1); LOK_ASSERT(rc == 0); rc = fakeSocketClose(pipe[0]); LOK_ASSERT(rc == 0); rc = fakeSocketClose(pipe[0]); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EBADF); rc = fakeSocketClose(pipe[1]); LOK_ASSERT(rc == 0); rc = fakeSocketClose(pipe[1]); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EBADF); // Create a pipe again. rc = fakeSocketPipe2(pipe); LOK_ASSERT(rc == 0); rc = fakeSocketAvailableDataLength(pipe[0]); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EAGAIN); rc = fakeSocketAvailableDataLength(pipe[1]); LOK_ASSERT(rc == -1); LOK_ASSERT(errno == EAGAIN); // Test poll functionality. struct pollfd pollfds[4]; pollfds[0].fd = s0; pollfds[0].events = POLLIN | POLLOUT; pollfds[1].fd = s1; pollfds[1].events = POLLIN | POLLOUT; pollfds[2].fd = s2; pollfds[2].events = POLLIN | POLLOUT; pollfds[3].fd = 40; pollfds[3].events = POLLIN | POLLOUT; rc = fakeSocketPoll(pollfds, 4, -1); // Hmm, does a real poll() set POLLIN for a listening socket? Probably only if there is a // connection in progress, and that is not the case here for s0. LOK_ASSERT(rc == 3); LOK_ASSERT(pollfds[0].revents == 0); LOK_ASSERT(pollfds[1].revents == POLLIN); LOK_ASSERT(pollfds[2].revents == POLLOUT); LOK_ASSERT(pollfds[3].revents == POLLNVAL); } CPPUNIT_TEST_SUITE_REGISTRATION(FakeSocketTest); int main(int, char**) { const char* envar = std::getenv("CPPUNIT_TEST_NAME"); std::string testName; if (envar) { testName = std::string(envar); } if (!testName.empty() && testName != "FakeSocketTest") { return 0; } CPPUNIT_NS::TestResult controller; CPPUNIT_NS::TestResultCollector result; controller.addListener(&result); CPPUNIT_NS::BriefTestProgressListener progress; controller.addListener(&progress); CPPUNIT_NS::TextTestProgressListener listener; controller.addListener(&listener); CPPUNIT_NS::Test* testRegistry = CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest(); CPPUNIT_NS::TestRunner runner; runner.addTest(testRegistry); runner.run(controller); CPPUNIT_NS::CompilerOutputter outputter(&result, std::cerr); outputter.setNoWrap(); outputter.write(); fakeSocketDumpState(); return result.wasSuccessful() ? 0 : 1; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */