blob: 553e58bd63add1acc7af40f9337bb52608ee3650 [file] [log] [blame]
// Copyright 2021 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 "net/dns/dns_config_service_android.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/android/build_info.h"
#include "base/bind.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/task_environment.h"
#include "net/android/network_library.h"
#include "net/base/ip_endpoint.h"
#include "net/base/mock_network_change_notifier.h"
#include "net/base/network_change_notifier.h"
#include "net/dns/dns_config.h"
#include "net/test/test_with_task_environment.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace net {
namespace internal {
namespace {
const IPEndPoint kNameserver1(IPAddress(1, 2, 3, 4), 53);
const IPEndPoint kNameserver2(IPAddress(1, 2, 3, 8), 53);
// DnsConfigServiceAndroid uses a simplified implementation for Android versions
// before relevant APIs were added in Android M. Most of these tests are
// targeting the logic used in M and beyond.
#define SKIP_ANDROID_VERSIONS_BEFORE_M() \
{ \
if (base::android::BuildInfo::GetInstance()->sdk_int() < \
base::android::SDK_VERSION_MARSHMALLOW) { \
GTEST_SKIP() << "Test not necessary or compatible with pre-M."; \
} \
}
// RefCountedThreadSafe to allow safe usage and reference storage in
// DnsConfigServiceAndroid's off-sequence utility classes.
class MockDnsServerGetter
: public base::RefCountedThreadSafe<MockDnsServerGetter> {
public:
void set_retval(bool retval) { retval_ = retval; }
void set_dns_servers(std::vector<IPEndPoint> dns_servers) {
dns_servers_ = std::move(dns_servers);
}
void set_dns_over_tls_active(bool dns_over_tls_active) {
dns_over_tls_active_ = dns_over_tls_active;
}
void set_dns_over_tls_hostname(std::string dns_over_tls_hostname) {
dns_over_tls_hostname_ = std::move(dns_over_tls_hostname);
}
void set_search_suffixes(std::vector<std::string> search_suffixes) {
search_suffixes_ = std::move(search_suffixes);
}
android::DnsServerGetter ConstructGetter() {
return base::BindRepeating(&MockDnsServerGetter::GetDnsServers, this);
}
private:
friend base::RefCountedThreadSafe<MockDnsServerGetter>;
~MockDnsServerGetter() = default;
bool GetDnsServers(std::vector<IPEndPoint>* dns_servers,
bool* dns_over_tls_active,
std::string* dns_over_tls_hostname,
std::vector<std::string>* search_suffixes) {
if (retval_) {
*dns_servers = dns_servers_;
*dns_over_tls_active = dns_over_tls_active_;
*dns_over_tls_hostname = dns_over_tls_hostname_;
*search_suffixes = search_suffixes_;
}
return retval_;
}
bool retval_ = false;
std::vector<IPEndPoint> dns_servers_;
bool dns_over_tls_active_ = false;
std::string dns_over_tls_hostname_;
std::vector<std::string> search_suffixes_;
};
class DnsConfigServiceAndroidTest : public testing::Test,
public WithTaskEnvironment {
public:
DnsConfigServiceAndroidTest()
: WithTaskEnvironment(
base::test::TaskEnvironment::TimeSource::MOCK_TIME) {
service_->set_dns_server_getter_for_testing(
mock_dns_server_getter_->ConstructGetter());
}
~DnsConfigServiceAndroidTest() override = default;
void OnConfigChanged(const DnsConfig& config) {
EXPECT_TRUE(config.IsValid());
seen_config_ = true;
real_config_ = config;
}
protected:
bool seen_config_ = false;
std::unique_ptr<DnsConfigServiceAndroid> service_ =
std::make_unique<DnsConfigServiceAndroid>();
DnsConfig real_config_;
scoped_refptr<MockDnsServerGetter> mock_dns_server_getter_ =
base::MakeRefCounted<MockDnsServerGetter>();
test::ScopedMockNetworkChangeNotifier mock_notifier_;
};
TEST_F(DnsConfigServiceAndroidTest, HandlesNetworkChangeNotifications) {
service_->WatchConfig(base::BindRepeating(
&DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this)));
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
// Cannot validate any behavior other than not crashing because this test runs
// on Android versions with unmocked behavior.
}
TEST_F(DnsConfigServiceAndroidTest, NewConfigReadOnNetworkChange) {
SKIP_ANDROID_VERSIONS_BEFORE_M();
mock_dns_server_getter_->set_retval(true);
mock_dns_server_getter_->set_dns_servers({kNameserver1});
service_->WatchConfig(base::BindRepeating(
&DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this)));
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1));
mock_dns_server_getter_->set_dns_servers({kNameserver2});
seen_config_ = false;
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
NetworkChangeNotifier::CONNECTION_WIFI);
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver2));
}
TEST_F(DnsConfigServiceAndroidTest, NoConfigNotificationWhenUnchanged) {
SKIP_ANDROID_VERSIONS_BEFORE_M();
mock_dns_server_getter_->set_retval(true);
mock_dns_server_getter_->set_dns_servers({kNameserver1});
service_->WatchConfig(base::BindRepeating(
&DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this)));
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1));
seen_config_ = false;
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
NetworkChangeNotifier::CONNECTION_WIFI);
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
// Because the DNS config hasn't changed, no new config should be seen.
EXPECT_FALSE(seen_config_);
}
TEST_F(DnsConfigServiceAndroidTest, IgnoresConnectionNoneChangeNotifications) {
SKIP_ANDROID_VERSIONS_BEFORE_M();
mock_dns_server_getter_->set_retval(true);
mock_dns_server_getter_->set_dns_servers({kNameserver1});
service_->WatchConfig(base::BindRepeating(
&DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this)));
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1));
// Change the DNS config to ensure the lack of notification is due to not
// being checked for.
mock_dns_server_getter_->set_dns_servers({kNameserver2});
seen_config_ = false;
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
NetworkChangeNotifier::CONNECTION_NONE);
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
// Expect no new config read for network change to NONE.
EXPECT_FALSE(seen_config_);
}
// Regression test for https://crbug.com/704662.
TEST_F(DnsConfigServiceAndroidTest, ChangeConfigMultipleTimes) {
SKIP_ANDROID_VERSIONS_BEFORE_M();
mock_dns_server_getter_->set_retval(true);
mock_dns_server_getter_->set_dns_servers({kNameserver1});
service_->WatchConfig(base::BindRepeating(
&DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this)));
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1));
for (int i = 0; i < 5; i++) {
mock_dns_server_getter_->set_dns_servers({kNameserver2});
seen_config_ = false;
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
NetworkChangeNotifier::CONNECTION_WIFI);
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver2));
mock_dns_server_getter_->set_dns_servers({kNameserver1});
seen_config_ = false;
NetworkChangeNotifier::NotifyObserversOfConnectionTypeChangeForTests(
NetworkChangeNotifier::CONNECTION_WIFI);
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_THAT(real_config_.nameservers, testing::ElementsAre(kNameserver1));
}
}
TEST_F(DnsConfigServiceAndroidTest, ReadsSearchSuffixes) {
SKIP_ANDROID_VERSIONS_BEFORE_M();
const std::vector<std::string> kSuffixes{"name1.test", "name2.test"};
mock_dns_server_getter_->set_retval(true);
mock_dns_server_getter_->set_dns_servers({kNameserver1});
mock_dns_server_getter_->set_search_suffixes(kSuffixes);
service_->ReadConfig(base::BindRepeating(
&DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this)));
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_EQ(real_config_.search, kSuffixes);
}
TEST_F(DnsConfigServiceAndroidTest, ReadsEmptySearchSuffixes) {
SKIP_ANDROID_VERSIONS_BEFORE_M();
mock_dns_server_getter_->set_retval(true);
mock_dns_server_getter_->set_dns_servers({kNameserver1});
service_->ReadConfig(base::BindRepeating(
&DnsConfigServiceAndroidTest::OnConfigChanged, base::Unretained(this)));
FastForwardBy(DnsConfigServiceAndroid::kConfigChangeDelay);
RunUntilIdle();
ASSERT_TRUE(seen_config_);
EXPECT_TRUE(real_config_.search.empty());
}
} // namespace
} // namespace internal
} // namespace net