local_discovery_ui_browsertest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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.
4
5#include "base/basictypes.h"
6#include "base/bind.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"
29
30using testing::InvokeWithoutArgs;
31using testing::Return;
32using testing::AtLeast;
33using testing::DoDefault;
34using testing::DoAll;
35using testing::InSequence;
36using testing::StrictMock;
37using testing::AnyNumber;
38
39using testing::InvokeWithoutArgs;
40using testing::Return;
41using testing::AtLeast;
42
43namespace local_discovery {
44
45namespace {
46
47const uint8 kQueryData[] = {
48  // Header
49  0x00, 0x00,
50  0x00, 0x00,               // Flags not set.
51  0x00, 0x01,               // Set QDCOUNT (question count) to 1, all the
52  // rest are 0 for a query.
53  0x00, 0x00,
54  0x00, 0x00,
55  0x00, 0x00,
56
57  // Question
58  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
59  0x04, '_', 't', 'c', 'p',
60  0x05, 'l', 'o', 'c', 'a', 'l',
61  0x00,
62
63  0x00, 0x0c,               // QTYPE: A query.
64  0x00, 0x01,               // QCLASS: IN class. Unicast bit not set.
65};
66
67const uint8 kAnnouncePacket[] = {
68  // Header
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
75
76  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
77  0x04, '_', 't', 'c', 'p',
78  0x05, 'l', 'o', 'c', 'a', 'l',
79  0x00,
80  0x00, 0x0c,        // TYPE is PTR.
81  0x00, 0x01,        // CLASS is IN.
82  0x00, 0x00,        // TTL (4 bytes) is 32768 second.
83  0x10, 0x00,
84  0x00, 0x0c,        // RDLENGTH is 12 bytes.
85  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
86  0xc0, 0x0c,
87
88  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
89  0xc0, 0x0c,
90  0x00, 0x10,        // TYPE is TXT.
91  0x00, 0x01,        // CLASS is IN.
92  0x00, 0x00,        // TTL (4 bytes) is 32768 seconds.
93  0x01, 0x00,
94  0x00, 0x34,        // RDLENGTH is 69 bytes.
95  0x03, 'i', 'd', '=',
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',
101
102  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
103  0xc0, 0x0c,
104  0x00, 0x21,        // Type is SRV
105  0x00, 0x01,        // CLASS is IN
106  0x00, 0x00,        // TTL (4 bytes) is 32768 second.
107  0x10, 0x00,
108  0x00, 0x17,        // RDLENGTH is 23
109  0x00, 0x00,
110  0x00, 0x00,
111  0x22, 0xb8,        // port 8888
112  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
113  0x05, 'l', 'o', 'c', 'a', 'l',
114  0x00,
115
116  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
117  0x05, 'l', 'o', 'c', 'a', 'l',
118  0x00,
119  0x00, 0x01,        // Type is A
120  0x00, 0x01,        // CLASS is IN
121  0x00, 0x00,        // TTL (4 bytes) is 32768 second.
122  0x10, 0x00,
123  0x00, 0x04,        // RDLENGTH is 4
124  0x01, 0x02, 0x03, 0x04,  // 1.2.3.4
125
126  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
127  0x05, 'l', 'o', 'c', 'a', 'l',
128  0x00,
129  0x00, 0x1C,        // Type is AAAA
130  0x00, 0x01,        // CLASS is IN
131  0x00, 0x00,        // TTL (4 bytes) is 32768 second.
132  0x10, 0x00,
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,
138};
139
140
141const uint8 kGoodbyePacket[] = {
142  // Header
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, 0x02,               // 1 RR (answers)
147  0x00, 0x00,               // 0 authority RRs
148  0x00, 0x00,               // 0 additional RRs
149
150  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
151  0x04, '_', 't', 'c', 'p',
152  0x05, 'l', 'o', 'c', 'a', 'l',
153  0x00,
154  0x00, 0x0c,        // TYPE is PTR.
155  0x00, 0x01,        // CLASS is IN.
156  0x00, 0x00,        // TTL (4 bytes) is 0 seconds.
157  0x00, 0x00,
158  0x00, 0x0c,        // RDLENGTH is 12 bytes.
159  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
160  0xc0, 0x0c,
161
162
163  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
164  0xc0, 0x0c,
165  0x00, 0x21,        // Type is SRV
166  0x00, 0x01,        // CLASS is IN
167  0x00, 0x00,        // TTL (4 bytes) is 0 seconds.
168  0x00, 0x00,
169  0x00, 0x17,        // RDLENGTH is 23
170  0x00, 0x00,
171  0x00, 0x00,
172  0x22, 0xb8,        // port 8888
173  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
174  0x05, 'l', 'o', 'c', 'a', 'l',
175  0x00,
176};
177
178const uint8 kAnnouncePacketRegistered[] = {
179  // Header
180  0x00, 0x00,               // ID is zeroed out
181  0x80, 0x00,               // Standard query response, RA, no error
182  0x00, 0x00,               // No questions (for simplicity)
183  0x00, 0x01,               // 1 RR (answers)
184  0x00, 0x00,               // 0 authority RRs
185  0x00, 0x00,               // 0 additional RRs
186
187  0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
188  0x07, '_', 'p', 'r', 'i', 'v', 'e', 't',
189  0x04, '_', 't', 'c', 'p',
190  0x05, 'l', 'o', 'c', 'a', 'l',
191  0x00,
192  0x00, 0x10,        // TYPE is TXT.
193  0x00, 0x01,        // CLASS is IN.
194  0x00, 0x00,        // TTL (4 bytes) is 32768 seconds.
195  0x01, 0x00,
196  0x00, 0x3b,        // RDLENGTH is 76 bytes.
197  0x0a, 'i', 'd', '=', 's', 'o', 'm', 'e', '_', 'i', 'd',
198  0x10, 't', 'y', '=', 'S', 'a', 'm', 'p', 'l', 'e', ' ',
199        'd', 'e', 'v', 'i', 'c', 'e',
200  0x1e, 'n', 'o', 't', 'e', '=',
201        'S', 'a', 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e', ' ',
202        'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n',
203};
204
205const char kResponseInfo[] = "{"
206    "     \"x-privet-token\" : \"MyPrivetToken\""
207    "}";
208
209const char kResponseInfoWithID[] = "{"
210    "     \"x-privet-token\" : \"MyPrivetToken\","
211    "     \"id\" : \"my_id\""
212    "}";
213
214const char kResponseRegisterStart[] = "{"
215    "     \"action\": \"start\","
216    "     \"user\": \"user@host.com\""
217    "}";
218
219const char kResponseRegisterClaimTokenNoConfirm[] = "{"
220    "    \"action\": \"getClaimToken\","
221    "    \"user\": \"user@host.com\","
222    "    \"error\": \"pending_user_action\","
223    "    \"timeout\": 1"
224    "}";
225
226const char kResponseRegisterClaimTokenConfirm[] = "{"
227    "    \"action\": \"getClaimToken\","
228    "    \"user\": \"user@host.com\","
229    "    \"token\": \"MySampleToken\","
230    "    \"claim_url\": \"http://someurl.com/\""
231    "}";
232
233const char kResponseCloudPrintConfirm[] = "{ \"success\": true }";
234
235const char kResponseRegisterComplete[] = "{"
236    "    \"action\": \"complete\","
237    "    \"user\": \"user@host.com\","
238    "    \"device_id\": \"my_id\""
239    "}";
240
241const char kResponseGaiaToken[] = "{"
242    "  \"access_token\": \"at1\","
243    "  \"expires_in\": 3600,"
244    "  \"token_type\": \"Bearer\""
245    "}";
246
247const char kResponseGaiaId[] = "{"
248    "  \"id\": \"12345\""
249    "}";
250
251const char kURLInfo[] = "http://1.2.3.4:8888/privet/info";
252
253const char kURLRegisterStart[] =
254    "http://1.2.3.4:8888/privet/register?action=start&user=user%40host.com";
255
256const char kURLRegisterClaimToken[] =
257    "http://1.2.3.4:8888/privet/register?action=getClaimToken&"
258    "user=user%40host.com";
259
260const char kURLCloudPrintConfirm[] =
261    "https://www.google.com/cloudprint/confirm?token=MySampleToken";
262
263const char kURLRegisterComplete[] =
264    "http://1.2.3.4:8888/privet/register?action=complete&user=user%40host.com";
265
266const char kURLGaiaToken[] =
267    "https://accounts.google.com/o/oauth2/token";
268
269const char kSampleUser[] = "user@host.com";
270
271class TestMessageLoopCondition {
272 public:
273  TestMessageLoopCondition() : signaled_(false),
274                               waiting_(false) {
275  }
276
277  ~TestMessageLoopCondition() {
278  }
279
280  // Signal a waiting method that it can continue executing.
281  void Signal() {
282    signaled_ = true;
283    if (waiting_)
284      base::MessageLoop::current()->Quit();
285  }
286
287  // Pause execution and recursively run the message loop until |Signal()| is
288  // called. Do not pause if |Signal()| has already been called.
289  void Wait() {
290    while (!signaled_) {
291      waiting_ = true;
292      base::MessageLoop::current()->Run();
293      waiting_ = false;
294    }
295    signaled_ = false;
296  }
297
298 private:
299  bool signaled_;
300  bool waiting_;
301
302  DISALLOW_COPY_AND_ASSIGN(TestMessageLoopCondition);
303};
304
305class MockableFakeURLFetcherCreator {
306 public:
307  MockableFakeURLFetcherCreator() {
308  }
309
310  ~MockableFakeURLFetcherCreator() {
311  }
312
313  MOCK_METHOD1(OnCreateFakeURLFetcher, void(const std::string& url));
314
315  scoped_ptr<net::FakeURLFetcher> CreateFakeURLFetcher(
316      const GURL& url,
317      net::URLFetcherDelegate* delegate,
318      const std::string& response_data,
319      net::HttpStatusCode response_code,
320      net::URLRequestStatus::Status status) {
321    OnCreateFakeURLFetcher(url.spec());
322    return scoped_ptr<net::FakeURLFetcher>(new net::FakeURLFetcher(
323        url, delegate, response_data, response_code, status));
324  }
325
326  net::FakeURLFetcherFactory::FakeURLFetcherCreator callback() {
327    return base::Bind(&MockableFakeURLFetcherCreator::CreateFakeURLFetcher,
328                      base::Unretained(this));
329  }
330};
331
332class LocalDiscoveryUITest : public WebUIBrowserTest {
333 public:
334  LocalDiscoveryUITest() : fake_fetcher_factory_(
335      &fetcher_impl_factory_,
336      fake_url_fetcher_creator_.callback()) {
337  }
338  virtual ~LocalDiscoveryUITest() {
339  }
340
341  virtual void SetUpOnMainThread() OVERRIDE {
342    WebUIBrowserTest::SetUpOnMainThread();
343
344    test_service_discovery_client_ = new TestServiceDiscoveryClient();
345    test_service_discovery_client_->Start();
346    EXPECT_CALL(*test_service_discovery_client_, OnSendTo(
347        std::string((const char*)kQueryData,
348                    sizeof(kQueryData))))
349        .Times(AtLeast(2))
350        .WillOnce(InvokeWithoutArgs(&condition_devices_listed_,
351                                    &TestMessageLoopCondition::Signal))
352        .WillRepeatedly(Return());
353
354    SigninManagerBase* signin_manager =
355        SigninManagerFactory::GetForProfile(browser()->profile());
356
357    DCHECK(signin_manager);
358    signin_manager->SetAuthenticatedUsername(kSampleUser);
359
360    fake_fetcher_factory().SetFakeResponse(
361        GURL(kURLInfo),
362        kResponseInfo,
363        net::HTTP_OK,
364        net::URLRequestStatus::SUCCESS);
365
366    fake_fetcher_factory().SetFakeResponse(
367        GURL(kURLRegisterStart),
368        kResponseRegisterStart,
369        net::HTTP_OK,
370        net::URLRequestStatus::SUCCESS);
371
372    fake_fetcher_factory().SetFakeResponse(
373        GURL(kURLRegisterClaimToken),
374        kResponseRegisterClaimTokenNoConfirm,
375        net::HTTP_OK,
376        net::URLRequestStatus::SUCCESS);
377
378    fake_fetcher_factory().SetFakeResponse(
379        GURL(kURLCloudPrintConfirm),
380        kResponseCloudPrintConfirm,
381        net::HTTP_OK,
382        net::URLRequestStatus::SUCCESS);
383
384    fake_fetcher_factory().SetFakeResponse(
385        GURL(kURLRegisterComplete),
386        kResponseRegisterComplete,
387        net::HTTP_OK,
388        net::URLRequestStatus::SUCCESS);
389
390    fake_fetcher_factory().SetFakeResponse(
391        GURL(kURLGaiaToken),
392        kResponseGaiaToken,
393        net::HTTP_OK,
394        net::URLRequestStatus::SUCCESS);
395
396    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
397        kURLGaiaToken))
398        .Times(AnyNumber());
399
400    fake_fetcher_factory().SetFakeResponse(
401        GaiaUrls::GetInstance()->oauth_user_info_url(),
402        kResponseGaiaId,
403        net::HTTP_OK,
404        net::URLRequestStatus::SUCCESS);
405
406    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
407        GaiaUrls::GetInstance()->oauth_user_info_url().spec()))
408        .Times(AnyNumber());
409
410    ProfileOAuth2TokenService* token_service =
411        ProfileOAuth2TokenServiceFactory::GetForProfile(browser()->profile());
412
413    token_service->UpdateCredentials("user@host.com", "MyFakeToken");
414
415    AddLibrary(base::FilePath(FILE_PATH_LITERAL("local_discovery_ui_test.js")));
416  }
417
418  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
419    WebUIBrowserTest::SetUpCommandLine(command_line);
420  }
421
422  void RunFor(base::TimeDelta time_period) {
423    base::CancelableCallback<void()> callback(base::Bind(
424        &base::MessageLoop::Quit, base::Unretained(
425            base::MessageLoop::current())));
426    base::MessageLoop::current()->PostDelayedTask(
427        FROM_HERE, callback.callback(), time_period);
428
429    base::MessageLoop::current()->Run();
430    callback.Cancel();
431  }
432
433  TestServiceDiscoveryClient* test_service_discovery_client() {
434    return test_service_discovery_client_.get();
435  }
436
437  TestMessageLoopCondition& condition_devices_listed() {
438    return condition_devices_listed_;
439  }
440
441  net::FakeURLFetcherFactory& fake_fetcher_factory() {
442    return fake_fetcher_factory_;
443  }
444
445  MockableFakeURLFetcherCreator& fake_url_fetcher_creator() {
446    return fake_url_fetcher_creator_;
447  }
448
449 private:
450  scoped_refptr<TestServiceDiscoveryClient> test_service_discovery_client_;
451  TestMessageLoopCondition condition_devices_listed_;
452
453  net::URLFetcherImplFactory fetcher_impl_factory_;
454  StrictMock<MockableFakeURLFetcherCreator> fake_url_fetcher_creator_;
455  net::FakeURLFetcherFactory fake_fetcher_factory_;
456
457  DISALLOW_COPY_AND_ASSIGN(LocalDiscoveryUITest);
458};
459
460IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, EmptyTest) {
461  ui_test_utils::NavigateToURL(browser(), GURL(
462      chrome::kChromeUIDevicesURL));
463  condition_devices_listed().Wait();
464  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
465}
466
467IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, AddRowTest) {
468  ui_test_utils::NavigateToURL(browser(), GURL(
469      chrome::kChromeUIDevicesURL));
470  condition_devices_listed().Wait();
471
472  test_service_discovery_client()->SimulateReceive(
473      kAnnouncePacket, sizeof(kAnnouncePacket));
474
475  base::MessageLoop::current()->RunUntilIdle();
476
477  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
478
479  test_service_discovery_client()->SimulateReceive(
480      kGoodbyePacket, sizeof(kGoodbyePacket));
481
482  RunFor(base::TimeDelta::FromMilliseconds(1100));
483
484  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkNoDevices"));
485}
486
487
488IN_PROC_BROWSER_TEST_F(LocalDiscoveryUITest, RegisterTest) {
489  TestMessageLoopCondition condition_token_claimed;
490
491  ui_test_utils::NavigateToURL(browser(), GURL(
492      chrome::kChromeUIDevicesURL));
493  condition_devices_listed().Wait();
494
495  test_service_discovery_client()->SimulateReceive(
496      kAnnouncePacket, sizeof(kAnnouncePacket));
497
498  base::MessageLoop::current()->RunUntilIdle();
499
500  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("checkOneDevice"));
501
502  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerShowOverlay"));
503
504  {
505    InSequence s;
506    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo));
507    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
508        kURLRegisterStart));
509    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
510        kURLRegisterClaimToken))
511        .WillOnce(InvokeWithoutArgs(&condition_token_claimed,
512                                    &TestMessageLoopCondition::Signal));
513  }
514
515  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("registerBegin"));
516
517  condition_token_claimed.Wait();
518
519  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectPageAdding1"));
520
521  fake_fetcher_factory().SetFakeResponse(
522      GURL(kURLRegisterClaimToken),
523      kResponseRegisterClaimTokenConfirm,
524      net::HTTP_OK,
525      net::URLRequestStatus::SUCCESS);
526
527  fake_fetcher_factory().SetFakeResponse(
528      GURL(kURLInfo),
529      kResponseInfoWithID,
530      net::HTTP_OK,
531      net::URLRequestStatus::SUCCESS);
532
533  {
534    InSequence s;
535    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
536        kURLRegisterClaimToken));
537    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
538        kURLCloudPrintConfirm));
539    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(
540        kURLRegisterComplete));
541    EXPECT_CALL(fake_url_fetcher_creator(), OnCreateFakeURLFetcher(kURLInfo))
542        .WillOnce(InvokeWithoutArgs(&condition_token_claimed,
543                                    &TestMessageLoopCondition::Signal));
544  }
545
546  condition_token_claimed.Wait();
547
548  test_service_discovery_client()->SimulateReceive(
549      kAnnouncePacketRegistered, sizeof(kAnnouncePacketRegistered));
550
551  base::MessageLoop::current()->RunUntilIdle();
552
553  EXPECT_TRUE(WebUIBrowserTest::RunJavascriptTest("expectRegisterDone"));
554}
555
556}  // namespace
557
558}  // namespace local_discovery
559