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/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_constants.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/url_constants.h"
21 #include "chrome/test/base/ui_test_utils.h"
22 #include "chrome/test/base/web_ui_browser_test.h"
23 #include "components/signin/core/browser/profile_oauth2_token_service.h"
24 #include "components/signin/core/browser/signin_manager.h"
25 #include "components/signin/core/browser/signin_manager_base.h"
26 #include "google_apis/gaia/gaia_urls.h"
27 #include "net/http/http_status_code.h"
28 #include "net/url_request/test_url_fetcher_factory.h"
29 #include "net/url_request/url_request_status.h"
30 #include "net/url_request/url_request_test_util.h"
32 #if defined(OS_CHROMEOS)
33 #include "base/prefs/pref_service.h"
34 #include "chrome/common/pref_names.h"
35 #include "chromeos/chromeos_switches.h"
38 using testing::InvokeWithoutArgs
;
39 using testing::Return
;
40 using testing::AtLeast
;
41 using testing::DoDefault
;
43 using testing::InSequence
;
44 using testing::StrictMock
;
45 using testing::AnyNumber
;
47 using testing::InvokeWithoutArgs
;
48 using testing::Return
;
49 using testing::AtLeast
;
51 namespace local_discovery
{
55 const uint8 kQueryData
[] = {
58 0x00, 0x00, // Flags not set.
59 0x00, 0x01, // Set QDCOUNT (question count) to 1, all the
60 // rest are 0 for a query.
66 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
67 0x04, '_', 't', 'c', 'p',
68 0x05, 'l', 'o', 'c', 'a', 'l',
71 0x00, 0x0c, // QTYPE: A query.
72 0x00, 0x01, // QCLASS: IN class. Unicast bit not set.
75 const uint8 kAnnouncePacket
[] = {
77 0x00, 0x00, // ID is zeroed out
78 0x80, 0x00, // Standard query response, no error
79 0x00, 0x00, // No questions (for simplicity)
80 0x00, 0x05, // 5 RR (answers)
81 0x00, 0x00, // 0 authority RRs
82 0x00, 0x00, // 0 additional RRs
84 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
85 0x04, '_', 't', 'c', 'p',
86 0x05, 'l', 'o', 'c', 'a', 'l',
88 0x00, 0x0c, // TYPE is PTR.
89 0x00, 0x01, // CLASS is IN.
90 0x00, 0x00, // TTL (4 bytes) is 32768 second.
92 0x00, 0x0c, // RDLENGTH is 12 bytes.
93 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
96 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
98 0x00, 0x10, // TYPE is TXT.
99 0x00, 0x01, // CLASS is IN.
100 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
102 0x00, 0x41, // RDLENGTH is 69 bytes.
104 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
105 'd', 'e', 'v', 'i', 'c', 'e',
106 0x1e, 'n', 'o', 't', 'e', '=',
107 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
108 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
109 0x0c, 't', 'y', 'p', 'e', '=', 'p', 'r', 'i', 'n', 't', 'e', 'r',
111 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
113 0x00, 0x21, // Type is SRV
114 0x00, 0x01, // CLASS is IN
115 0x00, 0x00, // TTL (4 bytes) is 32768 second.
117 0x00, 0x17, // RDLENGTH is 23
120 0x22, 0xb8, // port 8888
121 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
122 0x05, 'l', 'o', 'c', 'a', 'l',
125 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
126 0x05, 'l', 'o', 'c', 'a', 'l',
128 0x00, 0x01, // Type is A
129 0x00, 0x01, // CLASS is IN
130 0x00, 0x00, // TTL (4 bytes) is 32768 second.
132 0x00, 0x04, // RDLENGTH is 4
133 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
135 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
136 0x05, 'l', 'o', 'c', 'a', 'l',
138 0x00, 0x1C, // Type is AAAA
139 0x00, 0x01, // CLASS is IN
140 0x00, 0x00, // TTL (4 bytes) is 32768 second.
142 0x00, 0x10, // RDLENGTH is 16
143 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
144 0x01, 0x02, 0x03, 0x04,
145 0x01, 0x02, 0x03, 0x04,
146 0x01, 0x02, 0x03, 0x04,
150 const uint8 kGoodbyePacket
[] = {
152 0x00, 0x00, // ID is zeroed out
153 0x80, 0x00, // Standard query response, RA, no error
154 0x00, 0x00, // No questions (for simplicity)
155 0x00, 0x02, // 1 RR (answers)
156 0x00, 0x00, // 0 authority RRs
157 0x00, 0x00, // 0 additional RRs
159 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
160 0x04, '_', 't', 'c', 'p',
161 0x05, 'l', 'o', 'c', 'a', 'l',
163 0x00, 0x0c, // TYPE is PTR.
164 0x00, 0x01, // CLASS is IN.
165 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
167 0x00, 0x0c, // RDLENGTH is 12 bytes.
168 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
172 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
174 0x00, 0x21, // Type is SRV
175 0x00, 0x01, // CLASS is IN
176 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
178 0x00, 0x17, // RDLENGTH is 23
181 0x22, 0xb8, // port 8888
182 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
183 0x05, 'l', 'o', 'c', 'a', 'l',
187 const uint8 kAnnouncePacketRegistered
[] = {
189 0x00, 0x00, // ID is zeroed out
190 0x80, 0x00, // Standard query response, RA, no error
191 0x00, 0x00, // No questions (for simplicity)
192 0x00, 0x01, // 1 RR (answers)
193 0x00, 0x00, // 0 authority RRs
194 0x00, 0x00, // 0 additional RRs
196 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
197 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
198 0x04, '_', 't', 'c', 'p',
199 0x05, 'l', 'o', 'c', 'a', 'l',
201 0x00, 0x10, // TYPE is TXT.
202 0x00, 0x01, // CLASS is IN.
203 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
205 0x00, 0x3b, // RDLENGTH is 76 bytes.
206 0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd',
207 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
208 'd', 'e', 'v', 'i', 'c', 'e',
209 0x1e, 'n', 'o', 't', 'e', '=',
210 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
211 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
214 const char kResponseInfo
[] = "{"
215 " \"x-privet-token\" : \"MyPrivetToken\""
218 const char kResponseInfoWithID
[] = "{"
219 " \"x-privet-token\" : \"MyPrivetToken\","
220 " \"id\" : \"my_id\""
223 const char kResponseRegisterStart
[] = "{"
224 " \"action\": \"start\","
225 " \"user\": \"user@host.com\""
228 const char kResponseRegisterClaimTokenNoConfirm
[] = "{"
229 " \"action\": \"getClaimToken\","
230 " \"user\": \"user@host.com\","
231 " \"error\": \"pending_user_action\","
235 const char kResponseRegisterClaimTokenConfirm
[] = "{"
236 " \"action\": \"getClaimToken\","
237 " \"user\": \"user@host.com\","
238 " \"token\": \"MySampleToken\","
239 " \"claim_url\": \"http://someurl.com/\""
242 const char kResponseCloudPrintConfirm
[] = "{ \"success\": true }";
244 const char kResponseRegisterComplete
[] = "{"
245 " \"action\": \"complete\","
246 " \"user\": \"user@host.com\","
247 " \"device_id\": \"my_id\""
250 const char kResponseGaiaToken
[] = "{"
251 " \"access_token\": \"at1\","
252 " \"expires_in\": 3600,"
253 " \"token_type\": \"Bearer\""
256 const char kResponseGaiaId
[] = "{"
260 const char kURLInfo
[] = "http://1.2.3.4:8888/privet/info";
262 const char kURLRegisterStart
[] =
263 "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com";
265 const char kURLRegisterClaimToken
[] =
266 "http://1.2.3.4:8888/privet/register?action=getClaimToken&"
267 "user=user%40host.com";
269 const char kURLCloudPrintConfirm
[] =
270 "https://www.google.com/cloudprint/confirm?token=MySampleToken";
272 const char kURLRegisterComplete
[] =
273 "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com";
275 const char kURLGaiaToken
[] =
276 "https://accounts.google.com/o/oauth2/token";
278 const char kSampleGaiaId
[] = "12345";
279 const char kSampleUser
[] = "user@host.com";
281 class TestMessageLoopCondition
{
283 TestMessageLoopCondition() : signaled_(false),
287 ~TestMessageLoopCondition() {
290 // Signal a waiting method that it can continue executing.
294 base::MessageLoop::current()->Quit();
297 // Pause execution and recursively run the message loop until |Signal()| is
298 // called. Do not pause if |Signal()| has already been called.
302 base::MessageLoop::current()->Run();
312 DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition
);
315 class MockableFakeURLFetcherCreator
{
317 MockableFakeURLFetcherCreator() {
320 ~MockableFakeURLFetcherCreator() {
323 MOCK_METHOD1(OnCreateFakeURLFetcher
, void(const std::string
& url
));
325 scoped_ptr
<net::FakeURLFetcher
> CreateFakeURLFetcher(
327 net::URLFetcherDelegate
* delegate
,
328 const std::string
& response_data
,
329 net::HttpStatusCode response_code
,
330 net::URLRequestStatus::Status status
) {
331 OnCreateFakeURLFetcher(url
.spec());
332 return scoped_ptr
<net::FakeURLFetcher
>(new net::FakeURLFetcher(
333 url
, delegate
, response_data
, response_code
, status
));
336 net::FakeURLFetcherFactory::FakeURLFetcherCreator
callback() {
337 return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher
,
338 base::Unretained(this));
342 class LocalDiscoveryUITest
: public WebUIBrowserTest
{
344 LocalDiscoveryUITest() : fake_fetcher_factory_(
345 &fetcher_impl_factory_
,
346 fake_url_fetcher_creator_
.callback()) {
348 ~LocalDiscoveryUITest() override
{
351 void SetUpOnMainThread() override
{
352 WebUIBrowserTest::SetUpOnMainThread();
354 test_service_discovery_client_
= new TestServiceDiscoveryClient();
355 test_service_discovery_client_
->Start();
357 *test_service_discovery_client_
.get(),
358 OnSendTo(std::string((const char*)kQueryData
, sizeof(kQueryData
))))
360 .WillOnce(InvokeWithoutArgs(&condition_devices_listed_
,
361 &TestMessageLoopCondition::Signal
))
362 .WillRepeatedly(Return());
364 SigninManagerBase
* signin_manager
=
365 SigninManagerFactory::GetForProfile(browser()->profile());
367 DCHECK(signin_manager
);
368 signin_manager
->SetAuthenticatedAccountInfo(kSampleGaiaId
, kSampleUser
);
370 fake_fetcher_factory().SetFakeResponse(
374 net::URLRequestStatus::SUCCESS
);
376 fake_fetcher_factory().SetFakeResponse(
377 GURL(kURLRegisterStart
),
378 kResponseRegisterStart
,
380 net::URLRequestStatus::SUCCESS
);
382 fake_fetcher_factory().SetFakeResponse(
383 GURL(kURLRegisterClaimToken
),
384 kResponseRegisterClaimTokenNoConfirm
,
386 net::URLRequestStatus::SUCCESS
);
388 fake_fetcher_factory().SetFakeResponse(
389 GURL(kURLCloudPrintConfirm
),
390 kResponseCloudPrintConfirm
,
392 net::URLRequestStatus::SUCCESS
);
394 fake_fetcher_factory().SetFakeResponse(
395 GURL(kURLRegisterComplete
),
396 kResponseRegisterComplete
,
398 net::URLRequestStatus::SUCCESS
);
400 fake_fetcher_factory().SetFakeResponse(
404 net::URLRequestStatus::SUCCESS
);
406 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
410 fake_fetcher_factory().SetFakeResponse(
411 GaiaUrls::GetInstance()->oauth_user_info_url(),
414 net::URLRequestStatus::SUCCESS
);
416 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
417 GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
420 ProfileOAuth2TokenService
* token_service
=
421 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
423 token_service
->UpdateCredentials("user@host.com", "MyFakeToken");
425 AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
428 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
429 #if defined(OS_CHROMEOS)
430 // On chromeos, don't sign in with the stub-user automatically. Use the
431 // kLoginUser instead.
432 command_line
->AppendSwitchASCII(chromeos::switches::kLoginUser
,
434 command_line
->AppendSwitchASCII(chromeos::switches::kLoginProfile
,
435 chrome::kTestUserProfileDir
);
437 WebUIBrowserTest::SetUpCommandLine(command_line
);
440 void RunFor(base::TimeDelta time_period
) {
441 base::CancelableCallback
<void()> callback(base::Bind(
442 &base::MessageLoop::Quit
, base::Unretained(
443 base::MessageLoop::current())));
444 base::MessageLoop::current()->PostDelayedTask(
445 FROM_HERE
, callback
.callback(), time_period
);
447 base::MessageLoop::current()->Run();
451 TestServiceDiscoveryClient
* test_service_discovery_client() {
452 return test_service_discovery_client_
.get();
455 TestMessageLoopCondition
& condition_devices_listed() {
456 return condition_devices_listed_
;
459 net::FakeURLFetcherFactory
& fake_fetcher_factory() {
460 return fake_fetcher_factory_
;
463 MockableFakeURLFetcherCreator
& fake_url_fetcher_creator() {
464 return fake_url_fetcher_creator_
;
468 scoped_refptr
<TestServiceDiscoveryClient
> test_service_discovery_client_
;
469 TestMessageLoopCondition condition_devices_listed_
;
471 net::URLFetcherImplFactory fetcher_impl_factory_
;
472 StrictMock
<MockableFakeURLFetcherCreator
> fake_url_fetcher_creator_
;
473 net::FakeURLFetcherFactory fake_fetcher_factory_
;
475 DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest
);
478 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, EmptyTest
) {
479 ui_test_utils::NavigateToURL(browser(), GURL(
480 chrome::kChromeUIDevicesURL
));
481 condition_devices_listed().Wait();
482 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
485 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, AddRowTest
) {
486 ui_test_utils::NavigateToURL(browser(), GURL(
487 chrome::kChromeUIDevicesURL
));
488 condition_devices_listed().Wait();
490 test_service_discovery_client()->SimulateReceive(
491 kAnnouncePacket
, sizeof(kAnnouncePacket
));
493 base::MessageLoop::current()->RunUntilIdle();
495 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
497 test_service_discovery_client()->SimulateReceive(
498 kGoodbyePacket
, sizeof(kGoodbyePacket
));
500 RunFor(base::TimeDelta::FromMilliseconds(1100));
502 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
506 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, RegisterTest
) {
507 TestMessageLoopCondition condition_token_claimed
;
509 ui_test_utils::NavigateToURL(browser(), GURL(
510 chrome::kChromeUIDevicesURL
));
511 condition_devices_listed().Wait();
513 test_service_discovery_client()->SimulateReceive(
514 kAnnouncePacket
, sizeof(kAnnouncePacket
));
516 base::MessageLoop::current()->RunUntilIdle();
518 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
520 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay"));
524 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
));
525 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
527 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
528 kURLRegisterClaimToken
))
529 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
530 &TestMessageLoopCondition::Signal
));
533 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin"));
535 condition_token_claimed
.Wait();
537 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1"));
539 fake_fetcher_factory().SetFakeResponse(
540 GURL(kURLRegisterClaimToken
),
541 kResponseRegisterClaimTokenConfirm
,
543 net::URLRequestStatus::SUCCESS
);
545 fake_fetcher_factory().SetFakeResponse(
549 net::URLRequestStatus::SUCCESS
);
553 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
554 kURLRegisterClaimToken
));
555 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
556 kURLCloudPrintConfirm
));
557 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
558 kURLRegisterComplete
));
559 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
))
560 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
561 &TestMessageLoopCondition::Signal
));
564 condition_token_claimed
.Wait();
566 test_service_discovery_client()->SimulateReceive(
567 kAnnouncePacketRegistered
, sizeof(kAnnouncePacketRegistered
));
569 base::MessageLoop::current()->RunUntilIdle();
571 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone"));
576 } // namespace local_discovery