1 // Copyright 2014 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.
6 #include "base/command_line.h"
7 #include "base/json/json_reader.h"
8 #include "base/message_loop/message_loop_proxy.h"
9 #include "chrome/browser/extensions/api/gcd_private/gcd_private_api.h"
10 #include "chrome/browser/extensions/extension_apitest.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/local_discovery/gcd_api_flow.h"
13 #include "chrome/browser/local_discovery/wifi/mock_wifi_manager.h"
14 #include "chrome/common/extensions/api/mdns.h"
15 #include "extensions/common/switches.h"
16 #include "net/url_request/test_url_fetcher_factory.h"
17 #include "testing/gmock/include/gmock/gmock.h"
19 #if defined(ENABLE_MDNS)
20 #include "chrome/browser/local_discovery/test_service_discovery_client.h"
23 namespace api = extensions::api;
25 using testing::Invoke;
29 const char kCloudPrintResponse[] =
33 " {\"id\" : \"someCloudPrintID\","
34 " \"displayName\": \"someCloudPrintDisplayName\","
35 " \"description\": \"someCloudPrintDescription\"}"
39 const char kGCDResponse[] =
41 "\"kind\": \"clouddevices#devicesListResponse\","
43 " \"kind\": \"clouddevices#device\","
44 " \"id\": \"someGCDID\","
45 " \"deviceKind\": \"someType\","
46 " \"creationTimeMs\": \"123\","
47 " \"systemName\": \"someGCDDisplayName\","
48 " \"owner\": \"user@domain.com\","
49 " \"description\": \"someGCDDescription\","
52 " \"connectionStatus\": \"offline\""
56 " \"supportedType\": \"xmpp\""
58 " \"personalizedInfo\": {"
59 " \"maxRole\": \"owner\""
62 const char kPrivetInfoResponse[] =
64 "\"x-privet-token\": \"sample\""
67 const char kPrivetPingResponse[] =
69 "\"response\": \"pong\""
72 #if defined(ENABLE_MDNS)
74 const uint8 kAnnouncePacket[] = {
76 0x00, 0x00, // ID is zeroed out
77 0x80, 0x00, // Standard query response, no error
78 0x00, 0x00, // No questions (for simplicity)
79 0x00, 0x05, // 5 RR (answers)
80 0x00, 0x00, // 0 authority RRs
81 0x00, 0x00, // 0 additional RRs
82 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 0x04, '_',
83 't', 'c', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
84 0x00, 0x0c, // TYPE is PTR.
85 0x00, 0x01, // CLASS is IN.
86 0x00, 0x00, // TTL (4 bytes) is 32768 second.
87 0x10, 0x00, 0x00, 0x0c, // RDLENGTH is 12 bytes.
88 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
89 0xc0, 0x0c, 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i',
90 'c', 'e', 0xc0, 0x0c, 0x00, 0x10, // TYPE is TXT.
91 0x00, 0x01, // CLASS is IN.
92 0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
93 0x01, 0x00, 0x00, 0x41, // RDLENGTH is 69 bytes.
94 0x03, 'i', 'd', '=', 0x10, 't', 'y', '=', 'S', 'a',
95 'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c',
96 'e', 0x1e, 'n', 'o', 't', 'e', '=', 'S', 'a', 'm',
97 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e',
98 ' ', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i',
99 'o', 'n', 0x0c, 't', 'y', 'p', 'e', '=', 'p', 'r',
100 'i', 'n', 't', 'e', 'r', 0x09, 'm', 'y', 'S', 'e',
101 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c, 0x00, 0x21, // Type is SRV
102 0x00, 0x01, // CLASS is IN
103 0x00, 0x00, // TTL (4 bytes) is 32768 second.
104 0x10, 0x00, 0x00, 0x17, // RDLENGTH is 23
105 0x00, 0x00, 0x00, 0x00, 0x22, 0xb8, // port 8888
106 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
107 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x09, 'm', 'y',
108 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0x05, 'l', 'o',
109 'c', 'a', 'l', 0x00, 0x00, 0x01, // Type is A
110 0x00, 0x01, // CLASS is IN
111 0x00, 0x00, // TTL (4 bytes) is 32768 second.
112 0x10, 0x00, 0x00, 0x04, // RDLENGTH is 4
113 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
114 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
115 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x00, 0x1C, // Type is AAAA
116 0x00, 0x01, // CLASS is IN
117 0x00, 0x00, // TTL (4 bytes) is 32768 second.
118 0x10, 0x00, 0x00, 0x10, // RDLENGTH is 16
119 0x01, 0x02, 0x03, 0x04, // 1.2.3.4
120 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02,
124 const uint8 kGoodbyePacket[] = {
126 0x00, 0x00, // ID is zeroed out
127 0x80, 0x00, // Standard query response, RA, no error
128 0x00, 0x00, // No questions (for simplicity)
129 0x00, 0x02, // 1 RR (answers)
130 0x00, 0x00, // 0 authority RRs
131 0x00, 0x00, // 0 additional RRs
132 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 0x04, '_', 't', 'c',
133 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x00, 0x0c, // TYPE is PTR.
134 0x00, 0x01, // CLASS is IN.
135 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
136 0x00, 0x00, 0x00, 0x0c, // RDLENGTH is 12 bytes.
137 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c,
138 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c,
139 0x00, 0x21, // Type is SRV
140 0x00, 0x01, // CLASS is IN
141 0x00, 0x00, // TTL (4 bytes) is 0 seconds.
142 0x00, 0x00, 0x00, 0x17, // RDLENGTH is 23
143 0x00, 0x00, 0x00, 0x00, 0x22, 0xb8, // port 8888
144 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0x05, 'l',
145 'o', 'c', 'a', 'l', 0x00,
148 const uint8 kQueryPacket[] = {
150 0x00, 0x00, // ID is zeroed out
151 0x00, 0x00, // No flags.
152 0x00, 0x01, // One question.
153 0x00, 0x00, // 0 RRs (answers)
154 0x00, 0x00, // 0 authority RRs
155 0x00, 0x00, // 0 additional RRs
158 // This part is echoed back from the respective query.
159 0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 0x04, '_', 't', 'c',
160 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x00, 0x0c, // TYPE is PTR.
161 0x00, 0x01, // CLASS is IN.
164 #endif // ENABLE_MDNS
166 // Sentinel value to signify the request should fail.
167 const char kResponseValueFailure[] = "FAILURE";
169 class FakeGCDApiFlowFactory
170 : public extensions::GcdPrivateAPI::GCDApiFlowFactoryForTests {
172 FakeGCDApiFlowFactory() {
173 extensions::GcdPrivateAPI::SetGCDApiFlowFactoryForTests(this);
176 ~FakeGCDApiFlowFactory() override {
177 extensions::GcdPrivateAPI::SetGCDApiFlowFactoryForTests(NULL);
180 scoped_ptr<local_discovery::GCDApiFlow> CreateGCDApiFlow() override {
181 return scoped_ptr<local_discovery::GCDApiFlow>(new FakeGCDApiFlow(this));
184 void SetResponse(const GURL& url, const std::string& response) {
185 responses_[url] = response;
189 class FakeGCDApiFlow : public local_discovery::GCDApiFlow {
191 explicit FakeGCDApiFlow(FakeGCDApiFlowFactory* factory)
192 : factory_(factory) {}
194 ~FakeGCDApiFlow() override {}
196 void Start(scoped_ptr<Request> request) override {
197 std::string response_str = factory_->responses_[request->GetURL()];
199 if (response_str == kResponseValueFailure) {
200 request->OnGCDAPIFlowError(
201 local_discovery::GCDApiFlow::ERROR_MALFORMED_RESPONSE);
205 scoped_ptr<base::Value> response(base::JSONReader::Read(response_str));
206 ASSERT_TRUE(response);
208 base::DictionaryValue* response_dict;
209 ASSERT_TRUE(response->GetAsDictionary(&response_dict));
211 request->OnGCDAPIFlowComplete(*response_dict);
215 FakeGCDApiFlowFactory* factory_;
218 std::map<GURL /*request url*/, std::string /*response json*/> responses_;
221 class GcdPrivateAPITest : public ExtensionApiTest {
223 GcdPrivateAPITest() : url_fetcher_factory_(&url_fetcher_impl_factory_) {
224 #if defined(ENABLE_MDNS)
225 test_service_discovery_client_ =
226 new local_discovery::TestServiceDiscoveryClient();
227 test_service_discovery_client_->Start();
228 #endif // ENABLE_MDNS
231 void SetUpCommandLine(CommandLine* command_line) override {
232 ExtensionApiTest::SetUpCommandLine(command_line);
233 command_line->AppendSwitchASCII(
234 extensions::switches::kWhitelistedExtensionID,
235 "ddchlicdkolnonkihahngkmmmjnjlkkf");
238 #if defined(ENABLE_WIFI_BOOTSTRAPPING)
239 virtual void OnCreateWifiManager() {
240 wifi_manager_ = wifi_manager_factory_.GetLastCreatedWifiManager();
242 EXPECT_CALL(*wifi_manager_, Start());
244 EXPECT_CALL(*wifi_manager_,
245 RequestNetworkCredentialsInternal("SuccessNetwork"))
246 .WillOnce(Invoke(this, &GcdPrivateAPITest::RespondToNetwork));
248 EXPECT_CALL(*wifi_manager_,
249 RequestNetworkCredentialsInternal("FailureNetwork"))
250 .WillOnce(Invoke(this, &GcdPrivateAPITest::RespondToNetwork));
253 void RespondToNetwork(const std::string& network) {
254 bool success = (network == "SuccessNetwork");
256 wifi_manager_->CallRequestNetworkCredentialsCallback(
257 success, network, success ? "SuccessPass" : "");
262 FakeGCDApiFlowFactory api_flow_factory_;
263 net::URLFetcherImplFactory url_fetcher_impl_factory_;
264 net::FakeURLFetcherFactory url_fetcher_factory_;
266 #if defined(ENABLE_MDNS)
267 scoped_refptr<local_discovery::TestServiceDiscoveryClient>
268 test_service_discovery_client_;
269 #endif // ENABLE_MDNS
271 #if defined(ENABLE_WIFI_BOOTSTRAPPING)
272 local_discovery::wifi::MockWifiManagerFactory wifi_manager_factory_;
273 local_discovery::wifi::MockWifiManager* wifi_manager_;
277 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, GetCloudList) {
278 api_flow_factory_.SetResponse(
279 GURL("https://www.google.com/cloudprint/search"), kCloudPrintResponse);
281 api_flow_factory_.SetResponse(
282 GURL("https://www.googleapis.com/clouddevices/v1/devices"), kGCDResponse);
284 EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "get_cloud_list.html"));
287 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, Session) {
288 url_fetcher_factory_.SetFakeResponse(GURL("http://1.2.3.4:9090/privet/info"),
291 net::URLRequestStatus::SUCCESS);
293 url_fetcher_factory_.SetFakeResponse(GURL("http://1.2.3.4:9090/privet/ping"),
296 net::URLRequestStatus::SUCCESS);
298 EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "session.html"));
301 #if defined(ENABLE_MDNS)
303 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddBefore) {
304 test_service_discovery_client_->SimulateReceive(kAnnouncePacket,
305 sizeof(kAnnouncePacket));
308 RunExtensionSubtest("gcd_private/api", "receive_new_device.html"));
311 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddAfter) {
312 base::MessageLoopProxy::current()->PostDelayedTask(
314 base::Bind(&local_discovery::TestServiceDiscoveryClient::SimulateReceive,
315 test_service_discovery_client_,
317 sizeof(kAnnouncePacket)),
318 base::TimeDelta::FromSeconds(1));
321 RunExtensionSubtest("gcd_private/api", "receive_new_device.html"));
324 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddRemove) {
325 test_service_discovery_client_->SimulateReceive(kAnnouncePacket,
326 sizeof(kAnnouncePacket));
328 base::MessageLoopProxy::current()->PostDelayedTask(
330 base::Bind(&local_discovery::TestServiceDiscoveryClient::SimulateReceive,
331 test_service_discovery_client_,
333 sizeof(kGoodbyePacket)),
334 base::TimeDelta::FromSeconds(1));
336 EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "remove_device.html"));
339 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, SendQuery) {
340 // TODO(noamsml): Win Dbg has a workaround that makes RunExtensionSubtest
341 // always return true without actually running the test. Remove when fixed.
342 // See http://crbug.com/177163 for details.
343 #if !defined(OS_WIN) || defined(NDEBUG)
344 EXPECT_CALL(*test_service_discovery_client_.get(),
345 OnSendTo(std::string(reinterpret_cast<const char*>(kQueryPacket),
346 sizeof(kQueryPacket)))).Times(2);
348 EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "send_query.html"));
351 #endif // ENABLE_MDNS
353 #if defined(ENABLE_WIFI_BOOTSTRAPPING)
355 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, WifiMessage) {
356 EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "wifi_message.html"));
359 IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, WifiPasswords) {
360 // TODO(noamsml): Win Dbg has a workaround that makes RunExtensionSubtest
361 // always return true without actually running the test. Remove when fixed.
362 // See http://crbug.com/177163 for details.
363 #if !defined(OS_WIN) || defined(NDEBUG)
364 EXPECT_CALL(wifi_manager_factory_, WifiManagerCreated())
365 .WillOnce(Invoke(this, &GcdPrivateAPITest::OnCreateWifiManager));
368 EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "wifi_password.html"));
371 #endif // ENABLE_WIFI_BOOTSTRAPPING