| // Copyright 2018 The Fuchsia 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 "peridot/bin/ledger/p2p_provider/impl/p2p_provider_impl.h" |
| |
| #include <algorithm> |
| #include <ostream> |
| #include <string> |
| |
| #include <lib/fit/function.h> |
| |
| // gtest matchers are in gmock. |
| #include <lib/fidl/cpp/binding.h> |
| #include <lib/fxl/macros.h> |
| #include <lib/gtest/test_loop_fixture.h> |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "peridot/bin/ledger/p2p_provider/impl/static_user_id_provider.h" |
| #include "peridot/bin/ledger/p2p_provider/public/user_id_provider.h" |
| #include "peridot/bin/ledger/testing/netconnector/netconnector_factory.h" |
| |
| namespace p2p_provider { |
| namespace { |
| |
| class RecordingClient : public P2PProvider::Client { |
| public: |
| struct DeviceChange { |
| std::string device; |
| DeviceChangeType change; |
| |
| bool operator==(const DeviceChange& b) const { |
| return device == b.device && change == b.change; |
| } |
| }; |
| |
| struct Message { |
| std::string source; |
| std::string data; |
| |
| bool operator==(const Message& b) const { |
| return source == b.source && data == b.data; |
| } |
| }; |
| |
| void OnDeviceChange(fxl::StringView device_name, |
| DeviceChangeType change_type) override { |
| device_changes.push_back(DeviceChange{device_name.ToString(), change_type}); |
| } |
| |
| void OnNewMessage(fxl::StringView device_name, |
| fxl::StringView message) override { |
| messages.push_back(Message{device_name.ToString(), message.ToString()}); |
| } |
| std::vector<DeviceChange> device_changes; |
| std::vector<Message> messages; |
| }; |
| |
| std::ostream& operator<<(std::ostream& os, |
| const RecordingClient::DeviceChange& d) { |
| return os << "DeviceChange{" << d.device << ", " << bool(d.change) << "}"; |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const RecordingClient::Message& m) { |
| return os << "Message{" << m.source << ", " << m.data << "}"; |
| } |
| |
| class P2PProviderImplTest : public gtest::TestLoopFixture { |
| public: |
| P2PProviderImplTest() {} |
| ~P2PProviderImplTest() override {} |
| |
| std::unique_ptr<P2PProvider> GetProvider(std::string host_name, |
| std::string user_name = "user") { |
| fuchsia::netconnector::NetConnectorPtr netconnector; |
| net_connector_factory_.AddBinding(host_name, netconnector.NewRequest()); |
| return std::make_unique<p2p_provider::P2PProviderImpl>( |
| std::move(host_name), std::move(netconnector), |
| std::make_unique<StaticUserIdProvider>(std::move(user_name))); |
| } |
| |
| protected: |
| void SetUp() override { ::testing::Test::SetUp(); } |
| |
| ledger::NetConnectorFactory net_connector_factory_; |
| |
| private: |
| FXL_DISALLOW_COPY_AND_ASSIGN(P2PProviderImplTest); |
| }; |
| |
| TEST_F(P2PProviderImplTest, ThreeHosts_SameUser) { |
| std::unique_ptr<P2PProvider> provider1 = GetProvider("host1"); |
| RecordingClient client1; |
| provider1->Start(&client1); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(client1.device_changes.empty()); |
| |
| std::unique_ptr<P2PProvider> provider2 = GetProvider("host2"); |
| RecordingClient client2; |
| provider2->Start(&client2); |
| RunLoopUntilIdle(); |
| |
| EXPECT_THAT(client1.device_changes, |
| testing::UnorderedElementsAre(RecordingClient::DeviceChange{ |
| "host2", DeviceChangeType::NEW})); |
| EXPECT_THAT(client2.device_changes, |
| testing::UnorderedElementsAre(RecordingClient::DeviceChange{ |
| "host1", DeviceChangeType::NEW})); |
| |
| std::unique_ptr<P2PProvider> provider3 = GetProvider("host3"); |
| RecordingClient client3; |
| provider3->Start(&client3); |
| RunLoopUntilIdle(); |
| |
| EXPECT_THAT( |
| client1.device_changes, |
| testing::ElementsAre( |
| RecordingClient::DeviceChange{"host2", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host3", DeviceChangeType::NEW})); |
| |
| EXPECT_THAT( |
| client2.device_changes, |
| testing::ElementsAre( |
| RecordingClient::DeviceChange{"host1", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host3", DeviceChangeType::NEW})); |
| |
| EXPECT_THAT( |
| client3.device_changes, |
| testing::UnorderedElementsAre( |
| RecordingClient::DeviceChange{"host1", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host2", DeviceChangeType::NEW})); |
| |
| // Disconnect one host, and verify disconnection notices are sent. |
| provider2.reset(); |
| RunLoopUntilIdle(); |
| |
| EXPECT_THAT( |
| client1.device_changes, |
| testing::ElementsAre( |
| RecordingClient::DeviceChange{"host2", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host3", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host2", DeviceChangeType::DELETED})); |
| |
| EXPECT_THAT( |
| client3.device_changes, |
| testing::UnorderedElementsAre( |
| RecordingClient::DeviceChange{"host1", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host2", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host2", DeviceChangeType::DELETED})); |
| } |
| |
| TEST_F(P2PProviderImplTest, FourHosts_TwoUsers) { |
| std::unique_ptr<P2PProvider> provider1 = GetProvider("host1", "user1"); |
| RecordingClient client1; |
| provider1->Start(&client1); |
| std::unique_ptr<P2PProvider> provider2 = GetProvider("host2", "user2"); |
| RecordingClient client2; |
| provider2->Start(&client2); |
| std::unique_ptr<P2PProvider> provider3 = GetProvider("host3", "user2"); |
| RecordingClient client3; |
| provider3->Start(&client3); |
| std::unique_ptr<P2PProvider> provider4 = GetProvider("host4", "user1"); |
| RecordingClient client4; |
| provider4->Start(&client4); |
| RunLoopUntilIdle(); |
| |
| // Verify that only devices with the same user connect together. |
| EXPECT_THAT(client1.device_changes, |
| testing::ElementsAre(RecordingClient::DeviceChange{ |
| "host4", DeviceChangeType::NEW})); |
| EXPECT_THAT(client2.device_changes, |
| testing::ElementsAre(RecordingClient::DeviceChange{ |
| "host3", DeviceChangeType::NEW})); |
| EXPECT_THAT(client3.device_changes, |
| testing::ElementsAre(RecordingClient::DeviceChange{ |
| "host2", DeviceChangeType::NEW})); |
| EXPECT_THAT(client4.device_changes, |
| testing::ElementsAre(RecordingClient::DeviceChange{ |
| "host1", DeviceChangeType::NEW})); |
| |
| provider4.reset(); |
| RunLoopUntilIdle(); |
| |
| EXPECT_THAT( |
| client1.device_changes, |
| testing::ElementsAre( |
| RecordingClient::DeviceChange{"host4", DeviceChangeType::NEW}, |
| RecordingClient::DeviceChange{"host4", DeviceChangeType::DELETED})); |
| EXPECT_THAT(client2.device_changes, |
| testing::ElementsAre(RecordingClient::DeviceChange{ |
| "host3", DeviceChangeType::NEW})); |
| EXPECT_THAT(client3.device_changes, |
| testing::ElementsAre(RecordingClient::DeviceChange{ |
| "host2", DeviceChangeType::NEW})); |
| } |
| |
| TEST_F(P2PProviderImplTest, TwoHosts_Messages) { |
| std::unique_ptr<P2PProvider> provider1 = GetProvider("host1"); |
| RecordingClient client1; |
| provider1->Start(&client1); |
| std::unique_ptr<P2PProvider> provider2 = GetProvider("host2"); |
| RecordingClient client2; |
| provider2->Start(&client2); |
| |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(provider1->SendMessage("host2", "datagram")); |
| RunLoopUntilIdle(); |
| |
| EXPECT_THAT(client1.messages, testing::ElementsAre()); |
| EXPECT_THAT(client2.messages, testing::ElementsAre(RecordingClient::Message{ |
| "host1", "datagram"})); |
| } |
| } // namespace |
| } // namespace p2p_provider |