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_switches.h"
19 #include "chrome/common/url_constants.h"
20 #include "chrome/test/base/ui_test_utils.h"
21 #include "chrome/test/base/web_ui_browser_test.h"
22 #include "components/signin/core/browser/profile_oauth2_token_service.h"
23 #include "components/signin/core/browser/signin_manager.h"
24 #include "components/signin/core/browser/signin_manager_base.h"
25 #include "google_apis/gaia/gaia_urls.h"
26 #include "net/http/http_status_code.h"
27 #include "net/url_request/test_url_fetcher_factory.h"
28 #include "net/url_request/url_request_status.h"
29 #include "net/url_request/url_request_test_util.h"
31 #if defined(OS_CHROMEOS)
32 #include "base/prefs/pref_service.h"
33 #include "chrome/common/pref_names.h"
36 using testing::InvokeWithoutArgs;
37 using testing::Return;
38 using testing::AtLeast;
39 using testing::DoDefault;
41 using testing::InSequence;
42 using testing::StrictMock;
43 using testing::AnyNumber;
45 using testing::InvokeWithoutArgs;
46 using testing::Return;
47 using testing::AtLeast;
49 namespace local_discovery {
53 const uint8 kQueryData[] = {
56 0x00, 0x00, // Flags not set.
57 0x00, 0x01, // Set QDCOUNT (question count) to 1, all the
58 // rest are 0 for a query.
64 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
65 0x04, '_', 't', 'c', 'p',
66 0x05, 'l', 'o', 'c', 'a', 'l',
69 0x00, 0x0c, // QTYPE: A query.
70 0x00, 0x01, // QCLASS: IN class. Unicast bit not set.
73 const uint8 kAnnouncePacket[] = {
75 0x00, 0x00, // ID is zeroed out
76 0x80, 0x00, // Standard query response, no error
77 0x00, 0x00, // No questions (for simplicity)
78 0x00, 0x05, // 5 RR (answers)
79 0x00, 0x00, // 0 authority RRs
80 0x00, 0x00, // 0 additional RRs
82 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
83 0x04, '_', 't', 'c', 'p',
84 0x05, 'l', 'o', 'c', 'a', 'l',
86 0x00, 0x0c, // TYPE is PTR.
87 0x00, 0x01, // CLASS is IN.
88 0x00, 0x00, // TTL (4 bytes) is 32768 second.
90 0x00, 0x0c, // RDLENGTH is 12 bytes.
91 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
94 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
96 0x00, 0x10, // TYPE is TXT.
97 0x00, 0x01, // CLASS is IN.
98 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
100 0x00, 0x41, // RDLENGTH is 69 bytes.
102 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
103 'd', 'e', 'v', 'i', 'c', 'e',
104 0x1e, 'n', 'o', 't', 'e', '=',
105 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
106 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
107 0x0c, 't', 'y', 'p', 'e', '=', 'p', 'r', 'i', 'n', 't', 'e', 'r',
109 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
111 0x00, 0x21, // Type is SRV
112 0x00, 0x01, // CLASS is IN
113 0x00, 0x00, // TTL (4 bytes) is 32768 second.
115 0x00, 0x17, // RDLENGTH is 23
118 0x22, 0xb8, // port 8888
119 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
120 0x05, 'l', 'o', 'c', 'a', 'l',
123 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
124 0x05, 'l', 'o', 'c', 'a', 'l',
126 0x00, 0x01, // Type is A
127 0x00, 0x01, // CLASS is IN
128 0x00, 0x00, // TTL (4 bytes) is 32768 second.
130 0x00, 0x04, // RDLENGTH is 4
131 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
133 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
134 0x05, 'l', 'o', 'c', 'a', 'l',
136 0x00, 0x1C, // Type is AAAA
137 0x00, 0x01, // CLASS is IN
138 0x00, 0x00, // TTL (4 bytes) is 32768 second.
140 0x00, 0x10, // RDLENGTH is 16
141 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
142 0x01, 0x02, 0x03, 0x04,
143 0x01, 0x02, 0x03, 0x04,
144 0x01, 0x02, 0x03, 0x04,
148 const uint8 kGoodbyePacket[] = {
150 0x00, 0x00, // ID is zeroed out
151 0x80, 0x00, // Standard query response, RA, no error
152 0x00, 0x00, // No questions (for simplicity)
153 0x00, 0x02, // 1 RR (answers)
154 0x00, 0x00, // 0 authority RRs
155 0x00, 0x00, // 0 additional RRs
157 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
158 0x04, '_', 't', 'c', 'p',
159 0x05, 'l', 'o', 'c', 'a', 'l',
161 0x00, 0x0c, // TYPE is PTR.
162 0x00, 0x01, // CLASS is IN.
163 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
165 0x00, 0x0c, // RDLENGTH is 12 bytes.
166 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
170 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
172 0x00, 0x21, // Type is SRV
173 0x00, 0x01, // CLASS is IN
174 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
176 0x00, 0x17, // RDLENGTH is 23
179 0x22, 0xb8, // port 8888
180 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
181 0x05, 'l', 'o', 'c', 'a', 'l',
185 const uint8 kAnnouncePacketRegistered[] = {
187 0x00, 0x00, // ID is zeroed out
188 0x80, 0x00, // Standard query response, RA, no error
189 0x00, 0x00, // No questions (for simplicity)
190 0x00, 0x01, // 1 RR (answers)
191 0x00, 0x00, // 0 authority RRs
192 0x00, 0x00, // 0 additional RRs
194 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
195 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
196 0x04, '_', 't', 'c', 'p',
197 0x05, 'l', 'o', 'c', 'a', 'l',
199 0x00, 0x10, // TYPE is TXT.
200 0x00, 0x01, // CLASS is IN.
201 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
203 0x00, 0x3b, // RDLENGTH is 76 bytes.
204 0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd',
205 0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
206 'd', 'e', 'v', 'i', 'c', 'e',
207 0x1e, 'n', 'o', 't', 'e', '=',
208 'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
209 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
212 const char kResponseInfo[] = "{"
213 " \"x-privet-token\" : \"MyPrivetToken\""
216 const char kResponseInfoWithID[] = "{"
217 " \"x-privet-token\" : \"MyPrivetToken\","
218 " \"id\" : \"my_id\""
221 const char kResponseRegisterStart[] = "{"
222 " \"action\": \"start\","
223 " \"user\": \"user@host.com\""
226 const char kResponseRegisterClaimTokenNoConfirm[] = "{"
227 " \"action\": \"getClaimToken\","
228 " \"user\": \"user@host.com\","
229 " \"error\": \"pending_user_action\","
233 const char kResponseRegisterClaimTokenConfirm[] = "{"
234 " \"action\": \"getClaimToken\","
235 " \"user\": \"user@host.com\","
236 " \"token\": \"MySampleToken\","
237 " \"claim_url\": \"http://someurl.com/\""
240 const char kResponseCloudPrintConfirm[] = "{ \"success\": true }";
242 const char kResponseRegisterComplete[] = "{"
243 " \"action\": \"complete\","
244 " \"user\": \"user@host.com\","
245 " \"device_id\": \"my_id\""
248 const char kResponseGaiaToken[] = "{"
249 " \"access_token\": \"at1\","
250 " \"expires_in\": 3600,"
251 " \"token_type\": \"Bearer\""
254 const char kResponseGaiaId[] = "{"
258 const char kURLInfo[] = "http://1.2.3.4:8888/privet/info";
260 const char kURLRegisterStart[] =
261 "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com";
263 const char kURLRegisterClaimToken[] =
264 "http://1.2.3.4:8888/privet/register?action=getClaimToken&"
265 "user=user%40host.com";
267 const char kURLCloudPrintConfirm[] =
268 "https://www.google.com/cloudprint/confirm?token=MySampleToken";
270 const char kURLRegisterComplete[] =
271 "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com";
273 const char kURLGaiaToken[] =
274 "https://accounts.google.com/o/oauth2/token";
276 const char kSampleUser[] = "user@host.com";
278 class TestMessageLoopCondition {
280 TestMessageLoopCondition() : signaled_(false),
284 ~TestMessageLoopCondition() {
287 // Signal a waiting method that it can continue executing.
291 base::MessageLoop::current()->Quit();
294 // Pause execution and recursively run the message loop until |Signal()| is
295 // called. Do not pause if |Signal()| has already been called.
299 base::MessageLoop::current()->Run();
309 DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition);
312 class MockableFakeURLFetcherCreator {
314 MockableFakeURLFetcherCreator() {
317 ~MockableFakeURLFetcherCreator() {
320 MOCK_METHOD1(OnCreateFakeURLFetcher, void(const std::string& url));
322 scoped_ptr<net::FakeURLFetcher> CreateFakeURLFetcher(
324 net::URLFetcherDelegate* delegate,
325 const std::string& response_data,
326 net::HttpStatusCode response_code,
327 net::URLRequestStatus::Status status) {
328 OnCreateFakeURLFetcher(url.spec());
329 return scoped_ptr<net::FakeURLFetcher>(new net::FakeURLFetcher(
330 url, delegate, response_data, response_code, status));
333 net::FakeURLFetcherFactory::FakeURLFetcherCreator callback() {
334 return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher,
335 base::Unretained(this));
339 class LocalDiscoveryUITest : public WebUIBrowserTest {
341 LocalDiscoveryUITest() : fake_fetcher_factory_(
342 &fetcher_impl_factory_,
343 fake_url_fetcher_creator_.callback()) {
345 virtual ~LocalDiscoveryUITest() {
348 virtual void SetUpOnMainThread() override {
349 WebUIBrowserTest::SetUpOnMainThread();
351 test_service_discovery_client_ = new TestServiceDiscoveryClient();
352 test_service_discovery_client_->Start();
354 *test_service_discovery_client_.get(),
355 OnSendTo(std::string((const char*)kQueryData, sizeof(kQueryData))))
357 .WillOnce(InvokeWithoutArgs(&condition_devices_listed_,
358 &TestMessageLoopCondition::Signal))
359 .WillRepeatedly(Return());
361 SigninManagerBase* signin_manager =
362 SigninManagerFactory::GetForProfile(browser()->profile());
364 #if defined(OS_CHROMEOS)
365 // Chrome OS initializes prefs::kGoogleServicesUsername to "stub user" so
366 // we need to override it as well.
367 browser()->profile()->GetPrefs()->
368 SetString(prefs::kGoogleServicesUsername, kSampleUser);
370 DCHECK(signin_manager);
371 signin_manager->SetAuthenticatedUsername(kSampleUser);
373 fake_fetcher_factory().SetFakeResponse(
377 net::URLRequestStatus::SUCCESS);
379 fake_fetcher_factory().SetFakeResponse(
380 GURL(kURLRegisterStart),
381 kResponseRegisterStart,
383 net::URLRequestStatus::SUCCESS);
385 fake_fetcher_factory().SetFakeResponse(
386 GURL(kURLRegisterClaimToken),
387 kResponseRegisterClaimTokenNoConfirm,
389 net::URLRequestStatus::SUCCESS);
391 fake_fetcher_factory().SetFakeResponse(
392 GURL(kURLCloudPrintConfirm),
393 kResponseCloudPrintConfirm,
395 net::URLRequestStatus::SUCCESS);
397 fake_fetcher_factory().SetFakeResponse(
398 GURL(kURLRegisterComplete),
399 kResponseRegisterComplete,
401 net::URLRequestStatus::SUCCESS);
403 fake_fetcher_factory().SetFakeResponse(
407 net::URLRequestStatus::SUCCESS);
409 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
413 fake_fetcher_factory().SetFakeResponse(
414 GaiaUrls::GetInstance()->oauth_user_info_url(),
417 net::URLRequestStatus::SUCCESS);
419 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
420 GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
423 ProfileOAuth2TokenService* token_service =
424 ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
426 token_service->UpdateCredentials("user@host.com", "MyFakeToken");
428 AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
431 virtual void SetUpCommandLine(CommandLine* command_line) override {
432 WebUIBrowserTest::SetUpCommandLine(command_line);
435 void RunFor(base::TimeDelta time_period) {
436 base::CancelableCallback<void()> callback(base::Bind(
437 &base::MessageLoop::Quit, base::Unretained(
438 base::MessageLoop::current())));
439 base::MessageLoop::current()->PostDelayedTask(
440 FROM_HERE, callback.callback(), time_period);
442 base::MessageLoop::current()->Run();
446 TestServiceDiscoveryClient* test_service_discovery_client() {
447 return test_service_discovery_client_.get();
450 TestMessageLoopCondition& condition_devices_listed() {
451 return condition_devices_listed_;
454 net::FakeURLFetcherFactory& fake_fetcher_factory() {
455 return fake_fetcher_factory_;
458 MockableFakeURLFetcherCreator& fake_url_fetcher_creator() {
459 return fake_url_fetcher_creator_;
463 scoped_refptr<TestServiceDiscoveryClient> test_service_discovery_client_;
464 TestMessageLoopCondition condition_devices_listed_;
466 net::URLFetcherImplFactory fetcher_impl_factory_;
467 StrictMock<MockableFakeURLFetcherCreator> fake_url_fetcher_creator_;
468 net::FakeURLFetcherFactory fake_fetcher_factory_;
470 DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest);
473 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, EmptyTest) {
474 ui_test_utils::NavigateToURL(browser(), GURL(
475 chrome::kChromeUIDevicesURL));
476 condition_devices_listed().Wait();
477 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
480 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, AddRowTest) {
481 ui_test_utils::NavigateToURL(browser(), GURL(
482 chrome::kChromeUIDevicesURL));
483 condition_devices_listed().Wait();
485 test_service_discovery_client()->SimulateReceive(
486 kAnnouncePacket, sizeof(kAnnouncePacket));
488 base::MessageLoop::current()->RunUntilIdle();
490 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
492 test_service_discovery_client()->SimulateReceive(
493 kGoodbyePacket, sizeof(kGoodbyePacket));
495 RunFor(base::TimeDelta::FromMilliseconds(1100));
497 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
501 IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, RegisterTest) {
502 TestMessageLoopCondition condition_token_claimed;
504 ui_test_utils::NavigateToURL(browser(), GURL(
505 chrome::kChromeUIDevicesURL));
506 condition_devices_listed().Wait();
508 test_service_discovery_client()->SimulateReceive(
509 kAnnouncePacket, sizeof(kAnnouncePacket));
511 base::MessageLoop::current()->RunUntilIdle();
513 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
515 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay"));
519 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo));
520 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
522 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
523 kURLRegisterClaimToken))
524 .WillOnce(InvokeWithoutArgs(&condition_token_claimed,
525 &TestMessageLoopCondition::Signal));
528 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin"));
530 condition_token_claimed.Wait();
532 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1"));
534 fake_fetcher_factory().SetFakeResponse(
535 GURL(kURLRegisterClaimToken),
536 kResponseRegisterClaimTokenConfirm,
538 net::URLRequestStatus::SUCCESS);
540 fake_fetcher_factory().SetFakeResponse(
544 net::URLRequestStatus::SUCCESS);
548 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
549 kURLRegisterClaimToken));
550 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
551 kURLCloudPrintConfirm));
552 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
553 kURLRegisterComplete));
554 EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo))
555 .WillOnce(InvokeWithoutArgs(&condition_token_claimed,
556 &TestMessageLoopCondition::Signal));
559 condition_token_claimed.Wait();
561 test_service_discovery_client()->SimulateReceive(
562 kAnnouncePacketRegistered, sizeof(kAnnouncePacketRegistered));
564 base::MessageLoop::current()->RunUntilIdle();
566 EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone"));
571 } // namespace local_discovery