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