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, 0x01, // 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 const uint8 kAnnouncePacketRegistered
[] = {
165 0x00, 0x00, // ID is zeroed out
166 0x80, 0x00, // Standard query response, RA, no error
167 0x00, 0x00, // No questions (for simplicity)
168 0x00, 0x01, // 1 RR (answers)
169 0x00, 0x00, // 0 authority RRs
170 0x00, 0x00, // 0 additional RRs
172 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
173 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
174 0x04, '_', 't', 'c', 'p',
175 0x05, 'l', 'o', 'c', 'a', 'l',
177 0x00, 0x10, // TYPE is TXT.
178 0x00, 0x01, // CLASS is IN.
179 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
181 0x00, 0x3b, // RDLENGTH is 76 bytes.
182 0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd',
183 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
184 'd', 'e', 'v', 'i', 'c', 'e',
185 0x1e, 'n', 'o', 't', 'e', '=',
186 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
187 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
190 const char kResponseInfo
[] = "{"
191 " \"x-privet-token\" : \"MyPrivetToken\""
194 const char kResponseInfoWithID
[] = "{"
195 " \"x-privet-token\" : \"MyPrivetToken\","
196 " \"id\" : \"my_id\""
199 const char kResponseRegisterStart
[] = "{"
200 " \"action\": \"start\","
201 " \"user\": \"user@host.com\""
204 const char kResponseRegisterClaimTokenNoConfirm
[] = "{"
205 " \"action\": \"getClaimToken\","
206 " \"user\": \"user@host.com\","
207 " \"error\": \"pending_user_action\","
211 const char kResponseRegisterClaimTokenConfirm
[] = "{"
212 " \"action\": \"getClaimToken\","
213 " \"user\": \"user@host.com\","
214 " \"token\": \"MySampleToken\","
215 " \"claim_url\": \"http://someurl.com/\""
218 const char kResponseCloudPrintConfirm
[] = "{ \"success\": true }";
220 const char kResponseRegisterComplete
[] = "{"
221 " \"action\": \"complete\","
222 " \"user\": \"user@host.com\","
223 " \"device_id\": \"my_id\""
226 const char kResponseGaiaToken
[] = "{"
227 " \"access_token\": \"at1\","
228 " \"expires_in\": 3600,"
229 " \"token_type\": \"Bearer\""
232 const char kResponseGaiaId
[] = "{"
236 const char kURLInfo
[] = "http://1.2.3.4:8888/privet/info";
238 const char kURLRegisterStart
[] =
239 "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com";
241 const char kURLRegisterClaimToken
[] =
242 "http://1.2.3.4:8888/privet/register?action=getClaimToken&"
243 "user=user%40host.com";
245 const char kURLCloudPrintConfirm
[] =
246 "https://www.google.com/cloudprint/confirm?token=MySampleToken";
248 const char kURLRegisterComplete
[] =
249 "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com";
251 const char kURLGaiaToken
[] =
252 "https://accounts.google.com/o/oauth2/token";
254 const char kSampleUser
[] = "user@host.com";
256 class TestMessageLoopCondition
{
258 TestMessageLoopCondition() : signaled_(false),
262 ~TestMessageLoopCondition() {
265 // Signal a waiting method that it can continue executing.
269 base::MessageLoop::current()->Quit();
272 // Pause execution and recursively run the message loop until |Signal()| is
273 // called. Do not pause if |Signal()| has already been called.
277 base::MessageLoop::current()->Run();
287 DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition
);
290 class MockableFakeURLFetcherCreator
{
292 MockableFakeURLFetcherCreator() {
295 ~MockableFakeURLFetcherCreator() {
298 MOCK_METHOD1(OnCreateFakeURLFetcher
, void(const std::string
& url
));
300 scoped_ptr
<net::FakeURLFetcher
> CreateFakeURLFetcher(
302 net::URLFetcherDelegate
* delegate
,
303 const std::string
& response_data
,
304 net::HttpStatusCode response_code
,
305 net::URLRequestStatus::Status status
) {
306 OnCreateFakeURLFetcher(url
.spec());
307 return scoped_ptr
<net::FakeURLFetcher
>(new net::FakeURLFetcher(
308 url
, delegate
, response_data
, response_code
, status
));
311 net::FakeURLFetcherFactory::FakeURLFetcherCreator
callback() {
312 return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher
,
313 base::Unretained(this));
317 class LocalDiscoveryUITest
: public WebUIBrowserTest
{
319 LocalDiscoveryUITest() : fake_fetcher_factory_(
320 &fetcher_impl_factory_
,
321 fake_url_fetcher_creator_
.callback()) {
323 virtual ~LocalDiscoveryUITest() {
326 virtual void SetUpOnMainThread() OVERRIDE
{
327 WebUIBrowserTest::SetUpOnMainThread();
329 test_service_discovery_client_
= new TestServiceDiscoveryClient();
330 test_service_discovery_client_
->Start();
331 EXPECT_CALL(*test_service_discovery_client_
, OnSendTo(
332 std::string((const char*)kQueryData
,
333 sizeof(kQueryData
))))
335 .WillOnce(InvokeWithoutArgs(&condition_devices_listed_
,
336 &TestMessageLoopCondition::Signal
))
337 .WillRepeatedly(Return());
339 SigninManagerBase
* signin_manager
=
340 SigninManagerFactory::GetForProfile(browser()->profile());
342 DCHECK(signin_manager
);
343 signin_manager
->SetAuthenticatedUsername(kSampleUser
);
345 fake_fetcher_factory().SetFakeResponse(
349 net::URLRequestStatus::SUCCESS
);
351 fake_fetcher_factory().SetFakeResponse(
352 GURL(kURLRegisterStart
),
353 kResponseRegisterStart
,
355 net::URLRequestStatus::SUCCESS
);
357 fake_fetcher_factory().SetFakeResponse(
358 GURL(kURLRegisterClaimToken
),
359 kResponseRegisterClaimTokenNoConfirm
,
361 net::URLRequestStatus::SUCCESS
);
363 fake_fetcher_factory().SetFakeResponse(
364 GURL(kURLCloudPrintConfirm
),
365 kResponseCloudPrintConfirm
,
367 net::URLRequestStatus::SUCCESS
);
369 fake_fetcher_factory().SetFakeResponse(
370 GURL(kURLRegisterComplete
),
371 kResponseRegisterComplete
,
373 net::URLRequestStatus::SUCCESS
);
375 fake_fetcher_factory().SetFakeResponse(
379 net::URLRequestStatus::SUCCESS
);
381 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
385 fake_fetcher_factory().SetFakeResponse(
386 GaiaUrls::GetInstance()->oauth_user_info_url(),
389 net::URLRequestStatus::SUCCESS
);
391 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
392 GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
395 ProfileOAuth2TokenService
* token_service
=
396 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
398 token_service
->UpdateCredentials("user@host.com", "MyFakeToken");
400 AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
403 virtual void SetUpCommandLine(CommandLine
* command_line
) OVERRIDE
{
404 WebUIBrowserTest::SetUpCommandLine(command_line
);
407 void RunFor(base::TimeDelta time_period
) {
408 base::CancelableCallback
<void()> callback(base::Bind(
409 &base::MessageLoop::Quit
, base::Unretained(
410 base::MessageLoop::current())));
411 base::MessageLoop::current()->PostDelayedTask(
412 FROM_HERE
, callback
.callback(), time_period
);
414 base::MessageLoop::current()->Run();
418 TestServiceDiscoveryClient
* test_service_discovery_client() {
419 return test_service_discovery_client_
.get();
422 TestMessageLoopCondition
& condition_devices_listed() {
423 return condition_devices_listed_
;
426 net::FakeURLFetcherFactory
& fake_fetcher_factory() {
427 return fake_fetcher_factory_
;
430 MockableFakeURLFetcherCreator
& fake_url_fetcher_creator() {
431 return fake_url_fetcher_creator_
;
435 scoped_refptr
<TestServiceDiscoveryClient
> test_service_discovery_client_
;
436 TestMessageLoopCondition condition_devices_listed_
;
438 net::URLFetcherImplFactory fetcher_impl_factory_
;
439 StrictMock
<MockableFakeURLFetcherCreator
> fake_url_fetcher_creator_
;
440 net::FakeURLFetcherFactory fake_fetcher_factory_
;
442 DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest
);
445 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, EmptyTest
) {
446 ui_test_utils::NavigateToURL(browser(), GURL(
447 chrome::kChromeUIDevicesURL
));
448 condition_devices_listed().Wait();
449 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
452 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, AddRowTest
) {
453 ui_test_utils::NavigateToURL(browser(), GURL(
454 chrome::kChromeUIDevicesURL
));
455 condition_devices_listed().Wait();
457 test_service_discovery_client()->SimulateReceive(
458 kAnnouncePacket
, sizeof(kAnnouncePacket
));
460 base::MessageLoop::current()->RunUntilIdle();
462 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
464 test_service_discovery_client()->SimulateReceive(
465 kGoodbyePacket
, sizeof(kGoodbyePacket
));
467 RunFor(base::TimeDelta::FromMilliseconds(1100));
469 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
473 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest
, RegisterTest
) {
474 TestMessageLoopCondition condition_token_claimed
;
476 ui_test_utils::NavigateToURL(browser(), GURL(
477 chrome::kChromeUIDevicesURL
));
478 condition_devices_listed().Wait();
480 test_service_discovery_client()->SimulateReceive(
481 kAnnouncePacket
, sizeof(kAnnouncePacket
));
483 base::MessageLoop::current()->RunUntilIdle();
485 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
487 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay"));
491 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
));
492 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
494 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
495 kURLRegisterClaimToken
))
496 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
497 &TestMessageLoopCondition::Signal
));
500 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin"));
502 condition_token_claimed
.Wait();
504 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1"));
506 fake_fetcher_factory().SetFakeResponse(
507 GURL(kURLRegisterClaimToken
),
508 kResponseRegisterClaimTokenConfirm
,
510 net::URLRequestStatus::SUCCESS
);
512 fake_fetcher_factory().SetFakeResponse(
516 net::URLRequestStatus::SUCCESS
);
520 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
521 kURLRegisterClaimToken
));
522 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
523 kURLCloudPrintConfirm
));
524 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
525 kURLRegisterComplete
));
526 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo
))
527 .WillOnce(InvokeWithoutArgs(&condition_token_claimed
,
528 &TestMessageLoopCondition::Signal
));
531 condition_token_claimed
.Wait();
533 test_service_discovery_client()->SimulateReceive(
534 kAnnouncePacketRegistered
, sizeof(kAnnouncePacketRegistered
));
536 base::MessageLoop::current()->RunUntilIdle();
538 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone"));
543 } // namespace local_discovery