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