Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(132)

Side by Side Diff: remoting/host/setup/native_messaging_host_unittest.cc

Issue 49113003: It2Me native messaging: GYP and source refactoring (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Max/Linux build break: remoting_native_messaging_host Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/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/pin_hash.h"
21 #include "remoting/host/setup/native_messaging_channel.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
OLDNEW
« no previous file with comments | « remoting/host/setup/native_messaging_host_main.cc ('k') | remoting/host/setup/native_messaging_manifest.json » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698