// Copyright 2018 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/base/network_change_notifier_fuchsia.h" #include #include #include #include #include #include #include "base/auto_reset.h" #include "base/functional/bind.h" #include "base/logging.h" #include "base/run_loop.h" #include "base/test/task_environment.h" #include "base/threading/sequence_bound.h" #include "base/threading/thread.h" #include "net/base/ip_address.h" #include "net/base/network_change_notifier.h" #include "net/dns/dns_config_service.h" #include "net/dns/system_dns_config_change_notifier.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace net { namespace { enum : uint32_t { kDefaultInterfaceId = 1, kSecondaryInterfaceId = 2 }; using IPv4Octets = std::array; using IPv6Octets = std::array; constexpr IPv4Octets kDefaultIPv4Address = {192, 168, 0, 2}; constexpr uint8_t kDefaultIPv4Prefix = 16; constexpr IPv4Octets kSecondaryIPv4Address = {10, 0, 0, 1}; constexpr uint8_t kSecondaryIPv4Prefix = 8; constexpr IPv6Octets kDefaultIPv6Address = {0x20, 0x01, 0x01}; constexpr uint8_t kDefaultIPv6Prefix = 16; constexpr IPv6Octets kSecondaryIPv6Address = {0x20, 0x01, 0x02}; constexpr uint8_t kSecondaryIPv6Prefix = 16; constexpr const char kDefaultInterfaceName[] = "net1"; constexpr const char kSecondaryInterfaceName[] = "net2"; fuchsia::net::IpAddress IpAddressFrom(IPv4Octets octets) { fuchsia::net::IpAddress output; output.ipv4().addr = octets; return output; } fuchsia::net::IpAddress IpAddressFrom(IPv6Octets octets) { fuchsia::net::IpAddress output; output.ipv6().addr = octets; return output; } template fuchsia::net::Subnet SubnetFrom(T octets, uint8_t prefix) { fuchsia::net::Subnet output; output.addr = IpAddressFrom(octets); output.prefix_len = prefix; return output; } template fuchsia::net::interfaces::Address InterfaceAddressFrom(T octets, uint8_t prefix) { fuchsia::net::interfaces::Address addr; addr.set_addr(SubnetFrom(octets, prefix)); return addr; } template std::vector MakeSingleItemVec(T item) { std::vector vec; vec.push_back(std::move(item)); return vec; } fuchsia::net::interfaces::Properties DefaultInterfaceProperties( fuchsia::hardware::network::PortClass device_class = fuchsia::hardware::network::PortClass::ETHERNET) { // For most tests a live interface with an IPv4 address and ethernet class is // sufficient. fuchsia::net::interfaces::Properties interface; interface.set_id(kDefaultInterfaceId); interface.set_name(kDefaultInterfaceName); interface.set_online(true); interface.set_has_default_ipv4_route(true); interface.set_has_default_ipv6_route(true); interface.set_port_class(fuchsia::net::interfaces::PortClass::WithDevice( std::move(device_class))); interface.set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kDefaultIPv4Address, kDefaultIPv4Prefix))); return interface; } fuchsia::net::interfaces::Properties SecondaryInterfaceProperties() { // For most tests a live interface with an IPv4 address and ethernet class is // sufficient. fuchsia::net::interfaces::Properties interface; interface.set_id(kSecondaryInterfaceId); interface.set_name(kSecondaryInterfaceName); interface.set_online(true); interface.set_has_default_ipv4_route(false); interface.set_has_default_ipv6_route(false); interface.set_port_class(fuchsia::net::interfaces::PortClass::WithDevice( []() { return fuchsia::hardware::network::PortClass::ETHERNET; } ())); interface.set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix))); return interface; } template fuchsia::net::interfaces::Event MakeChangeEvent(uint64_t interface_id, F fn) { fuchsia::net::interfaces::Properties props; props.set_id(interface_id); fn(&props); return fuchsia::net::interfaces::Event::WithChanged(std::move(props)); } // Partial fake implementation of a fuchsia.net.interfaces/Watcher. class FakeWatcher : public fuchsia::net::interfaces::testing::Watcher_TestBase { public: FakeWatcher() : binding_(this) { // Always create the watcher with an empty set of interfaces. // Callers can override the initial set of events with SetInitial. pending_.push(fuchsia::net::interfaces::Event::WithIdle( fuchsia::net::interfaces::Empty{})); } FakeWatcher(const FakeWatcher&) = delete; FakeWatcher& operator=(const FakeWatcher&) = delete; ~FakeWatcher() override = default; void Bind(fidl::InterfaceRequest request) { CHECK_EQ(ZX_OK, binding_.Bind(std::move(request))); } void Unbind() { binding_.Unbind(); } void PushEvent(fuchsia::net::interfaces::Event event) { if (pending_callback_) { pending_callback_(std::move(event)); pending_callback_ = nullptr; } else { pending_.push(std::move(event)); } } void SetInitial(std::vector props) { // Discard any pending events. pending_ = std::queue(); for (auto& prop : props) { pending_.push( fuchsia::net::interfaces::Event::WithExisting(std::move(prop))); } pending_.push(fuchsia::net::interfaces::Event::WithIdle( fuchsia::net::interfaces::Empty{})); // We should not have a pending callback already when setting initial state. CHECK(!pending_callback_); } private: void Watch(WatchCallback callback) override { ASSERT_FALSE(pending_callback_); if (pending_.empty()) { pending_callback_ = std::move(callback); } else { callback(std::move(pending_.front())); pending_.pop(); } } void NotImplemented_(const std::string& name) override { LOG(FATAL) << "Unimplemented function called: " << name; } std::queue pending_; fidl::Binding binding_; WatchCallback pending_callback_ = nullptr; }; class FakeWatcherAsync { public: FakeWatcherAsync() { base::Thread::Options options(base::MessagePumpType::IO, 0); CHECK(thread_.StartWithOptions(std::move(options))); watcher_ = base::SequenceBound(thread_.task_runner()); } FakeWatcherAsync(const FakeWatcherAsync&) = delete; FakeWatcherAsync& operator=(const FakeWatcherAsync&) = delete; ~FakeWatcherAsync() = default; void Bind(fidl::InterfaceRequest request) { watcher_.AsyncCall(&FakeWatcher::Bind).WithArgs(std::move(request)); } void Unbind() { watcher_.AsyncCall(&FakeWatcher::Unbind); } // Asynchronously push an event to the watcher. void PushEvent(fuchsia::net::interfaces::Event event) { watcher_.AsyncCall(&FakeWatcher::PushEvent).WithArgs(std::move(event)); } // Asynchronously push an initial set of interfaces to the watcher. void SetInitial(std::vector props) { watcher_.AsyncCall(&FakeWatcher::SetInitial).WithArgs(std::move(props)); } // Asynchronously push an initial single intface to the watcher. void SetInitial(fuchsia::net::interfaces::Properties prop) { SetInitial(MakeSingleItemVec(std::move(prop))); } // Ensures that any PushEvent() or SetInitial() calls have // been processed. void FlushThread() { thread_.FlushForTesting(); } private: base::Thread thread_{"Watcher Thread"}; base::SequenceBound watcher_; }; template class ResultReceiver { public: ~ResultReceiver() { EXPECT_EQ(entries_.size(), 0u); } bool RunAndExpectEntries(std::vector expected_entries) { if (entries_.size() < expected_entries.size()) { base::RunLoop loop; base::AutoReset size(&expected_count_, expected_entries.size()); base::AutoReset quit(&quit_loop_, loop.QuitClosure()); loop.Run(); } return expected_entries == std::exchange(entries_, {}); } void AddEntry(T entry) { entries_.push_back(entry); if (quit_loop_ && entries_.size() >= expected_count_) std::move(quit_loop_).Run(); } protected: size_t expected_count_ = 0u; std::vector entries_; base::OnceClosure quit_loop_; }; // Accumulates the list of ConnectionTypes notified via OnConnectionTypeChanged. class FakeConnectionTypeObserver final : public NetworkChangeNotifier::ConnectionTypeObserver { public: FakeConnectionTypeObserver() { NetworkChangeNotifier::AddConnectionTypeObserver(this); } ~FakeConnectionTypeObserver() override { NetworkChangeNotifier::RemoveConnectionTypeObserver(this); } bool RunAndExpectConnectionTypes( std::vector sequence) { return receiver_.RunAndExpectEntries(sequence); } // ConnectionTypeObserver implementation. void OnConnectionTypeChanged( NetworkChangeNotifier::ConnectionType type) override { receiver_.AddEntry(type); } protected: ResultReceiver receiver_; }; // Accumulates the list of ConnectionTypes notified via OnConnectionTypeChanged. class FakeNetworkChangeObserver final : public NetworkChangeNotifier::NetworkChangeObserver { public: FakeNetworkChangeObserver() { NetworkChangeNotifier::AddNetworkChangeObserver(this); } ~FakeNetworkChangeObserver() override { NetworkChangeNotifier::RemoveNetworkChangeObserver(this); } bool RunAndExpectNetworkChanges( std::vector sequence) { return receiver_.RunAndExpectEntries(sequence); } // NetworkChangeObserver implementation. void OnNetworkChanged(NetworkChangeNotifier::ConnectionType type) override { receiver_.AddEntry(type); } protected: ResultReceiver receiver_; }; // Accumulates the list of ConnectionTypes notified via OnConnectionTypeChanged. class FakeIPAddressObserver final : public NetworkChangeNotifier::IPAddressObserver { public: FakeIPAddressObserver() { NetworkChangeNotifier::AddIPAddressObserver(this); } ~FakeIPAddressObserver() override { NetworkChangeNotifier::RemoveIPAddressObserver(this); EXPECT_EQ(ip_change_count_, 0u); } size_t ip_change_count() const { return ip_change_count_; } bool RunAndExpectCallCount(size_t expected_count) { if (ip_change_count_ < expected_count) { base::RunLoop loop; base::AutoReset expectation(&expected_count_, expected_count); base::AutoReset quit(&quit_loop_, loop.QuitClosure()); loop.Run(); } return std::exchange(ip_change_count_, 0u) == expected_count; } // IPAddressObserver implementation. void OnIPAddressChanged() override { ip_change_count_++; if (quit_loop_ && ip_change_count_ >= expected_count_) std::move(quit_loop_).Run(); } protected: size_t expected_count_ = 0u; size_t ip_change_count_ = 0u; base::OnceClosure quit_loop_; }; } // namespace class NetworkChangeNotifierFuchsiaTest : public testing::Test { public: NetworkChangeNotifierFuchsiaTest() = default; NetworkChangeNotifierFuchsiaTest(const NetworkChangeNotifierFuchsiaTest&) = delete; NetworkChangeNotifierFuchsiaTest& operator=( const NetworkChangeNotifierFuchsiaTest&) = delete; ~NetworkChangeNotifierFuchsiaTest() override = default; // Creates a NetworkChangeNotifier that binds to |watcher_|. // |observer_| is registered last, so that tests need only express // expectations on changes they make themselves. void CreateNotifier(bool require_wlan = false, bool disconnect_watcher = false) { // Ensure that internal state is up-to-date before the // notifier queries it. watcher_.FlushThread(); fidl::InterfaceHandle watcher; fidl::InterfaceRequest watcher_request = watcher.NewRequest(); if (disconnect_watcher) { // Reset the InterfaceRequest to close the `watcher` channel. watcher_request = {}; } else { watcher_.Bind(std::move(watcher_request)); } // Use a noop DNS notifier. dns_config_notifier_ = std::make_unique( nullptr /* task_runner */, nullptr /* dns_config_service */); notifier_ = base::WrapUnique(new NetworkChangeNotifierFuchsia( std::move(watcher), require_wlan, dns_config_notifier_.get())); type_observer_ = std::make_unique(); ip_observer_ = std::make_unique(); } void TearDown() override { // Spin the loops to catch any unintended notifications. watcher_.FlushThread(); base::RunLoop().RunUntilIdle(); } protected: base::test::SingleThreadTaskEnvironment task_environment_{ base::test::SingleThreadTaskEnvironment::MainThreadType::IO}; FakeWatcherAsync watcher_; // Allows us to allocate our own NetworkChangeNotifier for unit testing. NetworkChangeNotifier::DisableForTest disable_for_test_; std::unique_ptr dns_config_notifier_; std::unique_ptr notifier_; std::unique_ptr type_observer_; std::unique_ptr ip_observer_; }; TEST_F(NetworkChangeNotifierFuchsiaTest, ConnectFail_BeforeGetWatcher) { // CreateNotifier will pass an already-disconnected Watcher handle to the // new NetworkChangeNotifier, which will cause the process to exit during // construction. EXPECT_EXIT( CreateNotifier(/*require_wlan=*/false, /*disconnect_watcher=*/true), testing::ExitedWithCode(1), ""); } TEST_F(NetworkChangeNotifierFuchsiaTest, ConnectFail_AfterGetWatcher) { CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE, notifier_->GetCurrentConnectionType()); // Disconnect the Watcher protocol in-use by the NetworkChangeNotifier. watcher_.Unbind(); watcher_.FlushThread(); // Spin the loop to process the disconnection, which should terminate the // test process. EXPECT_EXIT(base::RunLoop().RunUntilIdle(), testing::ExitedWithCode(1), ""); // Teardown the notifier here to ensure it doesn't observe further events. notifier_ = nullptr; } TEST_F(NetworkChangeNotifierFuchsiaTest, InitialState) { CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE, notifier_->GetCurrentConnectionType()); } TEST_F(NetworkChangeNotifierFuchsiaTest, InterfacesChangeDuringConstruction) { // Set a live interface with an IP address. watcher_.SetInitial(DefaultInterfaceProperties( fuchsia::hardware::network::PortClass::WLAN_CLIENT)); // Inject an interfaces change event so that the notifier will receive it // immediately after the initial state. watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { props->set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix))); })); // Create the Notifier, which should process the initial network state before // returning, but not the change event, yet. CreateNotifier(); EXPECT_EQ(ip_observer_->ip_change_count(), 0u); // Now spin the loop to allow the change event to be processed, triggering a // call to the |ip_observer_|. EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, NotifyNetworkChangeOnInitialIPChange) { // Set a live interface with an IP address and create the notifier. watcher_.SetInitial(DefaultInterfaceProperties( fuchsia::hardware::network::PortClass::WLAN_CLIENT)); CreateNotifier(); // Add the NetworkChangeNotifier, and change the IP address. This should // trigger a network change notification. FakeNetworkChangeObserver network_change_observer; watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { props->set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix))); })); EXPECT_TRUE(network_change_observer.RunAndExpectNetworkChanges( {NetworkChangeNotifier::CONNECTION_NONE, NetworkChangeNotifier::CONNECTION_WIFI})); EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, NoChange) { // Set a live interface with an IP address and create the notifier. watcher_.SetInitial(DefaultInterfaceProperties()); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET, notifier_->GetCurrentConnectionType()); // Push an event with no side-effects. watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {})); } TEST_F(NetworkChangeNotifierFuchsiaTest, NoChangeV6) { auto initial = DefaultInterfaceProperties(); initial.set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix))); watcher_.SetInitial(std::move(initial)); CreateNotifier(); // Push an event with no side-effects. watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {})); } TEST_F(NetworkChangeNotifierFuchsiaTest, MultiInterfaceNoChange) { std::vector props; props.push_back(DefaultInterfaceProperties()); props.push_back(SecondaryInterfaceProperties()); watcher_.SetInitial(std::move(props)); CreateNotifier(); // Push an event with no side-effects. watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {})); } TEST_F(NetworkChangeNotifierFuchsiaTest, MultiV6IPNoChange) { auto props = DefaultInterfaceProperties(); props.mutable_addresses()->push_back( InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix)); props.mutable_addresses()->push_back( InterfaceAddressFrom(kSecondaryIPv6Address, kSecondaryIPv6Prefix)); watcher_.SetInitial(std::move(props)); CreateNotifier(); // Push an event with no side-effects. watcher_.PushEvent(MakeChangeEvent(kDefaultInterfaceId, [](auto*) {})); } TEST_F(NetworkChangeNotifierFuchsiaTest, IpChange) { watcher_.SetInitial(DefaultInterfaceProperties()); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET, notifier_->GetCurrentConnectionType()); watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { props->set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix))); })); // Expect a single OnIPAddressChanged() notification. EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, IpChangeV6) { auto props = DefaultInterfaceProperties(); props.set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix))); watcher_.SetInitial(std::move(props)); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET, notifier_->GetCurrentConnectionType()); watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { props->set_addresses(MakeSingleItemVec( InterfaceAddressFrom(kSecondaryIPv6Address, kSecondaryIPv6Prefix))); })); // Expect a single OnIPAddressChanged() notification. EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, MultiV6IPChanged) { auto props = DefaultInterfaceProperties(); props.mutable_addresses()->push_back( InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix)); watcher_.SetInitial(std::move(props)); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET, notifier_->GetCurrentConnectionType()); watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { std::vector addrs; addrs.push_back( InterfaceAddressFrom(kSecondaryIPv4Address, kSecondaryIPv4Prefix)); addrs.push_back( InterfaceAddressFrom(kSecondaryIPv6Address, kSecondaryIPv6Prefix)); props->set_addresses(std::move(addrs)); })); // Expect a single OnIPAddressChanged() notification. EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, Ipv6AdditionalIpChange) { watcher_.SetInitial(DefaultInterfaceProperties()); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET, notifier_->GetCurrentConnectionType()); watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { // Add the initial default address + a new IPv6 one. Address changes are // always sent as the entire new list of addresses. props->mutable_addresses()->push_back( InterfaceAddressFrom(kDefaultIPv4Address, kDefaultIPv4Prefix)); props->mutable_addresses()->push_back( InterfaceAddressFrom(kDefaultIPv6Address, kDefaultIPv6Prefix)); })); // Expect a single OnIPAddressChanged() notification. EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, InterfaceDown) { watcher_.SetInitial(DefaultInterfaceProperties()); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET, notifier_->GetCurrentConnectionType()); watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { props->set_online(false); })); EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes( {NetworkChangeNotifier::ConnectionType::CONNECTION_NONE})); EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, InterfaceUp) { auto props = DefaultInterfaceProperties(); props.set_online(false); watcher_.SetInitial(std::move(props)); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE, notifier_->GetCurrentConnectionType()); watcher_.PushEvent(MakeChangeEvent( kDefaultInterfaceId, [](fuchsia::net::interfaces::Properties* props) { props->set_online(true); })); EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes( {NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET})); EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, InterfaceDeleted) { watcher_.SetInitial(DefaultInterfaceProperties()); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET, notifier_->GetCurrentConnectionType()); watcher_.PushEvent( fuchsia::net::interfaces::Event::WithRemoved(kDefaultInterfaceId)); EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes( {NetworkChangeNotifier::ConnectionType::CONNECTION_NONE})); EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, InterfaceAdded) { // Initial interface list is intentionally left empty. CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE, notifier_->GetCurrentConnectionType()); watcher_.PushEvent( fuchsia::net::interfaces::Event::WithAdded(DefaultInterfaceProperties( fuchsia::hardware::network::PortClass::WLAN_CLIENT))); EXPECT_TRUE(type_observer_->RunAndExpectConnectionTypes( {NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI})); EXPECT_TRUE(ip_observer_->RunAndExpectCallCount(1)); } TEST_F(NetworkChangeNotifierFuchsiaTest, SecondaryInterfaceAddedNoop) { watcher_.SetInitial(DefaultInterfaceProperties()); CreateNotifier(); watcher_.PushEvent(fuchsia::net::interfaces::Event::WithAdded( SecondaryInterfaceProperties())); } TEST_F(NetworkChangeNotifierFuchsiaTest, SecondaryInterfaceDeletedNoop) { std::vector interfaces; interfaces.push_back(DefaultInterfaceProperties()); interfaces.push_back(SecondaryInterfaceProperties()); watcher_.SetInitial(std::move(interfaces)); CreateNotifier(); watcher_.PushEvent( fuchsia::net::interfaces::Event::WithRemoved(kSecondaryInterfaceId)); } TEST_F(NetworkChangeNotifierFuchsiaTest, FoundWiFi) { watcher_.SetInitial(DefaultInterfaceProperties( fuchsia::hardware::network::PortClass::WLAN_CLIENT)); CreateNotifier(); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, notifier_->GetCurrentConnectionType()); } TEST_F(NetworkChangeNotifierFuchsiaTest, FindsInterfaceWithRequiredWlan) { watcher_.SetInitial(DefaultInterfaceProperties( fuchsia::hardware::network::PortClass::WLAN_CLIENT)); CreateNotifier(/*require_wlan=*/true); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI, notifier_->GetCurrentConnectionType()); } TEST_F(NetworkChangeNotifierFuchsiaTest, IgnoresNonWlanInterface) { watcher_.SetInitial(DefaultInterfaceProperties()); CreateNotifier(/*require_wlan=*/true); EXPECT_EQ(NetworkChangeNotifier::ConnectionType::CONNECTION_NONE, notifier_->GetCurrentConnectionType()); } } // namespace net