1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/bind.h" 6#include "base/message_loop/message_loop.h" 7#include "chrome/browser/local_discovery/privet_http_impl.h" 8#include "net/base/host_port_pair.h" 9#include "net/base/net_errors.h" 10#include "net/url_request/test_url_fetcher_factory.h" 11#include "net/url_request/url_request_test_util.h" 12#include "testing/gmock/include/gmock/gmock.h" 13#include "testing/gtest/include/gtest/gtest.h" 14 15using testing::StrictMock; 16using testing::NiceMock; 17 18namespace local_discovery { 19 20namespace { 21 22const char kSampleInfoResponse[] = "{" 23 " \"version\": \"1.0\"," 24 " \"name\": \"Common printer\"," 25 " \"description\": \"Printer connected through Chrome connector\"," 26 " \"url\": \"https://www.google.com/cloudprint\"," 27 " \"type\": [" 28 " \"printer\"" 29 " ]," 30 " \"id\": \"11111111-2222-3333-4444-555555555555\"," 31 " \"device_state\": \"idle\"," 32 " \"connection_state\": \"online\"," 33 " \"manufacturer\": \"Google\"," 34 " \"model\": \"Google Chrome\"," 35 " \"serial_number\": \"1111-22222-33333-4444\"," 36 " \"firmware\": \"24.0.1312.52\"," 37 " \"uptime\": 600," 38 " \"setup_url\": \"http://support.google.com/\"," 39 " \"support_url\": \"http://support.google.com/cloudprint/?hl=en\"," 40 " \"update_url\": \"http://support.google.com/cloudprint/?hl=en\"," 41 " \"x-privet-token\": \"SampleTokenForTesting\"," 42 " \"api\": [" 43 " \"/privet/accesstoken\"," 44 " \"/privet/capabilities\"," 45 " \"/privet/printer/submitdoc\"," 46 " ]" 47 "}"; 48 49const char kSampleRegisterStartResponse[] = "{" 50 "\"user\": \"example@google.com\"," 51 "\"action\": \"start\"" 52 "}"; 53 54const char kSampleRegisterGetClaimTokenResponse[] = "{" 55 " \"action\": \"getClaimToken\"," 56 " \"user\": \"example@google.com\"," 57 " \"token\": \"MySampleToken\"," 58 " \"claim_url\": \"https://domain.com/SoMeUrL\"" 59 "}"; 60 61const char kSampleRegisterCompleteResponse[] = "{" 62 "\"user\": \"example@google.com\"," 63 "\"action\": \"complete\"," 64 "\"device_id\": \"MyDeviceID\"" 65 "}"; 66 67const char kSampleXPrivetErrorResponse[] = 68 "{ \"error\": \"invalid_x_privet_token\" }"; 69 70const char kSampleRegisterErrorTransient[] = 71 "{ \"error\": \"device_busy\", \"timeout\": 1}"; 72 73const char kSampleRegisterErrorPermanent[] = 74 "{ \"error\": \"user_cancel\" }"; 75 76const char kSampleInfoResponseBadJson[] = "{"; 77 78class MockTestURLFetcherFactoryDelegate 79 : public net::TestURLFetcher::DelegateForTests { 80 public: 81 // Callback issued correspondingly to the call to the |Start()| method. 82 MOCK_METHOD1(OnRequestStart, void(int fetcher_id)); 83 84 // Callback issued correspondingly to the call to |AppendChunkToUpload|. 85 // Uploaded chunks can be retrieved with the |upload_chunks()| getter. 86 MOCK_METHOD1(OnChunkUpload, void(int fetcher_id)); 87 88 // Callback issued correspondingly to the destructor. 89 MOCK_METHOD1(OnRequestEnd, void(int fetcher_id)); 90}; 91 92class PrivetHTTPTest : public ::testing::Test { 93 public: 94 PrivetHTTPTest() { 95 request_context_= new net::TestURLRequestContextGetter( 96 base::MessageLoopProxy::current()); 97 privet_client_.reset(new PrivetHTTPClientImpl( 98 net::HostPortPair("10.0.0.8", 6006), 99 request_context_.get())); 100 fetcher_factory_.SetDelegateForTests(&fetcher_delegate_); 101 } 102 virtual ~PrivetHTTPTest() { 103 } 104 105 protected: 106 base::MessageLoop loop_; 107 scoped_refptr<net::TestURLRequestContextGetter> request_context_; 108 net::TestURLFetcherFactory fetcher_factory_; 109 scoped_ptr<PrivetHTTPClient> privet_client_; 110 NiceMock<MockTestURLFetcherFactoryDelegate> fetcher_delegate_; 111}; 112 113class MockInfoDelegate : public PrivetInfoOperation::Delegate { 114 public: 115 MockInfoDelegate() {} 116 ~MockInfoDelegate() {} 117 118 virtual void OnPrivetInfoDone(int response_code, 119 const base::DictionaryValue* value) OVERRIDE { 120 if (!value) { 121 value_.reset(); 122 } else { 123 value_.reset(value->DeepCopy()); 124 } 125 126 OnPrivetInfoDoneInternal(response_code); 127 } 128 129 MOCK_METHOD1(OnPrivetInfoDoneInternal, void(int response_code)); 130 131 const base::DictionaryValue* value() { return value_.get(); } 132 protected: 133 scoped_ptr<base::DictionaryValue> value_; 134}; 135 136class MockRegisterDelegate : public PrivetRegisterOperation::Delegate { 137 public: 138 MockRegisterDelegate() { 139 } 140 ~MockRegisterDelegate() { 141 } 142 143 MOCK_METHOD2(OnPrivetRegisterClaimToken, void(const std::string& token, 144 const GURL& url)); 145 146 virtual void OnPrivetRegisterError( 147 const std::string& action, 148 PrivetRegisterOperation::FailureReason reason, 149 int printer_http_code, 150 const DictionaryValue* json) OVERRIDE { 151 // TODO(noamsml): Save and test for JSON? 152 OnPrivetRegisterErrorInternal(action, reason, printer_http_code); 153 } 154 155 MOCK_METHOD3(OnPrivetRegisterErrorInternal, 156 void(const std::string& action, 157 PrivetRegisterOperation::FailureReason reason, 158 int printer_http_code)); 159 160 MOCK_METHOD1(OnPrivetRegisterDone, void(const std::string& device_id)); 161}; 162 163class PrivetInfoTest : public PrivetHTTPTest { 164 public: 165 PrivetInfoTest() {} 166 167 virtual ~PrivetInfoTest() {} 168 169 virtual void SetUp() OVERRIDE { 170 info_operation_ = privet_client_->CreateInfoOperation(&info_delegate_); 171 } 172 173 protected: 174 scoped_ptr<PrivetInfoOperation> info_operation_; 175 StrictMock<MockInfoDelegate> info_delegate_; 176}; 177 178TEST_F(PrivetInfoTest, SuccessfulInfo) { 179 info_operation_->Start(); 180 181 net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); 182 ASSERT_TRUE(fetcher != NULL); 183 EXPECT_EQ(GURL("http://10.0.0.8:6006/privet/info"), 184 fetcher->GetOriginalURL()); 185 186 fetcher->SetResponseString(kSampleInfoResponse); 187 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 188 net::OK)); 189 fetcher->set_response_code(200); 190 191 EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(200)); 192 fetcher->delegate()->OnURLFetchComplete(fetcher); 193 194 std::string name; 195 196 privet_client_->GetCachedInfo()->GetString("name", &name); 197 EXPECT_EQ("Common printer", name); 198}; 199 200TEST_F(PrivetInfoTest, InfoSaveToken) { 201 info_operation_->Start(); 202 203 net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); 204 ASSERT_TRUE(fetcher != NULL); 205 fetcher->SetResponseString(kSampleInfoResponse); 206 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 207 net::OK)); 208 fetcher->set_response_code(200); 209 210 EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(200)); 211 fetcher->delegate()->OnURLFetchComplete(fetcher); 212 213 info_operation_ = privet_client_->CreateInfoOperation(&info_delegate_); 214 info_operation_->Start(); 215 216 fetcher = fetcher_factory_.GetFetcherByID(0); 217 ASSERT_TRUE(fetcher != NULL); 218 net::HttpRequestHeaders headers; 219 fetcher->GetExtraRequestHeaders(&headers); 220 std::string header_token; 221 ASSERT_TRUE(headers.GetHeader("X-Privet-Token", &header_token)); 222 EXPECT_EQ("SampleTokenForTesting", header_token); 223}; 224 225TEST_F(PrivetInfoTest, InfoFailureHTTP) { 226 info_operation_->Start(); 227 228 net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); 229 ASSERT_TRUE(fetcher != NULL); 230 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 231 net::OK)); 232 fetcher->set_response_code(404); 233 234 EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(404)); 235 fetcher->delegate()->OnURLFetchComplete(fetcher); 236 EXPECT_EQ(NULL, privet_client_->GetCachedInfo()); 237}; 238 239TEST_F(PrivetInfoTest, InfoFailureInternal) { 240 info_operation_->Start(); 241 242 net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); 243 ASSERT_TRUE(fetcher != NULL); 244 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::FAILED, 245 net::OK)); 246 fetcher->set_response_code(200); 247 248 EXPECT_CALL(info_delegate_, OnPrivetInfoDoneInternal(-1)); 249 fetcher->delegate()->OnURLFetchComplete(fetcher); 250 EXPECT_EQ(NULL, privet_client_->GetCachedInfo()); 251}; 252 253class PrivetRegisterTest : public PrivetHTTPTest { 254 public: 255 PrivetRegisterTest() { 256 } 257 virtual ~PrivetRegisterTest() { 258 } 259 260 virtual void SetUp() OVERRIDE { 261 info_operation_ = privet_client_->CreateInfoOperation(&info_delegate_); 262 register_operation_ = 263 privet_client_->CreateRegisterOperation("example@google.com", 264 ®ister_delegate_); 265 } 266 267 protected: 268 bool SuccessfulResponseToURL(const GURL& url, 269 const std::string& response) { 270 net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID(0); 271 if (!fetcher || url != fetcher->GetOriginalURL()) 272 return false; 273 274 fetcher->SetResponseString(response); 275 fetcher->set_status(net::URLRequestStatus(net::URLRequestStatus::SUCCESS, 276 net::OK)); 277 fetcher->set_response_code(200); 278 fetcher->delegate()->OnURLFetchComplete(fetcher); 279 return true; 280 } 281 282 void RunFor(base::TimeDelta time_period) { 283 base::CancelableCallback<void()> callback(base::Bind( 284 &PrivetRegisterTest::Stop, base::Unretained(this))); 285 base::MessageLoop::current()->PostDelayedTask( 286 FROM_HERE, callback.callback(), time_period); 287 288 base::MessageLoop::current()->Run(); 289 callback.Cancel(); 290 } 291 292 void Stop() { 293 base::MessageLoop::current()->Quit(); 294 } 295 296 scoped_ptr<PrivetInfoOperation> info_operation_; 297 NiceMock<MockInfoDelegate> info_delegate_; 298 scoped_ptr<PrivetRegisterOperation> register_operation_; 299 StrictMock<MockRegisterDelegate> register_delegate_; 300}; 301 302TEST_F(PrivetRegisterTest, RegisterSuccessSimple) { 303 // Start with info request first to populate XSRF token. 304 info_operation_->Start(); 305 306 EXPECT_TRUE(SuccessfulResponseToURL( 307 GURL("http://10.0.0.8:6006/privet/info"), 308 kSampleInfoResponse)); 309 310 register_operation_->Start(); 311 312 EXPECT_TRUE(SuccessfulResponseToURL( 313 GURL("http://10.0.0.8:6006/privet/register?" 314 "action=start&user=example@google.com"), 315 kSampleRegisterStartResponse)); 316 317 EXPECT_CALL(register_delegate_, OnPrivetRegisterClaimToken( 318 "MySampleToken", 319 GURL("https://domain.com/SoMeUrL"))); 320 321 EXPECT_TRUE(SuccessfulResponseToURL( 322 GURL("http://10.0.0.8:6006/privet/register?" 323 "action=getClaimToken&user=example@google.com"), 324 kSampleRegisterGetClaimTokenResponse)); 325 326 register_operation_->CompleteRegistration(); 327 328 EXPECT_CALL(register_delegate_, OnPrivetRegisterDone( 329 "MyDeviceID")); 330 331 EXPECT_TRUE(SuccessfulResponseToURL( 332 GURL("http://10.0.0.8:6006/privet/register?" 333 "action=complete&user=example@google.com"), 334 kSampleRegisterCompleteResponse)); 335} 336 337TEST_F(PrivetRegisterTest, RegisterNoInfoCall) { 338 register_operation_->Start(); 339 340 EXPECT_TRUE(SuccessfulResponseToURL( 341 GURL("http://10.0.0.8:6006/privet/info"), 342 kSampleInfoResponse)); 343 344 EXPECT_TRUE(SuccessfulResponseToURL( 345 GURL("http://10.0.0.8:6006/privet/register?" 346 "action=start&user=example@google.com"), 347 kSampleRegisterStartResponse)); 348} 349 350TEST_F(PrivetRegisterTest, RegisterXSRFFailure) { 351 register_operation_->Start(); 352 353 EXPECT_TRUE(SuccessfulResponseToURL( 354 GURL("http://10.0.0.8:6006/privet/info"), 355 kSampleInfoResponse)); 356 357 EXPECT_TRUE(SuccessfulResponseToURL( 358 GURL("http://10.0.0.8:6006/privet/register?" 359 "action=start&user=example@google.com"), 360 kSampleRegisterStartResponse)); 361 362 EXPECT_TRUE(SuccessfulResponseToURL( 363 GURL("http://10.0.0.8:6006/privet/register?" 364 "action=getClaimToken&user=example@google.com"), 365 kSampleXPrivetErrorResponse)); 366 367 EXPECT_TRUE(SuccessfulResponseToURL( 368 GURL("http://10.0.0.8:6006/privet/info"), 369 kSampleInfoResponse)); 370 371 EXPECT_CALL(register_delegate_, OnPrivetRegisterClaimToken( 372 "MySampleToken", GURL("https://domain.com/SoMeUrL"))); 373 374 EXPECT_TRUE(SuccessfulResponseToURL( 375 GURL("http://10.0.0.8:6006/privet/register?" 376 "action=getClaimToken&user=example@google.com"), 377 kSampleRegisterGetClaimTokenResponse)); 378} 379 380TEST_F(PrivetRegisterTest, TransientFailure) { 381 register_operation_->Start(); 382 383 EXPECT_TRUE(SuccessfulResponseToURL( 384 GURL("http://10.0.0.8:6006/privet/info"), 385 kSampleInfoResponse)); 386 387 EXPECT_TRUE(SuccessfulResponseToURL( 388 GURL("http://10.0.0.8:6006/privet/register?" 389 "action=start&user=example@google.com"), 390 kSampleRegisterErrorTransient)); 391 392 EXPECT_CALL(fetcher_delegate_, OnRequestStart(0)); 393 394 RunFor(base::TimeDelta::FromSeconds(2)); 395 396 testing::Mock::VerifyAndClearExpectations(&fetcher_delegate_); 397 398 EXPECT_TRUE(SuccessfulResponseToURL( 399 GURL("http://10.0.0.8:6006/privet/register?" 400 "action=start&user=example@google.com"), 401 kSampleRegisterStartResponse)); 402} 403 404TEST_F(PrivetRegisterTest, PermanentFailure) { 405 register_operation_->Start(); 406 407 EXPECT_TRUE(SuccessfulResponseToURL( 408 GURL("http://10.0.0.8:6006/privet/info"), 409 kSampleInfoResponse)); 410 411 EXPECT_TRUE(SuccessfulResponseToURL( 412 GURL("http://10.0.0.8:6006/privet/register?" 413 "action=start&user=example@google.com"), 414 kSampleRegisterStartResponse)); 415 416 EXPECT_CALL(register_delegate_, 417 OnPrivetRegisterErrorInternal( 418 "getClaimToken", 419 PrivetRegisterOperation::FAILURE_JSON_ERROR, 420 200)); 421 422 EXPECT_TRUE(SuccessfulResponseToURL( 423 GURL("http://10.0.0.8:6006/privet/register?" 424 "action=getClaimToken&user=example@google.com"), 425 kSampleRegisterErrorPermanent)); 426} 427 428TEST_F(PrivetRegisterTest, InfoFailure) { 429 register_operation_->Start(); 430 431 EXPECT_CALL(register_delegate_, 432 OnPrivetRegisterErrorInternal( 433 "info", 434 PrivetRegisterOperation::FAILURE_NETWORK, 435 -1)); 436 437 438 EXPECT_TRUE(SuccessfulResponseToURL( 439 GURL("http://10.0.0.8:6006/privet/info"), 440 kSampleInfoResponseBadJson)); 441} 442 443} // namespace 444 445} // namespace local_discovery 446