Index: blimp/client/session/assignment_source_unittest.cc |
diff --git a/blimp/client/session/assignment_source_unittest.cc b/blimp/client/session/assignment_source_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..68cb1e13b730f1aa1260f5ef21234edf779a0f52 |
--- /dev/null |
+++ b/blimp/client/session/assignment_source_unittest.cc |
@@ -0,0 +1,347 @@ |
+// Copyright 2016 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 "blimp/client/session/assignment_source.h" |
+ |
+#include "base/command_line.h" |
+#include "base/json/json_reader.h" |
+#include "base/json/json_writer.h" |
+#include "base/test/test_simple_task_runner.h" |
+#include "base/thread_task_runner_handle.h" |
+#include "base/values.h" |
+#include "blimp/client/app/blimp_client_switches.h" |
+#include "blimp/common/protocol_version.h" |
+#include "net/url_request/test_url_fetcher_factory.h" |
+#include "testing/gmock/include/gmock/gmock.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+using testing::_; |
+using testing::InSequence; |
+ |
+namespace blimp { |
+namespace client { |
+namespace { |
+ |
+MATCHER_P(AssignmentEquals, assignment, "") { |
+ return arg.transport_protocol == assignment.transport_protocol && |
+ arg.ip_endpoint == assignment.ip_endpoint && |
+ arg.client_token == assignment.client_token && |
+ arg.certificate == assignment.certificate && |
+ arg.certificate_fingerprint == assignment.certificate_fingerprint; |
+} |
+ |
+net::IPEndPoint BuildIPEndPoint(const std::string& ip, int port) { |
+ net::IPAddress ip_address; |
+ EXPECT_TRUE(ip_address.AssignFromIPLiteral(ip)); |
+ |
+ return net::IPEndPoint(ip_address, port); |
+} |
+ |
+Assignment BuildValidAssignment() { |
+ Assignment assignment; |
+ assignment.transport_protocol = Assignment::TransportProtocol::SSL; |
+ assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); |
+ assignment.client_token = "SecretT0kenz"; |
+ assignment.certificate_fingerprint = "WhaleWhaleWhale"; |
+ assignment.certificate = "whaaaaaaaaaaaaale"; |
+ return assignment; |
+} |
+ |
+std::string BuildResponseFromAssignment(const Assignment& assignment) { |
+ base::DictionaryValue dict; |
+ dict.SetString("clientToken", assignment.client_token); |
+ dict.SetString("host", assignment.ip_endpoint.address().ToString()); |
+ dict.SetInteger("port", assignment.ip_endpoint.port()); |
+ dict.SetString("certificateFingerprint", assignment.certificate_fingerprint); |
+ dict.SetString("certificate", assignment.certificate); |
+ |
+ std::string json; |
+ base::JSONWriter::Write(dict, &json); |
+ return json; |
+} |
+ |
+class AssignmentSourceTest : public testing::Test { |
+ public: |
+ AssignmentSourceTest() |
+ : task_runner_(new base::TestSimpleTaskRunner), |
+ task_runner_handle_(task_runner_), |
+ source_(task_runner_, task_runner_) {} |
+ |
+ // This expects the AssignmentSource::GetAssignment to return a custom |
+ // endpoint without having to hit the network. |
nyquist
2016/02/18 01:58:40
Could you help me as a new reader to this to say t
David Trainor- moved to gerrit
2016/02/18 16:01:55
Done.
|
+ void GetAlternateAssignment() { |
+ source_.GetAssignment("", |
+ base::Bind(&AssignmentSourceTest::AssignmentResponse, |
+ base::Unretained(this))); |
+ EXPECT_EQ(nullptr, factory_.GetFetcherByID(0)); |
+ task_runner_->RunUntilIdle(); |
+ } |
+ |
+ // See net/base/net_errors.h for possible status errors. |
+ void GetAssignment(net::HttpStatusCode response_code, |
nyquist
2016/02/18 01:58:40
This name is a little bit confusing on first read-
David Trainor- moved to gerrit
2016/02/18 16:01:54
Done.
|
+ int status, |
+ const std::string& response, |
+ const std::string& client_auth_token, |
+ const std::string& protocol_version) { |
+ source_.GetAssignment(client_auth_token, |
+ base::Bind(&AssignmentSourceTest::AssignmentResponse, |
+ base::Unretained(this))); |
+ |
+ net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0); |
+ |
+ task_runner_->RunUntilIdle(); |
+ |
+ EXPECT_NE(nullptr, fetcher); |
+ EXPECT_EQ(kDefaultAssignerURL, fetcher->GetOriginalURL().spec()); |
+ |
+ // Check that the request has a valid protocol_version. |
+ scoped_ptr<base::Value> json = |
+ base::JSONReader::Read(fetcher->upload_data()); |
+ EXPECT_NE(nullptr, json.get()); |
+ |
+ const base::DictionaryValue* dict; |
+ EXPECT_TRUE(json->GetAsDictionary(&dict)); |
+ |
+ std::string uploaded_protocol_version; |
+ EXPECT_TRUE( |
+ dict->GetString("protocol_version", &uploaded_protocol_version)); |
+ EXPECT_EQ(protocol_version, uploaded_protocol_version); |
+ |
+ // Check that the request has a valid authentication header. |
+ net::HttpRequestHeaders headers; |
+ fetcher->GetExtraRequestHeaders(&headers); |
+ |
+ std::string authorization; |
+ EXPECT_TRUE(headers.GetHeader("Authorization", &authorization)); |
+ EXPECT_EQ("Bearer " + client_auth_token, authorization); |
+ |
+ // Send the fake response back. |
+ fetcher->set_response_code(response_code); |
+ fetcher->set_status(net::URLRequestStatus::FromError(status)); |
+ fetcher->SetResponseString(response); |
+ fetcher->delegate()->OnURLFetchComplete(fetcher); |
+ |
+ task_runner_->RunUntilIdle(); |
+ } |
+ |
+ MOCK_METHOD2(AssignmentResponse, |
+ void(AssignmentSource::Result, const Assignment&)); |
+ |
+ protected: |
+ // Used to drive all AssignmentSource tasks. |
+ scoped_refptr<base::TestSimpleTaskRunner> task_runner_; |
+ base::ThreadTaskRunnerHandle task_runner_handle_; |
+ |
+ net::TestURLFetcherFactory factory_; |
+ |
+ AssignmentSource source_; |
+}; |
+ |
+TEST_F(AssignmentSourceTest, TestTCPAlternateEndpointSuccess) { |
+ Assignment assignment; |
+ assignment.transport_protocol = Assignment::TransportProtocol::TCP; |
+ assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); |
+ assignment.client_token = kDummyClientToken; |
+ |
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
+ switches::kBlimpletEndpoint, "tcp:100.150.200.250:500"); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, |
+ AssignmentEquals(assignment))) |
+ .Times(1); |
+ |
+ GetAlternateAssignment(); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestSSLAlternateEndpointSuccess) { |
+ Assignment assignment; |
+ assignment.transport_protocol = Assignment::TransportProtocol::SSL; |
+ assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); |
+ assignment.client_token = kDummyClientToken; |
+ |
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
+ switches::kBlimpletEndpoint, "ssl:100.150.200.250:500"); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, |
+ AssignmentEquals(assignment))) |
+ .Times(1); |
+ |
+ GetAlternateAssignment(); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestQUICAlternateEndpointSuccess) { |
+ Assignment assignment; |
+ assignment.transport_protocol = Assignment::TransportProtocol::QUIC; |
+ assignment.ip_endpoint = BuildIPEndPoint("100.150.200.250", 500); |
+ assignment.client_token = kDummyClientToken; |
+ |
+ base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( |
+ switches::kBlimpletEndpoint, "quic:100.150.200.250:500"); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, |
+ AssignmentEquals(assignment))) |
+ .Times(1); |
+ |
+ GetAlternateAssignment(); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestSuccess) { |
+ Assignment assignment = BuildValidAssignment(); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, |
+ AssignmentEquals(assignment))) |
+ .Times(1); |
+ |
+ GetAssignment(net::HTTP_OK, net::Error::OK, |
+ BuildResponseFromAssignment(assignment), "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestSecondRequestInterruptsFirst) { |
+ InSequence sequence; |
+ Assignment assignment = BuildValidAssignment(); |
+ |
+ source_.GetAssignment("", |
+ base::Bind(&AssignmentSourceTest::AssignmentResponse, |
+ base::Unretained(this))); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_SERVER_INTERRUPTED, |
+ AssignmentEquals(Assignment()))) |
+ .Times(1) |
+ .RetiresOnSaturation(); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, |
+ AssignmentEquals(assignment))) |
+ .Times(1) |
+ .RetiresOnSaturation(); |
+ |
+ GetAssignment(net::HTTP_OK, net::Error::OK, |
+ BuildResponseFromAssignment(assignment), "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestValidAfterError) { |
+ InSequence sequence; |
+ Assignment assignment = BuildValidAssignment(); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_NETWORK_FAILURE, _)) |
+ .Times(1) |
+ .RetiresOnSaturation(); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse(AssignmentSource::Result::RESULT_OK, |
+ AssignmentEquals(assignment))) |
+ .Times(1) |
+ .RetiresOnSaturation(); |
+ |
+ GetAssignment(net::HTTP_OK, net::Error::ERR_INSUFFICIENT_RESOURCES, "", |
+ "UserAuthT0kenz", kEngineVersion); |
+ |
+ GetAssignment(net::HTTP_OK, net::Error::OK, |
+ BuildResponseFromAssignment(assignment), "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestNetworkFailure) { |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_NETWORK_FAILURE, _)); |
+ GetAssignment(net::HTTP_OK, net::Error::ERR_INSUFFICIENT_RESOURCES, "", |
+ "UserAuthT0kenz", kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestBadRequest) { |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_BAD_REQUEST, _)); |
+ GetAssignment(net::HTTP_BAD_REQUEST, net::Error::OK, "", "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestUnauthorized) { |
+ EXPECT_CALL(*this, |
+ AssignmentResponse( |
+ AssignmentSource::Result::RESULT_EXPIRED_ACCESS_TOKEN, _)); |
+ GetAssignment(net::HTTP_UNAUTHORIZED, net::Error::OK, "", "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestForbidden) { |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_USER_INVALID, _)); |
+ GetAssignment(net::HTTP_FORBIDDEN, net::Error::OK, "", "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestTooManyRequests) { |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_OUT_OF_VMS, _)); |
+ GetAssignment(static_cast<net::HttpStatusCode>(429), net::Error::OK, "", |
+ "UserAuthT0kenz", kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestInternalServerError) { |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_SERVER_ERROR, _)); |
+ GetAssignment(net::HTTP_INTERNAL_SERVER_ERROR, net::Error::OK, "", |
+ "UserAuthT0kenz", kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestUnexpectedNetCodeFallback) { |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); |
+ GetAssignment(net::HTTP_NOT_IMPLEMENTED, net::Error::OK, "", "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestInvalidJsonResponse) { |
+ Assignment assignment = BuildValidAssignment(); |
+ |
+ // Remove half the response. |
+ std::string response = BuildResponseFromAssignment(assignment); |
+ response = response.substr(response.size() / 2); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); |
+ GetAssignment(net::HTTP_OK, net::Error::OK, response, "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestMissingResponsePort) { |
+ // Purposely do not add the 'port' field to the response. |
+ base::DictionaryValue dict; |
+ dict.SetString("clientToken", "SecretT0kenz"); |
+ dict.SetString("host", "happywhales"); |
+ dict.SetString("certificateFingerprint", "WhaleWhaleWhale"); |
+ dict.SetString("certificate", "whaaaaaaaaaaaaale"); |
+ |
+ std::string response; |
+ base::JSONWriter::Write(dict, &response); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); |
+ GetAssignment(net::HTTP_OK, net::Error::OK, response, "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+TEST_F(AssignmentSourceTest, TestInvalidIPAddress) { |
+ // Purposely add an invalid IP field to the response. |
+ base::DictionaryValue dict; |
+ dict.SetString("clientToken", "SecretT0kenz"); |
+ dict.SetString("host", "happywhales"); |
+ dict.SetInteger("port", 500); |
+ dict.SetString("certificateFingerprint", "WhaleWhaleWhale"); |
+ dict.SetString("certificate", "whaaaaaaaaaaaaale"); |
+ |
+ std::string response; |
+ base::JSONWriter::Write(dict, &response); |
+ |
+ EXPECT_CALL(*this, AssignmentResponse( |
+ AssignmentSource::Result::RESULT_BAD_RESPONSE, _)); |
+ GetAssignment(net::HTTP_OK, net::Error::OK, response, "UserAuthT0kenz", |
+ kEngineVersion); |
+} |
+ |
+} // namespace |
+} // namespace client |
+} // namespace blimp |