Index: remoting/host/setup/native_messaging_host_unittest.cc |
diff --git a/remoting/host/setup/native_messaging_host_unittest.cc b/remoting/host/setup/native_messaging_host_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7944e9d70818044acbc3db51bcf89edcb332d7bd |
--- /dev/null |
+++ b/remoting/host/setup/native_messaging_host_unittest.cc |
@@ -0,0 +1,404 @@ |
+// Copyright 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "base/compiler_specific.h" |
+#include "base/file_util.h" |
+#include "base/json/json_reader.h" |
+#include "base/json/json_writer.h" |
+#include "base/message_loop.h" |
+#include "base/run_loop.h" |
+#include "base/stl_util.h" |
+#include "base/strings/stringize_macros.h" |
+#include "base/values.h" |
+#include "net/base/file_stream.h" |
+#include "net/base/net_util.h" |
+#include "remoting/host/pin_hash.h" |
+#include "remoting/host/setup/mock_daemon_controller.h" |
+#include "remoting/host/setup/native_messaging_host.h" |
Sergey Ulanov
2013/05/18 01:59:31
nit: this should be first include for this file.
Lambros
2013/05/22 21:42:18
Done.
|
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace { |
+ |
+void VerifyHelloResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "helloResponse"); |
+ EXPECT_TRUE(response->GetString("version", &value)); |
+ EXPECT_EQ(value, STRINGIZE(VERSION)); |
+} |
+ |
+void VerifyGetHostNameResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "getHostNameResponse"); |
+ EXPECT_TRUE(response->GetString("hostname", &value)); |
+ EXPECT_EQ(value, net::GetHostName()); |
+} |
+ |
+void VerifyGetPinHashResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "getPinHashResponse"); |
+ EXPECT_TRUE(response->GetString("hash", &value)); |
+ EXPECT_EQ(value, remoting::MakeHostPinHash("my_host", "1234")); |
+} |
+ |
+void VerifyGenerateKeyPairResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "generateKeyPairResponse"); |
+ EXPECT_TRUE(response->GetString("private_key", &value)); |
+ EXPECT_TRUE(response->GetString("public_key", &value)); |
+} |
+ |
+void VerifyGetDaemonConfigResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "getDaemonConfigResponse"); |
+ EXPECT_TRUE(response->GetString("config", &value)); |
+ EXPECT_EQ(value, "{}"); |
+} |
+ |
+void VerifyGetUsageStatsConsentResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "getUsageStatsConsentResponse"); |
+ bool supported, allowed, set_by_policy; |
+ EXPECT_TRUE(response->GetBoolean("supported", &supported)); |
+ EXPECT_TRUE(response->GetBoolean("allowed", &allowed)); |
+ EXPECT_TRUE(response->GetBoolean("set_by_policy", &set_by_policy)); |
+ EXPECT_TRUE(supported); |
+ EXPECT_TRUE(allowed); |
+ EXPECT_TRUE(set_by_policy); |
+} |
+ |
+void VerifyStopDaemonResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "stopDaemonResponse"); |
+ int result; |
+ EXPECT_TRUE(response->GetInteger("result", &result)); |
+ EXPECT_EQ(result, 0); |
+} |
+ |
+void VerifyGetDaemonStateResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "getDaemonStateResponse"); |
+ int result; |
+ EXPECT_TRUE(response->GetInteger("state", &result)); |
+ EXPECT_EQ(result, 4); |
+} |
+ |
+void VerifyUpdateDaemonConfigResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "updateDaemonConfigResponse"); |
+ int result; |
+ EXPECT_TRUE(response->GetInteger("result", &result)); |
+ EXPECT_EQ(result, 0); |
+} |
+ |
+void VerifyStartDaemonResponse(const base::DictionaryValue* response) { |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_TRUE(response->GetString("type", &value)); |
+ EXPECT_EQ(value, "startDaemonResponse"); |
+ int result; |
+ EXPECT_TRUE(response->GetInteger("result", &result)); |
+ EXPECT_EQ(result, 0); |
+} |
+ |
+scoped_ptr<base::DictionaryValue> ReadMessageFromFile( |
Sergey Ulanov
2013/05/18 01:59:31
Can you use FileStreams to read from files?
Lambros
2013/05/22 21:42:18
Probably, but not sure if there's any advantage, o
|
+ base::PlatformFile handle) { |
+ uint32 length; |
+ int read_result = base::ReadPlatformFileAtCurrentPos( |
+ handle, reinterpret_cast<char*>(&length), sizeof(length)); |
+ if (read_result != sizeof(length)) { |
+ return scoped_ptr<base::DictionaryValue>(); |
+ } |
+ |
+ std::string message_json(length, '\0'); |
+ read_result = base::ReadPlatformFileAtCurrentPos( |
+ handle, string_as_array(&message_json), length); |
+ if (read_result != static_cast<int>(length)) { |
+ return scoped_ptr<base::DictionaryValue>(); |
+ } |
+ |
+ scoped_ptr<base::Value> message(base::JSONReader::Read(message_json)); |
+ if (!message || !message->IsType(base::Value::TYPE_DICTIONARY)) { |
+ return scoped_ptr<base::DictionaryValue>(); |
+ } |
+ |
+ return scoped_ptr<base::DictionaryValue>( |
+ static_cast<base::DictionaryValue*>(message.release())); |
+} |
+ |
+} // namespace |
+ |
+namespace remoting { |
+ |
+class NativeMessagingHostTest : public testing::Test { |
+ public: |
+ NativeMessagingHostTest(); |
+ virtual ~NativeMessagingHostTest(); |
+ |
+ virtual void SetUp() OVERRIDE; |
+ virtual void TearDown() OVERRIDE; |
+ |
+ void Run(); |
+ |
+ void WriteMessageToInputFile(const base::Value& message); |
+ |
+ // The Host process should shut down when it receives a malformed request. |
+ // This is tested by sending a known-good request, followed by |message|, |
+ // followed by the known-good request again. The response file should only |
+ // contain a single response from the first good request. |
+ void TestBadRequest(const base::Value& message); |
+ |
+ base::FilePath output_path() const { return output_path_; } |
+ |
+ private: |
+ base::FilePath input_path_; |
+ base::PlatformFile input_handle_; |
+ base::FilePath output_path_; |
+ base::PlatformFile output_handle_; |
+ |
+ base::MessageLoop message_loop_; |
+ base::RunLoop run_loop_; |
+ scoped_ptr<remoting::NativeMessagingHost> host_; |
+}; |
+ |
+NativeMessagingHostTest::NativeMessagingHostTest() |
+ : message_loop_(base::MessageLoop::TYPE_IO) {} |
+ |
+NativeMessagingHostTest::~NativeMessagingHostTest() {} |
+ |
+void NativeMessagingHostTest::SetUp() { |
+ file_util::CreateTemporaryFile(&input_path_); |
+ input_handle_ = base::CreatePlatformFile( |
+ input_path_, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL, |
+ NULL); |
+ |
+ file_util::CreateTemporaryFile(&output_path_); |
+ output_handle_ = base::CreatePlatformFile( |
+ output_path_, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE, NULL, |
+ NULL); |
+ |
+ host_.reset(new NativeMessagingHost(input_handle_, output_handle_, |
+ message_loop_.message_loop_proxy(), |
+ run_loop_.QuitClosure())); |
+ host_->SetDaemonControllerForTest( |
+ scoped_ptr<DaemonController>(new MockDaemonController())); |
+} |
+ |
+void NativeMessagingHostTest::TearDown() { |
+ base::ClosePlatformFile(input_handle_); |
+ base::ClosePlatformFile(output_handle_); |
+ EXPECT_TRUE(file_util::Delete(input_path_, false)); |
+ EXPECT_TRUE(file_util::Delete(output_path_, false)); |
+} |
+ |
+void NativeMessagingHostTest::Run() { |
+ host_->Start(); |
+ run_loop_.Run(); |
+} |
+ |
+void NativeMessagingHostTest::WriteMessageToInputFile( |
+ const base::Value& message) { |
+ std::string message_json; |
+ base::JSONWriter::Write(&message, &message_json); |
+ |
+ uint32 length = message_json.length(); |
+ file_util::AppendToFile(input_path_, reinterpret_cast<char*>(&length), |
+ sizeof(length)); |
+ file_util::AppendToFile(input_path_, message_json.data(), length); |
+} |
+ |
+void NativeMessagingHostTest::TestBadRequest(const base::Value& message) { |
+ base::DictionaryValue good_message; |
+ good_message.SetString("type", "hello"); |
+ |
+ WriteMessageToInputFile(good_message); |
+ WriteMessageToInputFile(message); |
+ WriteMessageToInputFile(good_message); |
+ |
+ Run(); |
+ |
+ // Read from output file, and verify responses. |
+ base::PlatformFile handle = base::CreatePlatformFile( |
+ output_path(), base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL, |
+ NULL); |
+ |
+ scoped_ptr<base::DictionaryValue> response = ReadMessageFromFile(handle); |
+ VerifyHelloResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ EXPECT_FALSE(response); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, All) { |
+ base::DictionaryValue message; |
+ message.SetString("type", "hello"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.SetString("type", "getHostName"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.SetString("type", "getPinHash"); |
+ message.SetString("hostId", "my_host"); |
+ message.SetString("pin", "1234"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.Clear(); |
+ message.SetString("type", "generateKeyPair"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.SetString("type", "getDaemonConfig"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.SetString("type", "getUsageStatsConsent"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.SetString("type", "stopDaemon"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.SetString("type", "getDaemonState"); |
+ WriteMessageToInputFile(message); |
+ |
+ // Following messages require a "config" dictionary. |
+ message.SetString("config", "{}"); |
+ message.SetString("type", "updateDaemonConfig"); |
+ WriteMessageToInputFile(message); |
+ |
+ message.SetBoolean("consent", true); |
+ message.SetString("type", "startDaemon"); |
+ WriteMessageToInputFile(message); |
+ |
+ Run(); |
+ |
+ // Read from output file, and verify responses. |
+ base::PlatformFile handle = base::CreatePlatformFile( |
+ output_path(), base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL, |
+ NULL); |
+ |
+ scoped_ptr<base::DictionaryValue> response = ReadMessageFromFile(handle); |
+ VerifyHelloResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyGetHostNameResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyGetPinHashResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyGenerateKeyPairResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyGetDaemonConfigResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyGetUsageStatsConsentResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyStopDaemonResponse(response.get()); |
Sergey Ulanov
2013/05/18 01:59:31
This doesn't really verify that daemon controller
Lambros
2013/05/22 21:42:18
I've improved the mock object to try to address th
|
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyGetDaemonStateResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyUpdateDaemonConfigResponse(response.get()); |
+ |
+ response = ReadMessageFromFile(handle); |
+ VerifyStartDaemonResponse(response.get()); |
+ |
+ base::ClosePlatformFile(handle); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, Id) { |
Sergey Ulanov
2013/05/18 01:59:31
Please add short description for each test, e.g.
Lambros
2013/05/22 21:42:18
Done.
|
+ base::DictionaryValue message; |
+ message.SetString("type", "hello"); |
+ WriteMessageToInputFile(message); |
+ message.SetString("id", "42"); |
+ WriteMessageToInputFile(message); |
+ |
+ Run(); |
+ |
+ base::PlatformFile handle = base::CreatePlatformFile( |
+ output_path(), base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ, NULL, |
+ NULL); |
+ scoped_ptr<base::DictionaryValue> response = ReadMessageFromFile(handle); |
+ EXPECT_TRUE(response); |
+ std::string value; |
+ EXPECT_FALSE(response->GetString("id", &value)); |
+ |
+ response = ReadMessageFromFile(handle); |
+ EXPECT_TRUE(response); |
+ EXPECT_TRUE(response->GetString("id", &value)); |
+ EXPECT_EQ(value, "42"); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, WrongFormat) { |
+ // Request should be a Dictionary. |
+ base::ListValue message; |
+ TestBadRequest(message); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, MissingType) { |
+ base::DictionaryValue message; |
+ TestBadRequest(message); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, InvalidType) { |
+ base::DictionaryValue message; |
+ message.SetString("type", "xxx"); |
+ TestBadRequest(message); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, GetPinHashNoHostId) { |
+ base::DictionaryValue message; |
+ message.SetString("type", "getPinHash"); |
+ message.SetString("pin", "1234"); |
+ TestBadRequest(message); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, GetPinHashNoPin) { |
+ base::DictionaryValue message; |
+ message.SetString("type", "getPinHash"); |
+ message.SetString("hostId", "my_host"); |
+ TestBadRequest(message); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, UpdateDaemonConfigInvalidConfig) { |
+ base::DictionaryValue message; |
+ message.SetString("type", "updateDaemonConfig"); |
+ message.SetString("config", "xxx"); |
+ TestBadRequest(message); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, StartDaemonInvalidConfig) { |
+ base::DictionaryValue message; |
+ message.SetString("type", "startDaemon"); |
+ message.SetString("config", "xxx"); |
+ message.SetBoolean("consent", true); |
+ TestBadRequest(message); |
+} |
+ |
+TEST_F(NativeMessagingHostTest, StartDaemonNoConsent) { |
+ base::DictionaryValue message; |
+ message.SetString("type", "startDaemon"); |
+ message.SetString("config", "{}"); |
+ TestBadRequest(message); |
+} |
+ |
+} // namespace remoting |