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/location.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "chrome/browser/local_discovery/test_service_discovery_client.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
16 #include "chrome/browser/signin/signin_manager_factory.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/browser/ui/webui/local_discovery/local_discovery_ui_handler.h"
19 #include "chrome/common/chrome_constants.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_browser_test.h"
24 #include "components/signin/core/browser/profile_oauth2_token_service.h"
25 #include "components/signin/core/browser/signin_manager.h"
26 #include "components/signin/core/browser/signin_manager_base.h"
27 #include "google_apis/gaia/gaia_urls.h"
28 #include "net/http/http_status_code.h"
29 #include "net/url_request/test_url_fetcher_factory.h"
30 #include "net/url_request/url_request_status.h"
31 #include "net/url_request/url_request_test_util.h"
33 #if defined(OS_CHROMEOS)
34 #include "base/prefs/pref_service.h"
35 #include "chrome/common/pref_names.h"
36 #include "chromeos/chromeos_switches.h"
39 using testing::InvokeWithoutArgs
;
40 using testing::Return
;
41 using testing::AtLeast
;
42 using testing::DoDefault
;
44 using testing::InSequence
;
45 using testing::StrictMock
;
46 using testing::AnyNumber
;
48 using testing::InvokeWithoutArgs
;
49 using testing::Return
;
50 using testing::AtLeast
;
52 namespace local_discovery
{
56 const uint8 kQueryData
[] = {
59 0x00, 0x00, // Flags not set.
60 0x00, 0x01, // Set QDCOUNT (question count) to 1, all the
61 // rest are 0 for a query.
67 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
68 0x04, '_', 't', 'c', 'p',
69 0x05, 'l', 'o', 'c', 'a', 'l',
72 0x00, 0x0c, // QTYPE: A query.
73 0x00, 0x01, // QCLASS: IN class. Unicast bit not set.
76 const uint8 kAnnouncePacket
[] = {
78 0x00, 0x00, // ID is zeroed out
79 0x80, 0x00, // Standard query response, no error
80 0x00, 0x00, // No questions (for simplicity)
81 0x00, 0x05, // 5 RR (answers)
82 0x00, 0x00, // 0 authority RRs
83 0x00, 0x00, // 0 additional RRs
85 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
86 0x04, '_', 't', 'c', 'p',
87 0x05, 'l', 'o', 'c', 'a', 'l',
89 0x00, 0x0c, // TYPE is PTR.
90 0x00, 0x01, // CLASS is IN.
91 0x00, 0x00, // TTL (4 bytes) is 32768 second.
93 0x00, 0x0c, // RDLENGTH is 12 bytes.
94 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
97 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
99 0x00, 0x10, // TYPE is TXT.
100 0x00, 0x01, // CLASS is IN.
101 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
103 0x00, 0x41, // RDLENGTH is 69 bytes.
105 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
106 'd', 'e', 'v', 'i', 'c', 'e',
107 0x1e, 'n', 'o', 't', 'e', '=',
108 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
109 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
110 0x0c, 't', 'y', 'p', 'e', '=', 'p', 'r', 'i', 'n', 't', 'e', 'r',
112 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
114 0x00, 0x21, // Type is SRV
115 0x00, 0x01, // CLASS is IN
116 0x00, 0x00, // TTL (4 bytes) is 32768 second.
118 0x00, 0x17, // RDLENGTH is 23
121 0x22, 0xb8, // port 8888
122 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
123 0x05, 'l', 'o', 'c', 'a', 'l',
126 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
127 0x05, 'l', 'o', 'c', 'a', 'l',
129 0x00, 0x01, // Type is A
130 0x00, 0x01, // CLASS is IN
131 0x00, 0x00, // TTL (4 bytes) is 32768 second.
133 0x00, 0x04, // RDLENGTH is 4
134 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
136 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
137 0x05, 'l', 'o', 'c', 'a', 'l',
139 0x00, 0x1C, // Type is AAAA
140 0x00, 0x01, // CLASS is IN
141 0x00, 0x00, // TTL (4 bytes) is 32768 second.
143 0x00, 0x10, // RDLENGTH is 16
144 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
145 0x01, 0x02, 0x03, 0x04,
146 0x01, 0x02, 0x03, 0x04,
147 0x01, 0x02, 0x03, 0x04,
151 const uint8 kGoodbyePacket
[] = {
153 0x00, 0x00, // ID is zeroed out
154 0x80, 0x00, // Standard query response, RA, no error
155 0x00, 0x00, // No questions (for simplicity)
156 0x00, 0x02, // 1 RR (answers)
157 0x00, 0x00, // 0 authority RRs
158 0x00, 0x00, // 0 additional RRs
160 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
161 0x04, '_', 't', 'c', 'p',
162 0x05, 'l', 'o', 'c', 'a', 'l',
164 0x00, 0x0c, // TYPE is PTR.
165 0x00, 0x01, // CLASS is IN.
166 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
168 0x00, 0x0c, // RDLENGTH is 12 bytes.
169 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
173 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
175 0x00, 0x21, // Type is SRV
176 0x00, 0x01, // CLASS is IN
177 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
179 0x00, 0x17, // RDLENGTH is 23
182 0x22, 0xb8, // port 8888
183 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
184 0x05, 'l', 'o', 'c', 'a', 'l',
188 const uint8 kAnnouncePacketRegistered
[] = {
190 0x00, 0x00, // ID is zeroed out
191 0x80, 0x00, // Standard query response, RA, no error
192 0x00, 0x00, // No questions (for simplicity)
193 0x00, 0x01, // 1 RR (answers)
194 0x00, 0x00, // 0 authority RRs
195 0x00, 0x00, // 0 additional RRs
197 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
198 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
199 0x04, '_', 't', 'c', 'p',
200 0x05, 'l', 'o', 'c', 'a', 'l',
202 0x00, 0x10, // TYPE is TXT.
203 0x00, 0x01, // CLASS is IN.
204 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
206 0x00, 0x3b, // RDLENGTH is 76 bytes.
207 0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd',
208 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
209 'd', 'e', 'v', 'i', 'c', 'e',
210 0x1e, 'n', 'o', 't', 'e', '=',
211 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
212 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
215 const char kResponseInfo
[] = "{"
216 " \"x-privet-token\" : \"MyPrivetToken\""
219 const char kResponseInfoWithID
[] = "{"
220 " \"x-privet-token\" : \"MyPrivetToken\","
221 " \"id\" : \"my_id\""
224 const char kResponseRegisterStart
[] = "{"
225 " \"action\": \"start\","
226 " \"user\": \"user@host.com\""
229 const char kResponseRegisterClaimTokenNoConfirm
[] = "{"
230 " \"action\": \"getClaimToken\","
231 " \"user\": \"user@host.com\","
232 " \"error\": \"pending_user_action\","
236 const char kResponseRegisterClaimTokenConfirm
[] = "{"
237 " \"action\": \"getClaimToken\","
238 " \"user\": \"user@host.com\","
239 " \"token\": \"MySampleToken\","
240 " \"claim_url\": \"http://someurl.com/\""
243 const char kResponseCloudPrintConfirm
[] = "{ \"success\": true }";
245 const char kResponseRegisterComplete
[] = "{"
246 " \"action\": \"complete\","
247 " \"user\": \"user@host.com\","
248 " \"device_id\": \"my_id\""
251 const char kResponseGaiaToken
[] = "{"
252 " \"access_token\": \"at1\","
253 " \"expires_in\": 3600,"
254 " \"token_type\": \"Bearer\""
257 const char kResponseGaiaId
[] = "{"
261 const char kURLInfo
[] = "http://1.2.3.4:8888/privet/info";
263 const char kURLRegisterStart
[] =
264 "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com";
266 const char kURLRegisterClaimToken
[] =
267 "http://1.2.3.4:8888/privet/register?action=getClaimToken&"
268 "user=user%40host.com";
270 const char kURLCloudPrintConfirm
[] =
271 "https://www.google.com/cloudprint/confirm?token=MySampleToken";
273 const char kURLRegisterComplete
[] =
274 "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com";
276 const char kSampleGaiaId
[] = "12345";
277 const char kSampleUser
[] = "user@host.com";
279 class TestMessageLoopCondition
{
281 TestMessageLoopCondition() : signaled_(false),
285 ~TestMessageLoopCondition() {
288 // Signal a waiting method that it can continue executing.
292 base::MessageLoop::current()->Quit();
295 // Pause execution and recursively run the message loop until |Signal()| is
296 // called. Do not pause if |Signal()| has already been called.
300 base::MessageLoop::current()->Run();
310 DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition
);
313 class MockableFakeURLFetcherCreator
{
315 MockableFakeURLFetcherCreator() {
318 ~MockableFakeURLFetcherCreator() {
321 MOCK_METHOD1(OnCreateFakeURLFetcher
, void(const std::string
& url
));
323 scoped_ptr
<net::FakeURLFetcher
> CreateFakeURLFetcher(
325 net::URLFetcherDelegate
* delegate
,
326 const std::string
& response_data
,
327 net::HttpStatusCode response_code
,
328 net::URLRequestStatus::Status status
) {
329 OnCreateFakeURLFetcher(url
.spec());
330 return scoped_ptr
<net::FakeURLFetcher
>(new net::FakeURLFetcher(
331 url
, delegate
, response_data
, response_code
, status
));
334 net::FakeURLFetcherFactory::FakeURLFetcherCreator
callback() {
335 return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher
,
336 base::Unretained(this));
340 class LocalDiscoveryUITest
: public WebUIBrowserTest
{
342 LocalDiscoveryUITest() : fake_fetcher_factory_(
343 &fetcher_impl_factory_
,
344 fake_url_fetcher_creator_
.callback()) {
346 ~LocalDiscoveryUITest() override
{
349 void SetUpOnMainThread() override
{
350 WebUIBrowserTest::SetUpOnMainThread();
352 test_service_discovery_client_
= new TestServiceDiscoveryClient();
353 test_service_discovery_client_
->Start();
355 *test_service_discovery_client_
.get(),
356 OnSendTo(std::string((const char*)kQueryData
, sizeof(kQueryData
))))
358 .WillOnce(InvokeWithoutArgs(&condition_devices_listed_
,
359 &TestMessageLoopCondition::Signal
))
360 .WillRepeatedly(Return());
362 SigninManagerBase
* signin_manager
=
363 SigninManagerFactory::GetForProfile(browser()->profile());
365 DCHECK(signin_manager
);
366 signin_manager
->SetAuthenticatedAccountInfo(kSampleGaiaId
, kSampleUser
);
368 fake_fetcher_factory().SetFakeResponse(
372 net::URLRequestStatus::SUCCESS
);
374 fake_fetcher_factory().SetFakeResponse(
375 GURL(kURLRegisterStart
),
376 kResponseRegisterStart
,
378 net::URLRequestStatus::SUCCESS
);
380 fake_fetcher_factory().SetFakeResponse(
381 GURL(kURLRegisterClaimToken
),
382 kResponseRegisterClaimTokenNoConfirm
,
384 net::URLRequestStatus::SUCCESS
);
386 fake_fetcher_factory().SetFakeResponse(
387 GURL(kURLCloudPrintConfirm
),
388 kResponseCloudPrintConfirm
,
390 net::URLRequestStatus::SUCCESS
);
392 fake_fetcher_factory().SetFakeResponse(
393 GURL(kURLRegisterComplete
),
394 kResponseRegisterComplete
,
396 net::URLRequestStatus::SUCCESS
);
398 fake_fetcher_factory().SetFakeResponse(
399 GaiaUrls::GetInstance()->oauth2_token_url(),
402 net::URLRequestStatus::SUCCESS
);
404 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
405 GaiaUrls::GetInstance()->oauth2_token_url().spec()))
408 fake_fetcher_factory().SetFakeResponse(
409 GaiaUrls::GetInstance()->oauth_user_info_url(),
412 net::URLRequestStatus::SUCCESS
);
414 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
415 GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
418 ProfileOAuth2TokenService
* token_service
=
419 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
421 token_service
->UpdateCredentials(
422 signin_manager
->GetAuthenticatedAccountId(), "MyFakeToken");
424 AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
427 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
428 #if defined(OS_CHROMEOS)
429 // On chromeos, don't sign in with the stub-user automatically. Use the
430 // kLoginUser instead.
431 command_line
->AppendSwitchASCII(chromeos::switches::kLoginUser
,
433 command_line
->AppendSwitchASCII(chromeos::switches::kLoginProfile
,
434 chrome::kTestUserProfileDir
);
436 WebUIBrowserTest::SetUpCommandLine(command_line
);
439 void RunFor(base::TimeDelta time_period
) {
440 base::CancelableCallback
<void()> callback(base::Bind(
441 &base::MessageLoop::Quit
, base::Unretained(
442 base::MessageLoop::current())));
443 base::MessageLoop::current()->task_runner()->PostDelayedTask(
444 FROM_HERE
, callback
.callback(), time_period
);
446 base::MessageLoop::current()->Run();
450 TestServiceDiscoveryClient
* test_service_discovery_client() {
451 return test_service_discovery_client_
.get();
454 TestMessageLoopCondition
& condition_devices_listed() {
455 return condition_devices_listed_
;
458 net::FakeURLFetcherFactory
& fake_fetcher_factory() {
459 return fake_fetcher_factory_
;
462 MockableFakeURLFetcherCreator
& fake_url_fetcher_creator() {
463 return fake_url_fetcher_creator_
;
467 scoped_refptr
<TestServiceDiscoveryClient
> test_service_discovery_client_
;
468 TestMessageLoopCondition condition_devices_listed_
;
470 net::URLFetcherImplFactory fetcher_impl_factory_
;
471 StrictMock
<MockableFakeURLFetcherCreator
> fake_url_fetcher_creator_
;
472 net::FakeURLFetcherFactory fake_fetcher_factory_
;
474 DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest
);
477 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, EmptyTest
) {
478 ui_test_utils::NavigateToURL(browser(), GURL(
479 chrome::kChromeUIDevicesURL
));
480 condition_devices_listed().Wait();
481 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
484 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, AddRowTest
) {
485 ui_test_utils::NavigateToURL(browser(), GURL(
486 chrome::kChromeUIDevicesURL
));
487 condition_devices_listed().Wait();
489 test_service_discovery_client()->SimulateReceive(
490 kAnnouncePacket
, sizeof(kAnnouncePacket
));
492 base::MessageLoop::current()->RunUntilIdle();
494 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
496 test_service_discovery_client()->SimulateReceive(
497 kGoodbyePacket
, sizeof(kGoodbyePacket
));
499 RunFor(base::TimeDelta::FromMilliseconds(1100));
501 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
505 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, RegisterTest
) {
506 TestMessageLoopCondition condition_token_claimed
;
508 ui_test_utils::NavigateToURL(browser(), GURL(
509 chrome::kChromeUIDevicesURL
));
510 condition_devices_listed().Wait();
512 test_service_discovery_client()->SimulateReceive(
513 kAnnouncePacket
, sizeof(kAnnouncePacket
));
515 base::MessageLoop::current()->RunUntilIdle();
517 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
519 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay"));
523 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
));
524 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
526 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
527 kURLRegisterClaimToken
))
528 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
529 &TestMessageLoopCondition::Signal
));
532 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin"));
534 condition_token_claimed
.Wait();
536 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1"));
538 fake_fetcher_factory().SetFakeResponse(
539 GURL(kURLRegisterClaimToken
),
540 kResponseRegisterClaimTokenConfirm
,
542 net::URLRequestStatus::SUCCESS
);
544 fake_fetcher_factory().SetFakeResponse(
548 net::URLRequestStatus::SUCCESS
);
552 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
553 kURLRegisterClaimToken
));
554 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
555 kURLCloudPrintConfirm
));
556 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
557 kURLRegisterComplete
));
558 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
))
559 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
560 &TestMessageLoopCondition::Signal
));
563 condition_token_claimed
.Wait();
565 test_service_discovery_client()->SimulateReceive(
566 kAnnouncePacketRegistered
, sizeof(kAnnouncePacketRegistered
));
568 base::MessageLoop::current()->RunUntilIdle();
570 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone"));
575 } // namespace local_discovery