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 "chromeos/chromeos_switches.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"
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 kSampleUser
[] = "user@host.com";
280 class TestMessageLoopCondition
{
282 TestMessageLoopCondition() : signaled_(false),
286 ~TestMessageLoopCondition() {
289 // Signal a waiting method that it can continue executing.
293 base::MessageLoop::current()->Quit();
296 // Pause execution and recursively run the message loop until |Signal()| is
297 // called. Do not pause if |Signal()| has already been called.
301 base::MessageLoop::current()->Run();
311 DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition
);
314 class MockableFakeURLFetcherCreator
{
316 MockableFakeURLFetcherCreator() {
319 ~MockableFakeURLFetcherCreator() {
322 MOCK_METHOD1(OnCreateFakeURLFetcher
, void(const std::string
& url
));
324 scoped_ptr
<net::FakeURLFetcher
> CreateFakeURLFetcher(
326 net::URLFetcherDelegate
* delegate
,
327 const std::string
& response_data
,
328 net::HttpStatusCode response_code
,
329 net::URLRequestStatus::Status status
) {
330 OnCreateFakeURLFetcher(url
.spec());
331 return scoped_ptr
<net::FakeURLFetcher
>(new net::FakeURLFetcher(
332 url
, delegate
, response_data
, response_code
, status
));
335 net::FakeURLFetcherFactory::FakeURLFetcherCreator
callback() {
336 return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher
,
337 base::Unretained(this));
341 class LocalDiscoveryUITest
: public WebUIBrowserTest
{
343 LocalDiscoveryUITest() : fake_fetcher_factory_(
344 &fetcher_impl_factory_
,
345 fake_url_fetcher_creator_
.callback()) {
347 virtual ~LocalDiscoveryUITest() {
350 virtual void SetUpOnMainThread() override
{
351 WebUIBrowserTest::SetUpOnMainThread();
353 test_service_discovery_client_
= new TestServiceDiscoveryClient();
354 test_service_discovery_client_
->Start();
356 *test_service_discovery_client_
.get(),
357 OnSendTo(std::string((const char*)kQueryData
, sizeof(kQueryData
))))
359 .WillOnce(InvokeWithoutArgs(&condition_devices_listed_
,
360 &TestMessageLoopCondition::Signal
))
361 .WillRepeatedly(Return());
363 SigninManagerBase
* signin_manager
=
364 SigninManagerFactory::GetForProfile(browser()->profile());
366 DCHECK(signin_manager
);
367 signin_manager
->SetAuthenticatedUsername(kSampleUser
);
369 fake_fetcher_factory().SetFakeResponse(
373 net::URLRequestStatus::SUCCESS
);
375 fake_fetcher_factory().SetFakeResponse(
376 GURL(kURLRegisterStart
),
377 kResponseRegisterStart
,
379 net::URLRequestStatus::SUCCESS
);
381 fake_fetcher_factory().SetFakeResponse(
382 GURL(kURLRegisterClaimToken
),
383 kResponseRegisterClaimTokenNoConfirm
,
385 net::URLRequestStatus::SUCCESS
);
387 fake_fetcher_factory().SetFakeResponse(
388 GURL(kURLCloudPrintConfirm
),
389 kResponseCloudPrintConfirm
,
391 net::URLRequestStatus::SUCCESS
);
393 fake_fetcher_factory().SetFakeResponse(
394 GURL(kURLRegisterComplete
),
395 kResponseRegisterComplete
,
397 net::URLRequestStatus::SUCCESS
);
399 fake_fetcher_factory().SetFakeResponse(
403 net::URLRequestStatus::SUCCESS
);
405 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
409 fake_fetcher_factory().SetFakeResponse(
410 GaiaUrls::GetInstance()->oauth_user_info_url(),
413 net::URLRequestStatus::SUCCESS
);
415 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
416 GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
419 ProfileOAuth2TokenService
* token_service
=
420 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
422 token_service
->UpdateCredentials("user@host.com", "MyFakeToken");
424 AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
427 virtual 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()->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