// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "platform/impl/udp_socket_reader_posix.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "platform/api/time.h" #include "platform/impl/socket_handle_posix.h" #include "platform/impl/udp_socket_posix.h" #include "platform/test/fake_clock.h" #include "platform/test/fake_task_runner.h" #include "platform/test/fake_udp_socket.h" namespace openscreen { namespace { using namespace ::testing; using ::testing::_; using ::testing::Invoke; class MockUdpSocketPosix : public UdpSocketPosix { public: explicit MockUdpSocketPosix(TaskRunner* task_runner, Client* client, int fd, Version version = Version::kV4) : UdpSocketPosix(task_runner, client, SocketHandle(fd), IPEndpoint()), version_(version) {} ~MockUdpSocketPosix() override = default; bool IsIPv4() const override { return version_ == UdpSocket::Version::kV4; } bool IsIPv6() const override { return version_ == UdpSocket::Version::kV6; } MOCK_METHOD0(Bind, void()); MOCK_METHOD1(SetMulticastOutboundInterface, void(NetworkInterfaceIndex)); MOCK_METHOD2(JoinMulticastGroup, void(const IPAddress&, NetworkInterfaceIndex)); MOCK_METHOD3(SendMessage, void(const void*, size_t, const IPEndpoint&)); MOCK_METHOD1(SetDscp, void(DscpMode)); private: Version version_; }; // Mock event waiter class MockNetworkWaiter final : public SocketHandleWaiter { public: using ReadyHandle = SocketHandleWaiter::ReadyHandle; MockNetworkWaiter() : SocketHandleWaiter(&FakeClock::now) {} ~MockNetworkWaiter() override = default; MOCK_METHOD2( AwaitSocketsReadable, ErrorOr>(const std::vector&, const Clock::duration&)); FakeClock fake_clock{Clock::time_point{Clock::duration{1234567}}}; }; // Mock Task Runner class MockTaskRunner final : public TaskRunner { public: MockTaskRunner() { tasks_posted = 0; delayed_tasks_posted = 0; } void PostPackagedTask(Task t) override { tasks_posted++; t(); } void PostPackagedTaskWithDelay(Task t, Clock::duration duration) override { delayed_tasks_posted++; t(); } bool IsRunningOnTaskRunner() override { return true; } uint32_t tasks_posted; uint32_t delayed_tasks_posted; }; } // namespace // Class extending NetworkWaiter to allow for looking at protected data. class TestingUdpSocketReader final : public UdpSocketReaderPosix { public: explicit TestingUdpSocketReader(SocketHandleWaiter* waiter) : UdpSocketReaderPosix(waiter) {} void OnDestroy(UdpSocket* socket) override { OnDelete(static_cast(socket), true); } bool IsMappedRead(UdpSocketPosix* socket) const { return IsMappedReadForTesting(socket); } }; TEST(UdpSocketReaderTest, WatchReadableSucceeds) { std::unique_ptr mock_waiter = std::unique_ptr(new MockNetworkWaiter()); std::unique_ptr task_runner = std::unique_ptr(new MockTaskRunner()); FakeUdpSocket::MockClient client; std::unique_ptr socket = std::make_unique(task_runner.get(), &client, 42, UdpSocket::Version::kV4); } TEST(UdpSocketReaderTest, UnwatchReadableSucceeds) { std::unique_ptr mock_waiter = std::unique_ptr(new MockNetworkWaiter()); std::unique_ptr task_runner = std::unique_ptr(new MockTaskRunner()); FakeUdpSocket::MockClient client; std::unique_ptr socket = std::make_unique(task_runner.get(), &client, 17, UdpSocket::Version::kV4); TestingUdpSocketReader network_waiter(mock_waiter.get()); EXPECT_FALSE(network_waiter.IsMappedRead(socket.get())); network_waiter.OnDestroy(socket.get()); EXPECT_FALSE(network_waiter.IsMappedRead(socket.get())); network_waiter.OnCreate(socket.get()); EXPECT_TRUE(network_waiter.IsMappedRead(socket.get())); network_waiter.OnDestroy(socket.get()); EXPECT_FALSE(network_waiter.IsMappedRead(socket.get())); network_waiter.OnDestroy(socket.get()); EXPECT_FALSE(network_waiter.IsMappedRead(socket.get())); } } // namespace openscreen