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.
5 #include "base/basictypes.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/browser.h"
19 #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
20 #include "chrome/common/chrome_switches.h"
21 #include "chrome/common/url_constants.h"
22 #include "chrome/test/base/ui_test_utils.h"
23 #include "chrome/test/base/web_ui_browsertest.h"
24 #include "google_apis/gaia/gaia_urls.h"
25 #include "net/http/http_status_code.h"
26 #include "net/url_request/test_url_fetcher_factory.h"
27 #include "net/url_request/url_request_status.h"
28 #include "net/url_request/url_request_test_util.h"
30 using testing::InvokeWithoutArgs
;
31 using testing::Return
;
32 using testing::AtLeast
;
33 using testing::DoDefault
;
35 using testing::InSequence
;
36 using testing::StrictMock
;
37 using testing::AnyNumber
;
39 using testing::InvokeWithoutArgs
;
40 using testing::Return
;
41 using testing::AtLeast
;
43 namespace local_discovery
{
47 const uint8 kQueryData
[] = {
50 0x00, 0x00, // Flags not set.
51 0x00, 0x01, // Set QDCOUNT (question count) to 1, all the
52 // rest are 0 for a query.
58 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
59 0x04, '_', 't', 'c', 'p',
60 0x05, 'l', 'o', 'c', 'a', 'l',
63 0x00, 0x0c, // QTYPE: A query.
64 0x00, 0x01, // QCLASS: IN class. Unicast bit not set.
67 const uint8 kAnnouncePacket
[] = {
69 0x00, 0x00, // ID is zeroed out
70 0x80, 0x00, // Standard query response, no error
71 0x00, 0x00, // No questions (for simplicity)
72 0x00, 0x05, // 5 RR (answers)
73 0x00, 0x00, // 0 authority RRs
74 0x00, 0x00, // 0 additional RRs
76 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
77 0x04, '_', 't', 'c', 'p',
78 0x05, 'l', 'o', 'c', 'a', 'l',
80 0x00, 0x0c, // TYPE is PTR.
81 0x00, 0x01, // CLASS is IN.
82 0x00, 0x00, // TTL (4 bytes) is 32768 second.
84 0x00, 0x0c, // RDLENGTH is 12 bytes.
85 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
88 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
90 0x00, 0x10, // TYPE is TXT.
91 0x00, 0x01, // CLASS is IN.
92 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
94 0x00, 0x34, // RDLENGTH is 69 bytes.
96 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
97 'd', 'e', 'v', 'i', 'c', 'e',
98 0x1e, 'n', 'o', 't', 'e', '=',
99 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
100 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
102 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
104 0x00, 0x21, // Type is SRV
105 0x00, 0x01, // CLASS is IN
106 0x00, 0x00, // TTL (4 bytes) is 32768 second.
108 0x00, 0x17, // RDLENGTH is 23
111 0x22, 0xb8, // port 8888
112 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
113 0x05, 'l', 'o', 'c', 'a', 'l',
116 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
117 0x05, 'l', 'o', 'c', 'a', 'l',
119 0x00, 0x01, // Type is A
120 0x00, 0x01, // CLASS is IN
121 0x00, 0x00, // TTL (4 bytes) is 32768 second.
123 0x00, 0x04, // RDLENGTH is 4
124 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
126 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
127 0x05, 'l', 'o', 'c', 'a', 'l',
129 0x00, 0x1C, // Type is AAAA
130 0x00, 0x01, // CLASS is IN
131 0x00, 0x00, // TTL (4 bytes) is 32768 second.
133 0x00, 0x10, // RDLENGTH is 16
134 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
135 0x01, 0x02, 0x03, 0x04,
136 0x01, 0x02, 0x03, 0x04,
137 0x01, 0x02, 0x03, 0x04,
141 const uint8 kGoodbyePacket
[] = {
143 0x00, 0x00, // ID is zeroed out
144 0x80, 0x00, // Standard query response, RA, no error
145 0x00, 0x00, // No questions (for simplicity)
146 0x00, 0x02, // 1 RR (answers)
147 0x00, 0x00, // 0 authority RRs
148 0x00, 0x00, // 0 additional RRs
150 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
151 0x04, '_', 't', 'c', 'p',
152 0x05, 'l', 'o', 'c', 'a', 'l',
154 0x00, 0x0c, // TYPE is PTR.
155 0x00, 0x01, // CLASS is IN.
156 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
158 0x00, 0x0c, // RDLENGTH is 12 bytes.
159 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
163 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
165 0x00, 0x21, // Type is SRV
166 0x00, 0x01, // CLASS is IN
167 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
169 0x00, 0x17, // RDLENGTH is 23
172 0x22, 0xb8, // port 8888
173 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
174 0x05, 'l', 'o', 'c', 'a', 'l',
178 const uint8 kAnnouncePacketRegistered
[] = {
180 0x00, 0x00, // ID is zeroed out
181 0x80, 0x00, // Standard query response, RA, no error
182 0x00, 0x00, // No questions (for simplicity)
183 0x00, 0x01, // 1 RR (answers)
184 0x00, 0x00, // 0 authority RRs
185 0x00, 0x00, // 0 additional RRs
187 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
188 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
189 0x04, '_', 't', 'c', 'p',
190 0x05, 'l', 'o', 'c', 'a', 'l',
192 0x00, 0x10, // TYPE is TXT.
193 0x00, 0x01, // CLASS is IN.
194 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
196 0x00, 0x3b, // RDLENGTH is 76 bytes.
197 0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd',
198 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
199 'd', 'e', 'v', 'i', 'c', 'e',
200 0x1e, 'n', 'o', 't', 'e', '=',
201 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
202 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
205 const char kResponseInfo
[] = "{"
206 " \"x-privet-token\" : \"MyPrivetToken\""
209 const char kResponseInfoWithID
[] = "{"
210 " \"x-privet-token\" : \"MyPrivetToken\","
211 " \"id\" : \"my_id\""
214 const char kResponseRegisterStart
[] = "{"
215 " \"action\": \"start\","
216 " \"user\": \"user@host.com\""
219 const char kResponseRegisterClaimTokenNoConfirm
[] = "{"
220 " \"action\": \"getClaimToken\","
221 " \"user\": \"user@host.com\","
222 " \"error\": \"pending_user_action\","
226 const char kResponseRegisterClaimTokenConfirm
[] = "{"
227 " \"action\": \"getClaimToken\","
228 " \"user\": \"user@host.com\","
229 " \"token\": \"MySampleToken\","
230 " \"claim_url\": \"http://someurl.com/\""
233 const char kResponseCloudPrintConfirm
[] = "{ \"success\": true }";
235 const char kResponseRegisterComplete
[] = "{"
236 " \"action\": \"complete\","
237 " \"user\": \"user@host.com\","
238 " \"device_id\": \"my_id\""
241 const char kResponseGaiaToken
[] = "{"
242 " \"access_token\": \"at1\","
243 " \"expires_in\": 3600,"
244 " \"token_type\": \"Bearer\""
247 const char kResponseGaiaId
[] = "{"
251 const char kURLInfo
[] = "http://1.2.3.4:8888/privet/info";
253 const char kURLRegisterStart
[] =
254 "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com";
256 const char kURLRegisterClaimToken
[] =
257 "http://1.2.3.4:8888/privet/register?action=getClaimToken&"
258 "user=user%40host.com";
260 const char kURLCloudPrintConfirm
[] =
261 "https://www.google.com/cloudprint/confirm?token=MySampleToken";
263 const char kURLRegisterComplete
[] =
264 "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com";
266 const char kURLGaiaToken
[] =
267 "https://accounts.google.com/o/oauth2/token";
269 const char kSampleUser
[] = "user@host.com";
271 class TestMessageLoopCondition
{
273 TestMessageLoopCondition() : signaled_(false),
277 ~TestMessageLoopCondition() {
280 // Signal a waiting method that it can continue executing.
284 base::MessageLoop::current()->Quit();
287 // Pause execution and recursively run the message loop until |Signal()| is
288 // called. Do not pause if |Signal()| has already been called.
292 base::MessageLoop::current()->Run();
302 DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition
);
305 class MockableFakeURLFetcherCreator
{
307 MockableFakeURLFetcherCreator() {
310 ~MockableFakeURLFetcherCreator() {
313 MOCK_METHOD1(OnCreateFakeURLFetcher
, void(const std::string
& url
));
315 scoped_ptr
<net::FakeURLFetcher
> CreateFakeURLFetcher(
317 net::URLFetcherDelegate
* delegate
,
318 const std::string
& response_data
,
319 net::HttpStatusCode response_code
,
320 net::URLRequestStatus::Status status
) {
321 OnCreateFakeURLFetcher(url
.spec());
322 return scoped_ptr
<net::FakeURLFetcher
>(new net::FakeURLFetcher(
323 url
, delegate
, response_data
, response_code
, status
));
326 net::FakeURLFetcherFactory::FakeURLFetcherCreator
callback() {
327 return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher
,
328 base::Unretained(this));
332 class LocalDiscoveryUITest
: public WebUIBrowserTest
{
334 LocalDiscoveryUITest() : fake_fetcher_factory_(
335 &fetcher_impl_factory_
,
336 fake_url_fetcher_creator_
.callback()) {
338 virtual ~LocalDiscoveryUITest() {
341 virtual void SetUpOnMainThread() OVERRIDE
{
342 WebUIBrowserTest::SetUpOnMainThread();
344 test_service_discovery_client_
= new TestServiceDiscoveryClient();
345 test_service_discovery_client_
->Start();
346 EXPECT_CALL(*test_service_discovery_client_
, OnSendTo(
347 std::string((const char*)kQueryData
,
348 sizeof(kQueryData
))))
350 .WillOnce(InvokeWithoutArgs(&condition_devices_listed_
,
351 &TestMessageLoopCondition::Signal
))
352 .WillRepeatedly(Return());
354 SigninManagerBase
* signin_manager
=
355 SigninManagerFactory::GetForProfile(browser()->profile());
357 DCHECK(signin_manager
);
358 signin_manager
->SetAuthenticatedUsername(kSampleUser
);
360 fake_fetcher_factory().SetFakeResponse(
364 net::URLRequestStatus::SUCCESS
);
366 fake_fetcher_factory().SetFakeResponse(
367 GURL(kURLRegisterStart
),
368 kResponseRegisterStart
,
370 net::URLRequestStatus::SUCCESS
);
372 fake_fetcher_factory().SetFakeResponse(
373 GURL(kURLRegisterClaimToken
),
374 kResponseRegisterClaimTokenNoConfirm
,
376 net::URLRequestStatus::SUCCESS
);
378 fake_fetcher_factory().SetFakeResponse(
379 GURL(kURLCloudPrintConfirm
),
380 kResponseCloudPrintConfirm
,
382 net::URLRequestStatus::SUCCESS
);
384 fake_fetcher_factory().SetFakeResponse(
385 GURL(kURLRegisterComplete
),
386 kResponseRegisterComplete
,
388 net::URLRequestStatus::SUCCESS
);
390 fake_fetcher_factory().SetFakeResponse(
394 net::URLRequestStatus::SUCCESS
);
396 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
400 fake_fetcher_factory().SetFakeResponse(
401 GaiaUrls::GetInstance()->oauth_user_info_url(),
404 net::URLRequestStatus::SUCCESS
);
406 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
407 GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
410 ProfileOAuth2TokenService
* token_service
=
411 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
413 token_service
->UpdateCredentials("user@host.com", "MyFakeToken");
415 AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
418 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
419 WebUIBrowserTest::SetUpCommandLine(command_line
);
422 void RunFor(base::TimeDelta time_period
) {
423 base::CancelableCallback
<void()> callback(base::Bind(
424 &base::MessageLoop::Quit
, base::Unretained(
425 base::MessageLoop::current())));
426 base::MessageLoop::current()->PostDelayedTask(
427 FROM_HERE
, callback
.callback(), time_period
);
429 base::MessageLoop::current()->Run();
433 TestServiceDiscoveryClient
* test_service_discovery_client() {
434 return test_service_discovery_client_
.get();
437 TestMessageLoopCondition
& condition_devices_listed() {
438 return condition_devices_listed_
;
441 net::FakeURLFetcherFactory
& fake_fetcher_factory() {
442 return fake_fetcher_factory_
;
445 MockableFakeURLFetcherCreator
& fake_url_fetcher_creator() {
446 return fake_url_fetcher_creator_
;
450 scoped_refptr
<TestServiceDiscoveryClient
> test_service_discovery_client_
;
451 TestMessageLoopCondition condition_devices_listed_
;
453 net::URLFetcherImplFactory fetcher_impl_factory_
;
454 StrictMock
<MockableFakeURLFetcherCreator
> fake_url_fetcher_creator_
;
455 net::FakeURLFetcherFactory fake_fetcher_factory_
;
457 DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest
);
460 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, EmptyTest
) {
461 ui_test_utils::NavigateToURL(browser(), GURL(
462 chrome::kChromeUIDevicesURL
));
463 condition_devices_listed().Wait();
464 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
467 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, AddRowTest
) {
468 ui_test_utils::NavigateToURL(browser(), GURL(
469 chrome::kChromeUIDevicesURL
));
470 condition_devices_listed().Wait();
472 test_service_discovery_client()->SimulateReceive(
473 kAnnouncePacket
, sizeof(kAnnouncePacket
));
475 base::MessageLoop::current()->RunUntilIdle();
477 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
479 test_service_discovery_client()->SimulateReceive(
480 kGoodbyePacket
, sizeof(kGoodbyePacket
));
482 RunFor(base::TimeDelta::FromMilliseconds(1100));
484 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
488 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, RegisterTest
) {
489 TestMessageLoopCondition condition_token_claimed
;
491 ui_test_utils::NavigateToURL(browser(), GURL(
492 chrome::kChromeUIDevicesURL
));
493 condition_devices_listed().Wait();
495 test_service_discovery_client()->SimulateReceive(
496 kAnnouncePacket
, sizeof(kAnnouncePacket
));
498 base::MessageLoop::current()->RunUntilIdle();
500 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
502 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay"));
506 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
));
507 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
509 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
510 kURLRegisterClaimToken
))
511 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
512 &TestMessageLoopCondition::Signal
));
515 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin"));
517 condition_token_claimed
.Wait();
519 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1"));
521 fake_fetcher_factory().SetFakeResponse(
522 GURL(kURLRegisterClaimToken
),
523 kResponseRegisterClaimTokenConfirm
,
525 net::URLRequestStatus::SUCCESS
);
527 fake_fetcher_factory().SetFakeResponse(
531 net::URLRequestStatus::SUCCESS
);
535 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
536 kURLRegisterClaimToken
));
537 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
538 kURLCloudPrintConfirm
));
539 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
540 kURLRegisterComplete
));
541 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
))
542 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
543 &TestMessageLoopCondition::Signal
));
546 condition_token_claimed
.Wait();
548 test_service_discovery_client()->SimulateReceive(
549 kAnnouncePacketRegistered
, sizeof(kAnnouncePacketRegistered
));
551 base::MessageLoop::current()->RunUntilIdle();
553 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone"));
558 } // namespace local_discovery