Chromium Code Reviews| Index: net/dns/host_resolver_mojo_unittest.cc |
| diff --git a/net/dns/host_resolver_mojo_unittest.cc b/net/dns/host_resolver_mojo_unittest.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c3aa98abfb30d97915d1133355567ba30d92fe19 |
| --- /dev/null |
| +++ b/net/dns/host_resolver_mojo_unittest.cc |
| @@ -0,0 +1,385 @@ |
| +// Copyright 2015 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/host_resolver_mojo.h" |
| + |
| +#include <string> |
| + |
| +#include "base/barrier_closure.h" |
| +#include "base/memory/scoped_vector.h" |
| +#include "base/run_loop.h" |
| +#include "net/base/net_errors.h" |
| +#include "net/base/request_priority.h" |
| +#include "net/dns/type_converters.h" |
| +#include "testing/gtest/include/gtest/gtest.h" |
| +#include "third_party/mojo/src/mojo/public/cpp/bindings/binding.h" |
| +#include "third_party/mojo/src/mojo/public/cpp/bindings/error_handler.h" |
| + |
| +namespace net { |
| +namespace { |
| + |
| +enum class ConnectionErrorSource { |
| + NONE, |
| + RESOLVER, |
| + REQUEST, |
| + CLIENT, |
| +}; |
| + |
| +void OnResolveComplete(int* error_out, |
| + const base::Closure& quit_run_loop, |
| + int error) { |
| + if (!quit_run_loop.is_null()) |
| + quit_run_loop.Run(); |
| + *error_out = error; |
| +} |
| + |
| +void Fail(int result) { |
| + FAIL() << "Unexpected callback called with error " << result; |
| +} |
| + |
| +class MockMojoHostResolverRequest : public mojo::ErrorHandler { |
| + public: |
| + MockMojoHostResolverRequest(interfaces::HostResolverRequestClientPtr client, |
| + const base::Closure& error_callback); |
| + void OnConnectionError() override; |
| + |
| + private: |
| + interfaces::HostResolverRequestClientPtr client_; |
| + const base::Closure error_callback_; |
| +}; |
| + |
| +MockMojoHostResolverRequest::MockMojoHostResolverRequest( |
| + interfaces::HostResolverRequestClientPtr client, |
| + const base::Closure& error_callback) |
| + : client_(client.Pass()), error_callback_(error_callback) { |
| + client_.set_error_handler(this); |
| +} |
| + |
| +void MockMojoHostResolverRequest::OnConnectionError() { |
| + error_callback_.Run(); |
| +} |
| + |
| +struct HostResolverAction { |
| + public: |
| + enum Action { |
| + COMPLETE, |
| + DROP, |
| + RETAIN, |
| + }; |
| + |
| + static scoped_ptr<HostResolverAction> ReturnError(int error) { |
| + scoped_ptr<HostResolverAction> result(new HostResolverAction); |
| + result->error = error; |
| + return result.Pass(); |
| + } |
| + |
| + static scoped_ptr<HostResolverAction> ReturnResult( |
| + const AddressList& address_list) { |
| + scoped_ptr<HostResolverAction> result(new HostResolverAction); |
| + result->addresses = interfaces::AddressList::From(address_list); |
| + return result.Pass(); |
| + } |
| + |
| + static scoped_ptr<HostResolverAction> DropRequest() { |
| + scoped_ptr<HostResolverAction> result(new HostResolverAction); |
| + result->action = DROP; |
| + return result.Pass(); |
| + } |
| + |
| + static scoped_ptr<HostResolverAction> RetainRequest() { |
| + scoped_ptr<HostResolverAction> result(new HostResolverAction); |
| + result->action = RETAIN; |
| + return result.Pass(); |
| + } |
| + |
| + Action action = COMPLETE; |
| + interfaces::AddressListPtr addresses; |
| + int error = OK; |
| +}; |
| + |
| +class MockMojoHostResolver : public interfaces::HostResolver, |
| + public mojo::ErrorHandler { |
| + public: |
| + explicit MockMojoHostResolver( |
|
Anand Mistry (off Chromium)
2015/02/16 06:32:44
Can you add a destructor that does:
ASSERT_EQ(resu
Sam McNally
2015/02/16 07:24:44
Done.
|
| + mojo::InterfaceRequest<interfaces::HostResolver> request, |
| + const base::Closure& resolver_connection_error_callback, |
| + const base::Closure& request_connection_error_callback); |
| + |
| + void AddAction(scoped_ptr<HostResolverAction> action); |
| + |
| + const mojo::Array<interfaces::HostResolverRequestInfoPtr>& requests() { |
| + return requests_received_; |
| + } |
| + |
| + private: |
| + // interfaces::HostResolver override. |
| + void Resolve(interfaces::HostResolverRequestInfoPtr request_info, |
| + interfaces::HostResolverRequestClientPtr client) override; |
| + |
| + // mojo::ErrorHandler override. |
| + void OnConnectionError() override; |
| + |
| + mojo::Binding<interfaces::HostResolver> binding_; |
| + ScopedVector<HostResolverAction> actions_; |
| + size_t results_returned_ = 0; |
| + mojo::Array<interfaces::HostResolverRequestInfoPtr> requests_received_; |
| + const base::Closure resolver_connection_error_callback_; |
| + const base::Closure request_connection_error_callback_; |
| + ScopedVector<MockMojoHostResolverRequest> requests_; |
| +}; |
| + |
| +MockMojoHostResolver::MockMojoHostResolver( |
| + mojo::InterfaceRequest<interfaces::HostResolver> request, |
| + const base::Closure& resolver_connection_error_callback, |
| + const base::Closure& request_connection_error_callback) |
| + : binding_(this, request.Pass()), |
| + resolver_connection_error_callback_(resolver_connection_error_callback), |
| + request_connection_error_callback_(request_connection_error_callback) { |
| + binding_.set_error_handler(this); |
| +} |
| + |
| +void MockMojoHostResolver::OnConnectionError() { |
| + resolver_connection_error_callback_.Run(); |
| +} |
| + |
| +void MockMojoHostResolver::AddAction(scoped_ptr<HostResolverAction> action) { |
| + actions_.push_back(action.release()); |
| +} |
| + |
| +void MockMojoHostResolver::Resolve( |
| + interfaces::HostResolverRequestInfoPtr request_info, |
| + interfaces::HostResolverRequestClientPtr client) { |
| + requests_received_.push_back(request_info.Pass()); |
| + ASSERT_LE(results_returned_, actions_.size()); |
| + switch (actions_[results_returned_]->action) { |
| + case HostResolverAction::COMPLETE: |
| + client->ReportResult(actions_[results_returned_]->error, |
| + actions_[results_returned_]->addresses.Pass()); |
| + break; |
| + case HostResolverAction::RETAIN: |
| + requests_.push_back(new MockMojoHostResolverRequest( |
| + client.Pass(), request_connection_error_callback_)); |
| + break; |
| + case HostResolverAction::DROP: |
| + client.reset(); |
| + break; |
| + } |
| + results_returned_++; |
| +} |
| + |
| +} // namespace |
| + |
| +class HostResolverMojoTest : public testing::Test, public mojo::ErrorHandler { |
| + protected: |
| + void SetUp() override { |
| + interfaces::HostResolverPtr resolver_ptr; |
| + mock_resolver_.reset(new MockMojoHostResolver( |
| + mojo::GetProxy(&resolver_ptr), |
| + base::Bind(&HostResolverMojoTest::HandleConnectionError, |
| + base::Unretained(this), ConnectionErrorSource::RESOLVER), |
| + base::Bind(&HostResolverMojoTest::HandleConnectionError, |
| + base::Unretained(this), ConnectionErrorSource::REQUEST))); |
| + resolver_.reset(new HostResolverMojo(resolver_ptr.Pass(), this)); |
| + } |
| + |
| + void OnConnectionError() override { |
| + HandleConnectionError(ConnectionErrorSource::CLIENT); |
| + } |
| + |
| + void HandleConnectionError(ConnectionErrorSource source) { |
| + if (!run_loop_quit_closure_.is_null()) |
| + run_loop_quit_closure_.Run(); |
| + ASSERT_EQ(expected_connection_error_source_, source); |
| + } |
| + |
| + int Resolve(const HostResolver::RequestInfo& request_info, |
| + AddressList* result) { |
| + HostResolver::RequestHandle request_handle = nullptr; |
| + base::RunLoop run_loop; |
| + int error = 1; |
| + resolver_->Resolve( |
| + request_info, DEFAULT_PRIORITY, result, |
| + base::Bind(&OnResolveComplete, &error, run_loop.QuitClosure()), |
|
Anand Mistry (off Chromium)
2015/02/16 06:32:44
net::TestCompletionCallback
Sam McNally
2015/02/16 07:24:44
Done.
|
| + &request_handle, BoundNetLog()); |
| + run_loop.Run(); |
| + return error; |
| + } |
| + |
| + void WaitForConnectionError(ConnectionErrorSource source) { |
| + expected_connection_error_source_ = source; |
| + base::RunLoop run_loop; |
| + run_loop_quit_closure_ = run_loop.QuitClosure(); |
| + run_loop.Run(); |
| + } |
| + |
| + scoped_ptr<MockMojoHostResolver> mock_resolver_; |
| + |
| + scoped_ptr<HostResolverMojo> resolver_; |
| + |
| + ConnectionErrorSource expected_connection_error_source_ = |
| + ConnectionErrorSource::NONE; |
| + base::Closure run_loop_quit_closure_; |
| +}; |
| + |
| +TEST_F(HostResolverMojoTest, Basic) { |
| + AddressList address_list; |
| + IPAddressNumber address_number; |
| + ASSERT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &address_number)); |
| + address_list.push_back(IPEndPoint(address_number, 12345)); |
| + address_list.push_back( |
| + IPEndPoint(ConvertIPv4NumberToIPv6Number(address_number), 12345)); |
| + mock_resolver_->AddAction(HostResolverAction::ReturnResult(address_list)); |
| + HostResolver::RequestInfo request_info( |
| + HostPortPair::FromString("example.com:12345")); |
| + AddressList result; |
| + EXPECT_EQ(OK, Resolve(request_info, &result)); |
| + ASSERT_EQ(2u, result.size()); |
| + EXPECT_EQ(address_list[0], result[0]); |
| + EXPECT_EQ(address_list[1], result[1]); |
| + |
| + ASSERT_EQ(1u, mock_resolver_->requests().size()); |
| + interfaces::HostResolverRequestInfo& request = *mock_resolver_->requests()[0]; |
| + EXPECT_EQ("example.com", request.host.To<std::string>()); |
| + EXPECT_EQ(12345, request.port); |
| + EXPECT_EQ(interfaces::ADDRESS_FAMILY_UNSPECIFIED, request.address_family); |
| + EXPECT_FALSE(request.is_my_ip_address); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, Multiple) { |
| + AddressList address_list; |
| + IPAddressNumber address_number; |
| + ASSERT_TRUE(ParseIPLiteralToNumber("1.2.3.4", &address_number)); |
| + address_list.push_back(IPEndPoint(address_number, 12345)); |
| + mock_resolver_->AddAction(HostResolverAction::ReturnResult(address_list)); |
| + mock_resolver_->AddAction( |
| + HostResolverAction::ReturnError(ERR_NAME_NOT_RESOLVED)); |
| + HostResolver::RequestInfo request_info1( |
| + HostPortPair::FromString("example.com:12345")); |
| + request_info1.set_address_family(ADDRESS_FAMILY_IPV4); |
| + request_info1.set_is_my_ip_address(true); |
| + HostResolver::RequestInfo request_info2( |
| + HostPortPair::FromString("example.org:80")); |
| + request_info2.set_address_family(ADDRESS_FAMILY_IPV6); |
| + AddressList result1; |
| + AddressList result2; |
| + HostResolver::RequestHandle request_handle1 = nullptr; |
| + HostResolver::RequestHandle request_handle2 = nullptr; |
| + int error1 = 1; |
| + int error2 = 1; |
| + base::RunLoop run_loop; |
| + base::Closure barrier = base::BarrierClosure(2, run_loop.QuitClosure()); |
| + resolver_->Resolve(request_info1, DEFAULT_PRIORITY, &result1, |
| + base::Bind(&OnResolveComplete, &error1, barrier), |
| + &request_handle1, BoundNetLog()); |
| + resolver_->Resolve(request_info2, DEFAULT_PRIORITY, &result2, |
| + base::Bind(&OnResolveComplete, &error2, barrier), |
| + &request_handle2, BoundNetLog()); |
| + run_loop.Run(); |
| + EXPECT_EQ(OK, error1); |
| + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, error2); |
| + ASSERT_EQ(1u, result1.size()); |
| + EXPECT_EQ(address_list[0], result1[0]); |
| + ASSERT_EQ(0u, result2.size()); |
| + |
| + ASSERT_EQ(2u, mock_resolver_->requests().size()); |
| + interfaces::HostResolverRequestInfo& request1 = |
| + *mock_resolver_->requests()[0]; |
| + EXPECT_EQ("example.com", request1.host.To<std::string>()); |
| + EXPECT_EQ(12345, request1.port); |
| + EXPECT_EQ(interfaces::ADDRESS_FAMILY_IPV4, request1.address_family); |
| + EXPECT_TRUE(request1.is_my_ip_address); |
| + interfaces::HostResolverRequestInfo& request2 = |
| + *mock_resolver_->requests()[1]; |
| + EXPECT_EQ("example.org", request2.host.To<std::string>()); |
| + EXPECT_EQ(80, request2.port); |
| + EXPECT_EQ(interfaces::ADDRESS_FAMILY_IPV6, request2.address_family); |
| + EXPECT_FALSE(request2.is_my_ip_address); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, Error) { |
| + mock_resolver_->AddAction( |
| + HostResolverAction::ReturnError(ERR_NAME_NOT_RESOLVED)); |
| + HostResolver::RequestInfo request_info( |
| + HostPortPair::FromString("example.com:8080")); |
| + request_info.set_address_family(ADDRESS_FAMILY_IPV4); |
| + AddressList result; |
| + EXPECT_EQ(ERR_NAME_NOT_RESOLVED, Resolve(request_info, &result)); |
| + EXPECT_TRUE(result.empty()); |
| + |
| + ASSERT_EQ(1u, mock_resolver_->requests().size()); |
| + interfaces::HostResolverRequestInfo& request = *mock_resolver_->requests()[0]; |
| + EXPECT_EQ("example.com", request.host.To<std::string>()); |
| + EXPECT_EQ(8080, request.port); |
| + EXPECT_EQ(interfaces::ADDRESS_FAMILY_IPV4, request.address_family); |
| + EXPECT_FALSE(request.is_my_ip_address); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, EmptyResult) { |
| + mock_resolver_->AddAction(HostResolverAction::ReturnError(OK)); |
| + HostResolver::RequestInfo request_info( |
| + HostPortPair::FromString("example.com:8080")); |
| + AddressList result; |
| + EXPECT_EQ(OK, Resolve(request_info, &result)); |
| + EXPECT_TRUE(result.empty()); |
| + |
| + ASSERT_EQ(1u, mock_resolver_->requests().size()); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, Cancel) { |
| + mock_resolver_->AddAction(HostResolverAction::RetainRequest()); |
| + HostResolver::RequestInfo request_info( |
| + HostPortPair::FromString("example.com:80")); |
| + request_info.set_address_family(ADDRESS_FAMILY_IPV6); |
| + AddressList result; |
| + HostResolver::RequestHandle request_handle = nullptr; |
| + resolver_->Resolve(request_info, DEFAULT_PRIORITY, &result, base::Bind(&Fail), |
| + &request_handle, BoundNetLog()); |
| + resolver_->CancelRequest(request_handle); |
| + WaitForConnectionError(ConnectionErrorSource::REQUEST); |
| + EXPECT_TRUE(result.empty()); |
| + |
| + ASSERT_EQ(1u, mock_resolver_->requests().size()); |
| + interfaces::HostResolverRequestInfo& request = *mock_resolver_->requests()[0]; |
| + EXPECT_EQ("example.com", request.host.To<std::string>()); |
| + EXPECT_EQ(80, request.port); |
| + EXPECT_EQ(interfaces::ADDRESS_FAMILY_IPV6, request.address_family); |
| + EXPECT_FALSE(request.is_my_ip_address); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, ImplDropsClientConnection) { |
| + mock_resolver_->AddAction(HostResolverAction::DropRequest()); |
| + HostResolver::RequestInfo request_info( |
| + HostPortPair::FromString("example.com:1")); |
| + AddressList result; |
| + EXPECT_EQ(ERR_FAILED, Resolve(request_info, &result)); |
| + EXPECT_TRUE(result.empty()); |
| + |
| + ASSERT_EQ(1u, mock_resolver_->requests().size()); |
| + interfaces::HostResolverRequestInfo& request = *mock_resolver_->requests()[0]; |
| + EXPECT_EQ("example.com", request.host.To<std::string>()); |
| + EXPECT_EQ(1, request.port); |
| + EXPECT_EQ(interfaces::ADDRESS_FAMILY_UNSPECIFIED, request.address_family); |
| + EXPECT_FALSE(request.is_my_ip_address); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, DestroyImpl) { |
| + mock_resolver_.reset(); |
| + WaitForConnectionError(ConnectionErrorSource::CLIENT); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, DestroyClient) { |
| + resolver_.reset(); |
| + WaitForConnectionError(ConnectionErrorSource::RESOLVER); |
| +} |
| + |
| +TEST_F(HostResolverMojoTest, ResolveFromCache) { |
| + HostResolver::RequestInfo request_info( |
| + HostPortPair::FromString("example.com:8080")); |
| + AddressList result; |
| + EXPECT_EQ(ERR_DNS_CACHE_MISS, |
| + resolver_->ResolveFromCache(request_info, &result, BoundNetLog())); |
| + EXPECT_TRUE(result.empty()); |
| +} |
| + |
| +} // namespace net |