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

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

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

Powered by Google App Engine
This is Rietveld 408576698