- add sources.
[platform/framework/web/crosswalk.git] / src / remoting / host / setup / me2me_native_messaging_host_unittest.cc
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 "remoting/host/setup/me2me_native_messaging_host.h"
6
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/stl_util.h"
14 #include "base/strings/stringize_macros.h"
15 #include "base/values.h"
16 #include "google_apis/gaia/gaia_oauth_client.h"
17 #include "net/base/file_stream.h"
18 #include "net/base/net_util.h"
19 #include "remoting/base/auto_thread_task_runner.h"
20 #include "remoting/host/native_messaging/native_messaging_channel.h"
21 #include "remoting/host/pin_hash.h"
22 #include "remoting/host/setup/test_util.h"
23 #include "remoting/protocol/pairing_registry.h"
24 #include "remoting/protocol/protocol_mock_objects.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26
27 using remoting::protocol::MockPairingRegistryDelegate;
28 using remoting::protocol::PairingRegistry;
29 using remoting::protocol::SynchronousPairingRegistry;
30
31 namespace {
32
33 void VerifyHelloResponse(scoped_ptr<base::DictionaryValue> response) {
34   ASSERT_TRUE(response);
35   std::string value;
36   EXPECT_TRUE(response->GetString("type", &value));
37   EXPECT_EQ("helloResponse", value);
38   EXPECT_TRUE(response->GetString("version", &value));
39   EXPECT_EQ(STRINGIZE(VERSION), value);
40 }
41
42 void VerifyGetHostNameResponse(scoped_ptr<base::DictionaryValue> response) {
43   ASSERT_TRUE(response);
44   std::string value;
45   EXPECT_TRUE(response->GetString("type", &value));
46   EXPECT_EQ("getHostNameResponse", value);
47   EXPECT_TRUE(response->GetString("hostname", &value));
48   EXPECT_EQ(net::GetHostName(), value);
49 }
50
51 void VerifyGetPinHashResponse(scoped_ptr<base::DictionaryValue> response) {
52   ASSERT_TRUE(response);
53   std::string value;
54   EXPECT_TRUE(response->GetString("type", &value));
55   EXPECT_EQ("getPinHashResponse", value);
56   EXPECT_TRUE(response->GetString("hash", &value));
57   EXPECT_EQ(remoting::MakeHostPinHash("my_host", "1234"), value);
58 }
59
60 void VerifyGenerateKeyPairResponse(scoped_ptr<base::DictionaryValue> response) {
61   ASSERT_TRUE(response);
62   std::string value;
63   EXPECT_TRUE(response->GetString("type", &value));
64   EXPECT_EQ("generateKeyPairResponse", value);
65   EXPECT_TRUE(response->GetString("privateKey", &value));
66   EXPECT_TRUE(response->GetString("publicKey", &value));
67 }
68
69 void VerifyGetDaemonConfigResponse(scoped_ptr<base::DictionaryValue> response) {
70   ASSERT_TRUE(response);
71   std::string value;
72   EXPECT_TRUE(response->GetString("type", &value));
73   EXPECT_EQ("getDaemonConfigResponse", value);
74   const base::DictionaryValue* config = NULL;
75   EXPECT_TRUE(response->GetDictionary("config", &config));
76   EXPECT_TRUE(base::DictionaryValue().Equals(config));
77 }
78
79 void VerifyGetUsageStatsConsentResponse(
80     scoped_ptr<base::DictionaryValue> response) {
81   ASSERT_TRUE(response);
82   std::string value;
83   EXPECT_TRUE(response->GetString("type", &value));
84   EXPECT_EQ("getUsageStatsConsentResponse", value);
85   bool supported, allowed, set_by_policy;
86   EXPECT_TRUE(response->GetBoolean("supported", &supported));
87   EXPECT_TRUE(response->GetBoolean("allowed", &allowed));
88   EXPECT_TRUE(response->GetBoolean("setByPolicy", &set_by_policy));
89   EXPECT_TRUE(supported);
90   EXPECT_TRUE(allowed);
91   EXPECT_TRUE(set_by_policy);
92 }
93
94 void VerifyStopDaemonResponse(scoped_ptr<base::DictionaryValue> response) {
95   ASSERT_TRUE(response);
96   std::string value;
97   EXPECT_TRUE(response->GetString("type", &value));
98   EXPECT_EQ("stopDaemonResponse", value);
99   EXPECT_TRUE(response->GetString("result", &value));
100   EXPECT_EQ("OK", value);
101 }
102
103 void VerifyGetDaemonStateResponse(scoped_ptr<base::DictionaryValue> response) {
104   ASSERT_TRUE(response);
105   std::string value;
106   EXPECT_TRUE(response->GetString("type", &value));
107   EXPECT_EQ("getDaemonStateResponse", value);
108   EXPECT_TRUE(response->GetString("state", &value));
109   EXPECT_EQ("STARTED", value);
110 }
111
112 void VerifyUpdateDaemonConfigResponse(
113     scoped_ptr<base::DictionaryValue> response) {
114   ASSERT_TRUE(response);
115   std::string value;
116   EXPECT_TRUE(response->GetString("type", &value));
117   EXPECT_EQ("updateDaemonConfigResponse", value);
118   EXPECT_TRUE(response->GetString("result", &value));
119   EXPECT_EQ("OK", value);
120 }
121
122 void VerifyStartDaemonResponse(scoped_ptr<base::DictionaryValue> response) {
123   ASSERT_TRUE(response);
124   std::string value;
125   EXPECT_TRUE(response->GetString("type", &value));
126   EXPECT_EQ("startDaemonResponse", value);
127   EXPECT_TRUE(response->GetString("result", &value));
128   EXPECT_EQ("OK", value);
129 }
130
131 }  // namespace
132
133 namespace remoting {
134
135 class MockDaemonControllerDelegate : public DaemonController::Delegate {
136  public:
137   MockDaemonControllerDelegate();
138   virtual ~MockDaemonControllerDelegate();
139
140   // DaemonController::Delegate interface.
141   virtual DaemonController::State GetState() OVERRIDE;
142   virtual scoped_ptr<base::DictionaryValue> GetConfig() OVERRIDE;
143   virtual void SetConfigAndStart(
144       scoped_ptr<base::DictionaryValue> config,
145       bool consent,
146       const DaemonController::CompletionCallback& done) OVERRIDE;
147   virtual void UpdateConfig(
148       scoped_ptr<base::DictionaryValue> config,
149       const DaemonController::CompletionCallback& done) OVERRIDE;
150   virtual void Stop(const DaemonController::CompletionCallback& done) OVERRIDE;
151   virtual void SetWindow(void* window_handle) OVERRIDE;
152   virtual std::string GetVersion() OVERRIDE;
153   virtual DaemonController::UsageStatsConsent GetUsageStatsConsent() OVERRIDE;
154
155  private:
156   DISALLOW_COPY_AND_ASSIGN(MockDaemonControllerDelegate);
157 };
158
159 MockDaemonControllerDelegate::MockDaemonControllerDelegate() {}
160
161 MockDaemonControllerDelegate::~MockDaemonControllerDelegate() {}
162
163 DaemonController::State MockDaemonControllerDelegate::GetState() {
164   return DaemonController::STATE_STARTED;
165 }
166
167 scoped_ptr<base::DictionaryValue> MockDaemonControllerDelegate::GetConfig() {
168   return scoped_ptr<base::DictionaryValue>(new base::DictionaryValue());
169 }
170
171 void MockDaemonControllerDelegate::SetConfigAndStart(
172     scoped_ptr<base::DictionaryValue> config,
173     bool consent,
174     const DaemonController::CompletionCallback& done) {
175
176   // Verify parameters passed in.
177   if (consent && config && config->HasKey("start")) {
178     done.Run(DaemonController::RESULT_OK);
179   } else {
180     done.Run(DaemonController::RESULT_FAILED);
181   }
182 }
183
184 void MockDaemonControllerDelegate::UpdateConfig(
185     scoped_ptr<base::DictionaryValue> config,
186     const DaemonController::CompletionCallback& done) {
187   if (config && config->HasKey("update")) {
188     done.Run(DaemonController::RESULT_OK);
189   } else {
190     done.Run(DaemonController::RESULT_FAILED);
191   }
192 }
193
194 void MockDaemonControllerDelegate::Stop(
195     const DaemonController::CompletionCallback& done) {
196   done.Run(DaemonController::RESULT_OK);
197 }
198
199 void MockDaemonControllerDelegate::SetWindow(void* window_handle) {}
200
201 std::string MockDaemonControllerDelegate::GetVersion() {
202   // Unused - NativeMessagingHost returns the compiled-in version string
203   // instead of calling this method.
204   NOTREACHED();
205   return std::string();
206 }
207
208 DaemonController::UsageStatsConsent
209 MockDaemonControllerDelegate::GetUsageStatsConsent() {
210   DaemonController::UsageStatsConsent consent;
211   consent.supported = true;
212   consent.allowed = true;
213   consent.set_by_policy = true;
214   return consent;
215 }
216
217 class NativeMessagingHostTest : public testing::Test {
218  public:
219   NativeMessagingHostTest();
220   virtual ~NativeMessagingHostTest();
221
222   virtual void SetUp() OVERRIDE;
223   virtual void TearDown() OVERRIDE;
224
225   void Run();
226
227   // Deletes |host_|.
228   void DeleteHost();
229
230   scoped_ptr<base::DictionaryValue> ReadMessageFromOutputPipe();
231
232   void WriteMessageToInputPipe(const base::Value& message);
233
234   // The Host process should shut down when it receives a malformed request.
235   // This is tested by sending a known-good request, followed by |message|,
236   // followed by the known-good request again. The response file should only
237   // contain a single response from the first good request.
238   void TestBadRequest(const base::Value& message);
239
240  protected:
241   // Reference to the MockDaemonControllerDelegate, which is owned by
242   // |channel_|.
243   MockDaemonControllerDelegate* daemon_controller_delegate_;
244
245  private:
246   // Each test creates two unidirectional pipes: "input" and "output".
247   // NativeMessagingHost reads from input_read_handle and writes to
248   // output_write_handle. The unittest supplies data to input_write_handle, and
249   // verifies output from output_read_handle.
250   //
251   // unittest -> [input] -> NativeMessagingHost -> [output] -> unittest
252   base::PlatformFile input_write_handle_;
253   base::PlatformFile output_read_handle_;
254
255   base::MessageLoop message_loop_;
256   base::RunLoop run_loop_;
257   scoped_refptr<AutoThreadTaskRunner> task_runner_;
258   scoped_ptr<remoting::NativeMessagingChannel> channel_;
259
260   DISALLOW_COPY_AND_ASSIGN(NativeMessagingHostTest);
261 };
262
263 NativeMessagingHostTest::NativeMessagingHostTest()
264     : message_loop_(base::MessageLoop::TYPE_IO) {}
265
266 NativeMessagingHostTest::~NativeMessagingHostTest() {}
267
268 void NativeMessagingHostTest::SetUp() {
269   base::PlatformFile input_read_handle;
270   base::PlatformFile output_write_handle;
271
272   ASSERT_TRUE(MakePipe(&input_read_handle, &input_write_handle_));
273   ASSERT_TRUE(MakePipe(&output_read_handle_, &output_write_handle));
274
275   // Arrange to run |message_loop_| until no components depend on it.
276   task_runner_ = new AutoThreadTaskRunner(
277       message_loop_.message_loop_proxy(), run_loop_.QuitClosure());
278
279   daemon_controller_delegate_ = new MockDaemonControllerDelegate();
280   scoped_refptr<DaemonController> daemon_controller(
281       new DaemonController(
282           scoped_ptr<DaemonController::Delegate>(daemon_controller_delegate_)));
283
284   scoped_refptr<PairingRegistry> pairing_registry =
285       new SynchronousPairingRegistry(scoped_ptr<PairingRegistry::Delegate>(
286           new MockPairingRegistryDelegate()));
287   scoped_ptr<NativeMessagingChannel::Delegate> host(
288       new NativeMessagingHost(daemon_controller,
289                               pairing_registry,
290                               scoped_ptr<remoting::OAuthClient>()));
291   channel_.reset(
292       new NativeMessagingChannel(host.Pass(),
293                                  input_read_handle,
294                                  output_write_handle));
295 }
296
297 void NativeMessagingHostTest::TearDown() {
298   // DaemonController destroys its internals asynchronously. Let these and any
299   // other pending tasks run to make sure we don't leak the memory owned by
300   // them.
301   message_loop_.RunUntilIdle();
302
303   // The NativeMessagingHost dtor closes the handles that are passed to it.
304   // |input_write_handle_| gets closed just before starting the host. So the
305   // only handle left to close is |output_read_handle_|.
306   base::ClosePlatformFile(output_read_handle_);
307 }
308
309 void NativeMessagingHostTest::Run() {
310   // Close the write-end of input, so that the host sees EOF after reading
311   // messages and won't block waiting for more input.
312   base::ClosePlatformFile(input_write_handle_);
313   channel_->Start(base::Bind(&NativeMessagingHostTest::DeleteHost,
314                              base::Unretained(this)));
315   run_loop_.Run();
316 }
317
318 void NativeMessagingHostTest::DeleteHost() {
319   // Destroy |channel_| so that it closes its end of the output pipe, so that
320   // TestBadRequest() will see EOF and won't block waiting for more data.
321   channel_.reset();
322   task_runner_ = NULL;
323 }
324
325 scoped_ptr<base::DictionaryValue>
326 NativeMessagingHostTest::ReadMessageFromOutputPipe() {
327   uint32 length;
328   int read_result = base::ReadPlatformFileAtCurrentPos(
329       output_read_handle_, reinterpret_cast<char*>(&length), sizeof(length));
330   if (read_result != sizeof(length)) {
331     return scoped_ptr<base::DictionaryValue>();
332   }
333
334   std::string message_json(length, '\0');
335   read_result = base::ReadPlatformFileAtCurrentPos(
336       output_read_handle_, string_as_array(&message_json), length);
337   if (read_result != static_cast<int>(length)) {
338     return scoped_ptr<base::DictionaryValue>();
339   }
340
341   scoped_ptr<base::Value> message(base::JSONReader::Read(message_json));
342   if (!message || !message->IsType(base::Value::TYPE_DICTIONARY)) {
343     return scoped_ptr<base::DictionaryValue>();
344   }
345
346   return scoped_ptr<base::DictionaryValue>(
347       static_cast<base::DictionaryValue*>(message.release()));
348 }
349
350 void NativeMessagingHostTest::WriteMessageToInputPipe(
351     const base::Value& message) {
352   std::string message_json;
353   base::JSONWriter::Write(&message, &message_json);
354
355   uint32 length = message_json.length();
356   base::WritePlatformFileAtCurrentPos(input_write_handle_,
357                                       reinterpret_cast<char*>(&length),
358                                       sizeof(length));
359   base::WritePlatformFileAtCurrentPos(input_write_handle_, message_json.data(),
360                                       length);
361 }
362
363 void NativeMessagingHostTest::TestBadRequest(const base::Value& message) {
364   base::DictionaryValue good_message;
365   good_message.SetString("type", "hello");
366
367   // This test currently relies on synchronous processing of hello messages and
368   // message parameters verification.
369   WriteMessageToInputPipe(good_message);
370   WriteMessageToInputPipe(message);
371   WriteMessageToInputPipe(good_message);
372
373   Run();
374
375   // Read from output pipe, and verify responses.
376   scoped_ptr<base::DictionaryValue> response =
377       ReadMessageFromOutputPipe();
378   VerifyHelloResponse(response.Pass());
379
380   response = ReadMessageFromOutputPipe();
381   EXPECT_FALSE(response);
382 }
383
384 // Test all valid request-types.
385 TEST_F(NativeMessagingHostTest, All) {
386   int next_id = 0;
387   base::DictionaryValue message;
388   message.SetInteger("id", next_id++);
389   message.SetString("type", "hello");
390   WriteMessageToInputPipe(message);
391
392   message.SetInteger("id", next_id++);
393   message.SetString("type", "getHostName");
394   WriteMessageToInputPipe(message);
395
396   message.SetInteger("id", next_id++);
397   message.SetString("type", "getPinHash");
398   message.SetString("hostId", "my_host");
399   message.SetString("pin", "1234");
400   WriteMessageToInputPipe(message);
401
402   message.Clear();
403   message.SetInteger("id", next_id++);
404   message.SetString("type", "generateKeyPair");
405   WriteMessageToInputPipe(message);
406
407   message.SetInteger("id", next_id++);
408   message.SetString("type", "getDaemonConfig");
409   WriteMessageToInputPipe(message);
410
411   message.SetInteger("id", next_id++);
412   message.SetString("type", "getUsageStatsConsent");
413   WriteMessageToInputPipe(message);
414
415   message.SetInteger("id", next_id++);
416   message.SetString("type", "stopDaemon");
417   WriteMessageToInputPipe(message);
418
419   message.SetInteger("id", next_id++);
420   message.SetString("type", "getDaemonState");
421   WriteMessageToInputPipe(message);
422
423   // Following messages require a "config" dictionary.
424   base::DictionaryValue config;
425   config.SetBoolean("update", true);
426   message.Set("config", config.DeepCopy());
427   message.SetInteger("id", next_id++);
428   message.SetString("type", "updateDaemonConfig");
429   WriteMessageToInputPipe(message);
430
431   config.Clear();
432   config.SetBoolean("start", true);
433   message.Set("config", config.DeepCopy());
434   message.SetBoolean("consent", true);
435   message.SetInteger("id", next_id++);
436   message.SetString("type", "startDaemon");
437   WriteMessageToInputPipe(message);
438
439   Run();
440
441   void (*verify_routines[])(scoped_ptr<base::DictionaryValue>) = {
442     &VerifyHelloResponse,
443     &VerifyGetHostNameResponse,
444     &VerifyGetPinHashResponse,
445     &VerifyGenerateKeyPairResponse,
446     &VerifyGetDaemonConfigResponse,
447     &VerifyGetUsageStatsConsentResponse,
448     &VerifyStopDaemonResponse,
449     &VerifyGetDaemonStateResponse,
450     &VerifyUpdateDaemonConfigResponse,
451     &VerifyStartDaemonResponse,
452   };
453   ASSERT_EQ(arraysize(verify_routines), static_cast<size_t>(next_id));
454
455   // Read all responses from output pipe, and verify them.
456   for (int i = 0; i < next_id; ++i) {
457     scoped_ptr<base::DictionaryValue> response = ReadMessageFromOutputPipe();
458
459     // Make sure that id is available and is in the range.
460     int id;
461     ASSERT_TRUE(response->GetInteger("id", &id));
462     ASSERT_TRUE(0 <= id && id < next_id);
463
464     // Call the verification routine corresponding to the message id.
465     ASSERT_TRUE(verify_routines[id]);
466     verify_routines[id](response.Pass());
467
468     // Clear the pointer so that the routine cannot be called the second time.
469     verify_routines[id] = NULL;
470   }
471 }
472
473 // Verify that response ID matches request ID.
474 TEST_F(NativeMessagingHostTest, Id) {
475   base::DictionaryValue message;
476   message.SetString("type", "hello");
477   WriteMessageToInputPipe(message);
478   message.SetString("id", "42");
479   WriteMessageToInputPipe(message);
480
481   Run();
482
483   scoped_ptr<base::DictionaryValue> response =
484       ReadMessageFromOutputPipe();
485   EXPECT_TRUE(response);
486   std::string value;
487   EXPECT_FALSE(response->GetString("id", &value));
488
489   response = ReadMessageFromOutputPipe();
490   EXPECT_TRUE(response);
491   EXPECT_TRUE(response->GetString("id", &value));
492   EXPECT_EQ("42", value);
493 }
494
495 // Verify non-Dictionary requests are rejected.
496 TEST_F(NativeMessagingHostTest, WrongFormat) {
497   base::ListValue message;
498   TestBadRequest(message);
499 }
500
501 // Verify requests with no type are rejected.
502 TEST_F(NativeMessagingHostTest, MissingType) {
503   base::DictionaryValue message;
504   TestBadRequest(message);
505 }
506
507 // Verify rejection if type is unrecognized.
508 TEST_F(NativeMessagingHostTest, InvalidType) {
509   base::DictionaryValue message;
510   message.SetString("type", "xxx");
511   TestBadRequest(message);
512 }
513
514 // Verify rejection if getPinHash request has no hostId.
515 TEST_F(NativeMessagingHostTest, GetPinHashNoHostId) {
516   base::DictionaryValue message;
517   message.SetString("type", "getPinHash");
518   message.SetString("pin", "1234");
519   TestBadRequest(message);
520 }
521
522 // Verify rejection if getPinHash request has no pin.
523 TEST_F(NativeMessagingHostTest, GetPinHashNoPin) {
524   base::DictionaryValue message;
525   message.SetString("type", "getPinHash");
526   message.SetString("hostId", "my_host");
527   TestBadRequest(message);
528 }
529
530 // Verify rejection if updateDaemonConfig request has invalid config.
531 TEST_F(NativeMessagingHostTest, UpdateDaemonConfigInvalidConfig) {
532   base::DictionaryValue message;
533   message.SetString("type", "updateDaemonConfig");
534   message.SetString("config", "xxx");
535   TestBadRequest(message);
536 }
537
538 // Verify rejection if startDaemon request has invalid config.
539 TEST_F(NativeMessagingHostTest, StartDaemonInvalidConfig) {
540   base::DictionaryValue message;
541   message.SetString("type", "startDaemon");
542   message.SetString("config", "xxx");
543   message.SetBoolean("consent", true);
544   TestBadRequest(message);
545 }
546
547 // Verify rejection if startDaemon request has no "consent" parameter.
548 TEST_F(NativeMessagingHostTest, StartDaemonNoConsent) {
549   base::DictionaryValue message;
550   message.SetString("type", "startDaemon");
551   message.Set("config", base::DictionaryValue().DeepCopy());
552   TestBadRequest(message);
553 }
554
555 }  // namespace remoting