local_discovery_ui_browsertest.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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/basictypes.h" 6#include "base/bind.h" 7#include "base/callback.h" 8#include "base/command_line.h" 9#include "base/compiler_specific.h" 10#include "base/memory/scoped_ptr.h" 11#include "base/message_loop/message_loop.h" 12#include "chrome/browser/local_discovery/test_service_discovery_client.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 15#include "chrome/browser/signin/signin_manager_factory.h" 16#include "chrome/browser/ui/browser.h" 17#include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h" 18#include "chrome/common/chrome_switches.h" 19#include "chrome/common/url_constants.h" 20#include "chrome/test/base/ui_test_utils.h" 21#include "chrome/test/base/web_ui_browsertest.h" 22#include "components/signin/core/browser/profile_oauth2_token_service.h" 23#include "components/signin/core/browser/signin_manager.h" 24#include "components/signin/core/browser/signin_manager_base.h" 25#include "google_apis/gaia/gaia_urls.h" 26#include "net/http/http_status_code.h" 27#include "net/url_request/test_url_fetcher_factory.h" 28#include "net/url_request/url_request_status.h" 29#include "net/url_request/url_request_test_util.h" 30 31#if defined(OS_CHROMEOS) 32#include "base/prefs/pref_service.h" 33#include "chrome/common/pref_names.h" 34#endif 35 36using testing::InvokeWithoutArgs; 37using testing::Return; 38using testing::AtLeast; 39using testing::DoDefault; 40using testing::DoAll; 41using testing::InSequence; 42using testing::StrictMock; 43using testing::AnyNumber; 44 45using testing::InvokeWithoutArgs; 46using testing::Return; 47using testing::AtLeast; 48 49namespace local_discovery { 50 51namespace { 52 53const uint8 kQueryData[] = { 54 // Header 55 0x00, 0x00, 56 0x00, 0x00, // Flags not set. 57 0x00, 0x01, // Set QDCOUNT (question count) to 1, all the 58 // rest are 0 for a query. 59 0x00, 0x00, 60 0x00, 0x00, 61 0x00, 0x00, 62 63 // Question 64 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 65 0x04, '_', 't', 'c', 'p', 66 0x05, 'l', 'o', 'c', 'a', 'l', 67 0x00, 68 69 0x00, 0x0c, // QTYPE: A query. 70 0x00, 0x01, // QCLASS: IN class. Unicast bit not set. 71}; 72 73const uint8 kAnnouncePacket[] = { 74 // Header 75 0x00, 0x00, // ID is zeroed out 76 0x80, 0x00, // Standard query response, no error 77 0x00, 0x00, // No questions (for simplicity) 78 0x00, 0x05, // 5 RR (answers) 79 0x00, 0x00, // 0 authority RRs 80 0x00, 0x00, // 0 additional RRs 81 82 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 83 0x04, '_', 't', 'c', 'p', 84 0x05, 'l', 'o', 'c', 'a', 'l', 85 0x00, 86 0x00, 0x0c, // TYPE is PTR. 87 0x00, 0x01, // CLASS is IN. 88 0x00, 0x00, // TTL (4 bytes) is 32768 second. 89 0x10, 0x00, 90 0x00, 0x0c, // RDLENGTH is 12 bytes. 91 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 92 0xc0, 0x0c, 93 94 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 95 0xc0, 0x0c, 96 0x00, 0x10, // TYPE is TXT. 97 0x00, 0x01, // CLASS is IN. 98 0x00, 0x00, // TTL (4 bytes) is 32768 seconds. 99 0x01, 0x00, 100 0x00, 0x34, // RDLENGTH is 69 bytes. 101 0x03, 'i', 'd', '=', 102 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ', 103 'd', 'e', 'v', 'i', 'c', 'e', 104 0x1e, 'n', 'o', 't', 'e', '=', 105 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ', 106 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', 107 108 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 109 0xc0, 0x0c, 110 0x00, 0x21, // Type is SRV 111 0x00, 0x01, // CLASS is IN 112 0x00, 0x00, // TTL (4 bytes) is 32768 second. 113 0x10, 0x00, 114 0x00, 0x17, // RDLENGTH is 23 115 0x00, 0x00, 116 0x00, 0x00, 117 0x22, 0xb8, // port 8888 118 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 119 0x05, 'l', 'o', 'c', 'a', 'l', 120 0x00, 121 122 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 123 0x05, 'l', 'o', 'c', 'a', 'l', 124 0x00, 125 0x00, 0x01, // Type is A 126 0x00, 0x01, // CLASS is IN 127 0x00, 0x00, // TTL (4 bytes) is 32768 second. 128 0x10, 0x00, 129 0x00, 0x04, // RDLENGTH is 4 130 0x01, 0x02, 0x03, 0x04, // 1.2.3.4 131 132 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 133 0x05, 'l', 'o', 'c', 'a', 'l', 134 0x00, 135 0x00, 0x1C, // Type is AAAA 136 0x00, 0x01, // CLASS is IN 137 0x00, 0x00, // TTL (4 bytes) is 32768 second. 138 0x10, 0x00, 139 0x00, 0x10, // RDLENGTH is 16 140 0x01, 0x02, 0x03, 0x04, // 1.2.3.4 141 0x01, 0x02, 0x03, 0x04, 142 0x01, 0x02, 0x03, 0x04, 143 0x01, 0x02, 0x03, 0x04, 144}; 145 146 147const uint8 kGoodbyePacket[] = { 148 // Header 149 0x00, 0x00, // ID is zeroed out 150 0x80, 0x00, // Standard query response, RA, no error 151 0x00, 0x00, // No questions (for simplicity) 152 0x00, 0x02, // 1 RR (answers) 153 0x00, 0x00, // 0 authority RRs 154 0x00, 0x00, // 0 additional RRs 155 156 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 157 0x04, '_', 't', 'c', 'p', 158 0x05, 'l', 'o', 'c', 'a', 'l', 159 0x00, 160 0x00, 0x0c, // TYPE is PTR. 161 0x00, 0x01, // CLASS is IN. 162 0x00, 0x00, // TTL (4 bytes) is 0 seconds. 163 0x00, 0x00, 164 0x00, 0x0c, // RDLENGTH is 12 bytes. 165 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 166 0xc0, 0x0c, 167 168 169 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 170 0xc0, 0x0c, 171 0x00, 0x21, // Type is SRV 172 0x00, 0x01, // CLASS is IN 173 0x00, 0x00, // TTL (4 bytes) is 0 seconds. 174 0x00, 0x00, 175 0x00, 0x17, // RDLENGTH is 23 176 0x00, 0x00, 177 0x00, 0x00, 178 0x22, 0xb8, // port 8888 179 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 180 0x05, 'l', 'o', 'c', 'a', 'l', 181 0x00, 182}; 183 184const uint8 kAnnouncePacketRegistered[] = { 185 // Header 186 0x00, 0x00, // ID is zeroed out 187 0x80, 0x00, // Standard query response, RA, no error 188 0x00, 0x00, // No questions (for simplicity) 189 0x00, 0x01, // 1 RR (answers) 190 0x00, 0x00, // 0 authority RRs 191 0x00, 0x00, // 0 additional RRs 192 193 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 194 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 195 0x04, '_', 't', 'c', 'p', 196 0x05, 'l', 'o', 'c', 'a', 'l', 197 0x00, 198 0x00, 0x10, // TYPE is TXT. 199 0x00, 0x01, // CLASS is IN. 200 0x00, 0x00, // TTL (4 bytes) is 32768 seconds. 201 0x01, 0x00, 202 0x00, 0x3b, // RDLENGTH is 76 bytes. 203 0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd', 204 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ', 205 'd', 'e', 'v', 'i', 'c', 'e', 206 0x1e, 'n', 'o', 't', 'e', '=', 207 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ', 208 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', 209}; 210 211const char kResponseInfo[] = "{" 212 " \"x-privet-token\" : \"MyPrivetToken\"" 213 "}"; 214 215const char kResponseInfoWithID[] = "{" 216 " \"x-privet-token\" : \"MyPrivetToken\"," 217 " \"id\" : \"my_id\"" 218 "}"; 219 220const char kResponseRegisterStart[] = "{" 221 " \"action\": \"start\"," 222 " \"user\": \"user@host.com\"" 223 "}"; 224 225const char kResponseRegisterClaimTokenNoConfirm[] = "{" 226 " \"action\": \"getClaimToken\"," 227 " \"user\": \"user@host.com\"," 228 " \"error\": \"pending_user_action\"," 229 " \"timeout\": 1" 230 "}"; 231 232const char kResponseRegisterClaimTokenConfirm[] = "{" 233 " \"action\": \"getClaimToken\"," 234 " \"user\": \"user@host.com\"," 235 " \"token\": \"MySampleToken\"," 236 " \"claim_url\": \"http://someurl.com/\"" 237 "}"; 238 239const char kResponseCloudPrintConfirm[] = "{ \"success\": true }"; 240 241const char kResponseRegisterComplete[] = "{" 242 " \"action\": \"complete\"," 243 " \"user\": \"user@host.com\"," 244 " \"device_id\": \"my_id\"" 245 "}"; 246 247const char kResponseGaiaToken[] = "{" 248 " \"access_token\": \"at1\"," 249 " \"expires_in\": 3600," 250 " \"token_type\": \"Bearer\"" 251 "}"; 252 253const char kResponseGaiaId[] = "{" 254 " \"id\": \"12345\"" 255 "}"; 256 257const char kURLInfo[] = "http://1.2.3.4:8888/privet/info"; 258 259const char kURLRegisterStart[] = 260 "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com"; 261 262const char kURLRegisterClaimToken[] = 263 "http://1.2.3.4:8888/privet/register?action=getClaimToken&" 264 "user=user%40host.com"; 265 266const char kURLCloudPrintConfirm[] = 267 "https://www.google.com/cloudprint/confirm?token=MySampleToken"; 268 269const char kURLRegisterComplete[] = 270 "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com"; 271 272const char kURLGaiaToken[] = 273 "https://accounts.google.com/o/oauth2/token"; 274 275const char kSampleUser[] = "user@host.com"; 276 277class TestMessageLoopCondition { 278 public: 279 TestMessageLoopCondition() : signaled_(false), 280 waiting_(false) { 281 } 282 283 ~TestMessageLoopCondition() { 284 } 285 286 // Signal a waiting method that it can continue executing. 287 void Signal() { 288 signaled_ = true; 289 if (waiting_) 290 base::MessageLoop::current()->Quit(); 291 } 292 293 // Pause execution and recursively run the message loop until |Signal()| is 294 // called. Do not pause if |Signal()| has already been called. 295 void Wait() { 296 while (!signaled_) { 297 waiting_ = true; 298 base::MessageLoop::current()->Run(); 299 waiting_ = false; 300 } 301 signaled_ = false; 302 } 303 304 private: 305 bool signaled_; 306 bool waiting_; 307 308 DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition); 309}; 310 311class MockableFakeURLFetcherCreator { 312 public: 313 MockableFakeURLFetcherCreator() { 314 } 315 316 ~MockableFakeURLFetcherCreator() { 317 } 318 319 MOCK_METHOD1(OnCreateFakeURLFetcher, void(const std::string& url)); 320 321 scoped_ptr<net::FakeURLFetcher> CreateFakeURLFetcher( 322 const GURL& url, 323 net::URLFetcherDelegate* delegate, 324 const std::string& response_data, 325 net::HttpStatusCode response_code, 326 net::URLRequestStatus::Status status) { 327 OnCreateFakeURLFetcher(url.spec()); 328 return scoped_ptr<net::FakeURLFetcher>(new net::FakeURLFetcher( 329 url, delegate, response_data, response_code, status)); 330 } 331 332 net::FakeURLFetcherFactory::FakeURLFetcherCreator callback() { 333 return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher, 334 base::Unretained(this)); 335 } 336}; 337 338class LocalDiscoveryUITest : public WebUIBrowserTest { 339 public: 340 LocalDiscoveryUITest() : fake_fetcher_factory_( 341 &fetcher_impl_factory_, 342 fake_url_fetcher_creator_.callback()) { 343 } 344 virtual ~LocalDiscoveryUITest() { 345 } 346 347 virtual void SetUpOnMainThread() OVERRIDE { 348 WebUIBrowserTest::SetUpOnMainThread(); 349 350 test_service_discovery_client_ = new TestServiceDiscoveryClient(); 351 test_service_discovery_client_->Start(); 352 EXPECT_CALL(*test_service_discovery_client_, OnSendTo( 353 std::string((const char*)kQueryData, 354 sizeof(kQueryData)))) 355 .Times(AtLeast(2)) 356 .WillOnce(InvokeWithoutArgs(&condition_devices_listed_, 357 &TestMessageLoopCondition::Signal)) 358 .WillRepeatedly(Return()); 359 360 SigninManagerBase* signin_manager = 361 SigninManagerFactory::GetForProfile(browser()->profile()); 362 363#if defined(OS_CHROMEOS) 364 // Chrome OS initializes prefs::kGoogleServicesUsername to "stub user" so 365 // we need to override it as well. 366 browser()->profile()->GetPrefs()-> 367 SetString(prefs::kGoogleServicesUsername, kSampleUser); 368#endif 369 DCHECK(signin_manager); 370 signin_manager->SetAuthenticatedUsername(kSampleUser); 371 372 fake_fetcher_factory().SetFakeResponse( 373 GURL(kURLInfo), 374 kResponseInfo, 375 net::HTTP_OK, 376 net::URLRequestStatus::SUCCESS); 377 378 fake_fetcher_factory().SetFakeResponse( 379 GURL(kURLRegisterStart), 380 kResponseRegisterStart, 381 net::HTTP_OK, 382 net::URLRequestStatus::SUCCESS); 383 384 fake_fetcher_factory().SetFakeResponse( 385 GURL(kURLRegisterClaimToken), 386 kResponseRegisterClaimTokenNoConfirm, 387 net::HTTP_OK, 388 net::URLRequestStatus::SUCCESS); 389 390 fake_fetcher_factory().SetFakeResponse( 391 GURL(kURLCloudPrintConfirm), 392 kResponseCloudPrintConfirm, 393 net::HTTP_OK, 394 net::URLRequestStatus::SUCCESS); 395 396 fake_fetcher_factory().SetFakeResponse( 397 GURL(kURLRegisterComplete), 398 kResponseRegisterComplete, 399 net::HTTP_OK, 400 net::URLRequestStatus::SUCCESS); 401 402 fake_fetcher_factory().SetFakeResponse( 403 GURL(kURLGaiaToken), 404 kResponseGaiaToken, 405 net::HTTP_OK, 406 net::URLRequestStatus::SUCCESS); 407 408 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher( 409 kURLGaiaToken)) 410 .Times(AnyNumber()); 411 412 fake_fetcher_factory().SetFakeResponse( 413 GaiaUrls::GetInstance()->oauth_user_info_url(), 414 kResponseGaiaId, 415 net::HTTP_OK, 416 net::URLRequestStatus::SUCCESS); 417 418 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher( 419 GaiaUrls::GetInstance()->oauth_user_info_url().spec())) 420 .Times(AnyNumber()); 421 422 ProfileOAuth2TokenService* token_service = 423 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile()); 424 425 token_service->UpdateCredentials("user@host.com", "MyFakeToken"); 426 427 AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js"))); 428 } 429 430 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 431 WebUIBrowserTest::SetUpCommandLine(command_line); 432 } 433 434 void RunFor(base::TimeDelta time_period) { 435 base::CancelableCallback<void()> callback(base::Bind( 436 &base::MessageLoop::Quit, base::Unretained( 437 base::MessageLoop::current()))); 438 base::MessageLoop::current()->PostDelayedTask( 439 FROM_HERE, callback.callback(), time_period); 440 441 base::MessageLoop::current()->Run(); 442 callback.Cancel(); 443 } 444 445 TestServiceDiscoveryClient* test_service_discovery_client() { 446 return test_service_discovery_client_.get(); 447 } 448 449 TestMessageLoopCondition& condition_devices_listed() { 450 return condition_devices_listed_; 451 } 452 453 net::FakeURLFetcherFactory& fake_fetcher_factory() { 454 return fake_fetcher_factory_; 455 } 456 457 MockableFakeURLFetcherCreator& fake_url_fetcher_creator() { 458 return fake_url_fetcher_creator_; 459 } 460 461 private: 462 scoped_refptr<TestServiceDiscoveryClient> test_service_discovery_client_; 463 TestMessageLoopCondition condition_devices_listed_; 464 465 net::URLFetcherImplFactory fetcher_impl_factory_; 466 StrictMock<MockableFakeURLFetcherCreator> fake_url_fetcher_creator_; 467 net::FakeURLFetcherFactory fake_fetcher_factory_; 468 469 DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest); 470}; 471 472IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, EmptyTest) { 473 ui_test_utils::NavigateToURL(browser(), GURL( 474 chrome::kChromeUIDevicesURL)); 475 condition_devices_listed().Wait(); 476 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices")); 477} 478 479IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, AddRowTest) { 480 ui_test_utils::NavigateToURL(browser(), GURL( 481 chrome::kChromeUIDevicesURL)); 482 condition_devices_listed().Wait(); 483 484 test_service_discovery_client()->SimulateReceive( 485 kAnnouncePacket, sizeof(kAnnouncePacket)); 486 487 base::MessageLoop::current()->RunUntilIdle(); 488 489 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice")); 490 491 test_service_discovery_client()->SimulateReceive( 492 kGoodbyePacket, sizeof(kGoodbyePacket)); 493 494 RunFor(base::TimeDelta::FromMilliseconds(1100)); 495 496 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices")); 497} 498 499 500IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, RegisterTest) { 501 TestMessageLoopCondition condition_token_claimed; 502 503 ui_test_utils::NavigateToURL(browser(), GURL( 504 chrome::kChromeUIDevicesURL)); 505 condition_devices_listed().Wait(); 506 507 test_service_discovery_client()->SimulateReceive( 508 kAnnouncePacket, sizeof(kAnnouncePacket)); 509 510 base::MessageLoop::current()->RunUntilIdle(); 511 512 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice")); 513 514 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay")); 515 516 { 517 InSequence s; 518 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo)); 519 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher( 520 kURLRegisterStart)); 521 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher( 522 kURLRegisterClaimToken)) 523 .WillOnce(InvokeWithoutArgs(&condition_token_claimed, 524 &TestMessageLoopCondition::Signal)); 525 } 526 527 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin")); 528 529 condition_token_claimed.Wait(); 530 531 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1")); 532 533 fake_fetcher_factory().SetFakeResponse( 534 GURL(kURLRegisterClaimToken), 535 kResponseRegisterClaimTokenConfirm, 536 net::HTTP_OK, 537 net::URLRequestStatus::SUCCESS); 538 539 fake_fetcher_factory().SetFakeResponse( 540 GURL(kURLInfo), 541 kResponseInfoWithID, 542 net::HTTP_OK, 543 net::URLRequestStatus::SUCCESS); 544 545 { 546 InSequence s; 547 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher( 548 kURLRegisterClaimToken)); 549 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher( 550 kURLCloudPrintConfirm)); 551 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher( 552 kURLRegisterComplete)); 553 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo)) 554 .WillOnce(InvokeWithoutArgs(&condition_token_claimed, 555 &TestMessageLoopCondition::Signal)); 556 } 557 558 condition_token_claimed.Wait(); 559 560 test_service_discovery_client()->SimulateReceive( 561 kAnnouncePacketRegistered, sizeof(kAnnouncePacketRegistered)); 562 563 base::MessageLoop::current()->RunUntilIdle(); 564 565 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone")); 566} 567 568} // namespace 569 570} // namespace local_discovery 571