Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / extensions / api / messaging / native_message_process_host_unittest.cc
blob687da92a435ac34552cb53b3d10ff419458405df
1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/api/messaging/native_message_process_host.h"
7 #include "base/bind.h"
8 #include "base/files/file.h"
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_file.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/json/json_reader.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/rand_util.h"
17 #include "base/run_loop.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/test/test_timeouts.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/threading/sequenced_worker_pool.h"
22 #include "base/time/time.h"
23 #include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
24 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
25 #include "chrome/common/extensions/features/feature_channel.h"
26 #include "components/version_info/version_info.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/test/test_browser_thread_bundle.h"
29 #include "extensions/common/extension.h"
30 #include "testing/gtest/include/gtest/gtest.h"
32 #if defined(OS_WIN)
33 #include <windows.h>
34 #include "base/win/scoped_handle.h"
35 #else
36 #include <unistd.h>
37 #endif
39 using content::BrowserThread;
41 namespace {
43 const char kTestMessage[] = "{\"text\": \"Hello.\"}";
45 } // namespace
47 namespace extensions {
49 class FakeLauncher : public NativeProcessLauncher {
50 public:
51 FakeLauncher(base::File read_file, base::File write_file)
52 : read_file_(read_file.Pass()),
53 write_file_(write_file.Pass()) {
56 static scoped_ptr<NativeProcessLauncher> Create(base::FilePath read_file,
57 base::FilePath write_file) {
58 int read_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
59 int write_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE;
60 #if !defined(OS_POSIX)
61 read_flags |= base::File::FLAG_ASYNC;
62 write_flags |= base::File::FLAG_ASYNC;
63 #endif
64 return scoped_ptr<NativeProcessLauncher>(new FakeLauncher(
65 base::File(read_file, read_flags),
66 base::File(write_file, write_flags)));
69 static scoped_ptr<NativeProcessLauncher> CreateWithPipeInput(
70 base::File read_pipe,
71 base::FilePath write_file) {
72 int write_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE;
73 #if !defined(OS_POSIX)
74 write_flags |= base::File::FLAG_ASYNC;
75 #endif
77 return scoped_ptr<NativeProcessLauncher>(new FakeLauncher(
78 read_pipe.Pass(),
79 base::File(write_file, write_flags)));
82 void Launch(const GURL& origin,
83 const std::string& native_host_name,
84 const LaunchedCallback& callback) const override {
85 callback.Run(NativeProcessLauncher::RESULT_SUCCESS,
86 base::Process(), read_file_.Pass(), write_file_.Pass());
89 private:
90 mutable base::File read_file_;
91 mutable base::File write_file_;
94 class NativeMessagingTest : public ::testing::Test,
95 public NativeMessageHost::Client,
96 public base::SupportsWeakPtr<NativeMessagingTest> {
97 protected:
98 NativeMessagingTest()
99 : current_channel_(version_info::Channel::DEV),
100 thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
101 channel_closed_(false) {}
103 void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
105 void TearDown() override {
106 if (native_message_host_.get()) {
107 BrowserThread::DeleteSoon(
108 BrowserThread::IO, FROM_HERE, native_message_host_.release());
110 base::RunLoop().RunUntilIdle();
113 void PostMessageFromNativeHost(const std::string& message) override {
114 last_message_ = message;
116 // Parse the message.
117 base::Value* parsed = base::JSONReader::DeprecatedRead(message);
118 base::DictionaryValue* dict_value;
119 if (parsed && parsed->GetAsDictionary(&dict_value)) {
120 last_message_parsed_.reset(dict_value);
121 } else {
122 LOG(ERROR) << "Failed to parse " << message;
123 last_message_parsed_.reset();
124 delete parsed;
127 if (run_loop_)
128 run_loop_->Quit();
131 void CloseChannel(const std::string& error_message) override {
132 channel_closed_ = true;
133 if (run_loop_)
134 run_loop_->Quit();
137 protected:
138 std::string FormatMessage(const std::string& message) {
139 uint32_t length = message.length();
140 return std::string(reinterpret_cast<char*>(&length), 4).append(message);
143 base::FilePath CreateTempFileWithMessage(const std::string& message) {
144 base::FilePath filename;
145 if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &filename))
146 return base::FilePath();
148 std::string message_with_header = FormatMessage(message);
149 int bytes_written = base::WriteFile(
150 filename, message_with_header.data(), message_with_header.size());
151 if (bytes_written < 0 ||
152 (message_with_header.size() != static_cast<size_t>(bytes_written))) {
153 return base::FilePath();
155 return filename;
158 base::ScopedTempDir temp_dir_;
159 // Force the channel to be dev.
160 ScopedCurrentChannel current_channel_;
161 scoped_ptr<NativeMessageHost> native_message_host_;
162 scoped_ptr<base::RunLoop> run_loop_;
163 content::TestBrowserThreadBundle thread_bundle_;
164 std::string last_message_;
165 scoped_ptr<base::DictionaryValue> last_message_parsed_;
166 bool channel_closed_;
169 // Read a single message from a local file.
170 TEST_F(NativeMessagingTest, SingleSendMessageRead) {
171 base::FilePath temp_output_file = temp_dir_.path().AppendASCII("output");
172 base::FilePath temp_input_file = CreateTempFileWithMessage(kTestMessage);
173 ASSERT_FALSE(temp_input_file.empty());
175 scoped_ptr<NativeProcessLauncher> launcher =
176 FakeLauncher::Create(temp_input_file, temp_output_file).Pass();
177 native_message_host_ = NativeMessageProcessHost::CreateWithLauncher(
178 ScopedTestNativeMessagingHost::kExtensionId,
179 "empty_app.py",
180 launcher.Pass());
181 native_message_host_->Start(this);
182 ASSERT_TRUE(native_message_host_.get());
183 run_loop_.reset(new base::RunLoop());
184 run_loop_->RunUntilIdle();
186 if (last_message_.empty()) {
187 run_loop_.reset(new base::RunLoop());
188 scoped_ptr<NativeMessageProcessHost> native_message_process_host_(
189 static_cast<NativeMessageProcessHost*>(native_message_host_.release()));
190 native_message_process_host_->ReadNowForTesting();
191 run_loop_->Run();
193 EXPECT_EQ(kTestMessage, last_message_);
196 // Tests sending a single message. The message should get written to
197 // |temp_file| and should match the contents of single_message_request.msg.
198 TEST_F(NativeMessagingTest, SingleSendMessageWrite) {
199 base::FilePath temp_output_file = temp_dir_.path().AppendASCII("output");
201 base::File read_file;
202 #if defined(OS_WIN)
203 base::string16 pipe_name = base::StringPrintf(
204 L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", base::RandUint64());
205 base::File write_handle = base::File::CreateForAsyncHandle(
206 CreateNamedPipeW(pipe_name.c_str(),
207 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
208 FILE_FLAG_FIRST_PIPE_INSTANCE,
209 PIPE_TYPE_BYTE, 1, 0, 0, 5000, NULL));
210 ASSERT_TRUE(write_handle.IsValid());
211 base::File read_handle = base::File::CreateForAsyncHandle(
212 CreateFileW(pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
213 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL));
214 ASSERT_TRUE(read_handle.IsValid());
216 read_file = read_handle.Pass();
217 #else // defined(OS_WIN)
218 base::PlatformFile pipe_handles[2];
219 ASSERT_EQ(0, pipe(pipe_handles));
220 read_file = base::File(pipe_handles[0]);
221 base::File write_file(pipe_handles[1]);
222 #endif // !defined(OS_WIN)
224 scoped_ptr<NativeProcessLauncher> launcher =
225 FakeLauncher::CreateWithPipeInput(read_file.Pass(),
226 temp_output_file).Pass();
227 native_message_host_ = NativeMessageProcessHost::CreateWithLauncher(
228 ScopedTestNativeMessagingHost::kExtensionId,
229 "empty_app.py",
230 launcher.Pass());
231 native_message_host_->Start(this);
232 ASSERT_TRUE(native_message_host_.get());
233 base::RunLoop().RunUntilIdle();
235 native_message_host_->OnMessage(kTestMessage);
236 base::RunLoop().RunUntilIdle();
238 std::string output;
239 base::TimeTicks start_time = base::TimeTicks::Now();
240 while (base::TimeTicks::Now() - start_time < TestTimeouts::action_timeout()) {
241 ASSERT_TRUE(base::ReadFileToString(temp_output_file, &output));
242 if (!output.empty())
243 break;
244 base::PlatformThread::YieldCurrentThread();
247 EXPECT_EQ(FormatMessage(kTestMessage), output);
250 // Test send message with a real client. The client just echo's back the text
251 // it received.
252 TEST_F(NativeMessagingTest, EchoConnect) {
253 ScopedTestNativeMessagingHost test_host;
254 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(false));
255 std::string error_message;
256 native_message_host_ = NativeMessageProcessHost::Create(
257 NULL,
258 ScopedTestNativeMessagingHost::kExtensionId,
259 ScopedTestNativeMessagingHost::kHostName,
260 false,
261 &error_message);
262 native_message_host_->Start(this);
263 ASSERT_TRUE(native_message_host_.get());
265 native_message_host_->OnMessage("{\"text\": \"Hello.\"}");
266 run_loop_.reset(new base::RunLoop());
267 run_loop_->Run();
268 ASSERT_FALSE(last_message_.empty());
269 ASSERT_TRUE(last_message_parsed_);
271 std::string expected_url = std::string("chrome-extension://") +
272 ScopedTestNativeMessagingHost::kExtensionId + "/";
273 int id;
274 EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id));
275 EXPECT_EQ(1, id);
276 std::string text;
277 EXPECT_TRUE(last_message_parsed_->GetString("echo.text", &text));
278 EXPECT_EQ("Hello.", text);
279 std::string url;
280 EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url));
281 EXPECT_EQ(expected_url, url);
283 native_message_host_->OnMessage("{\"foo\": \"bar\"}");
284 run_loop_.reset(new base::RunLoop());
285 run_loop_->Run();
286 EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id));
287 EXPECT_EQ(2, id);
288 EXPECT_TRUE(last_message_parsed_->GetString("echo.foo", &text));
289 EXPECT_EQ("bar", text);
290 EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url));
291 EXPECT_EQ(expected_url, url);
294 TEST_F(NativeMessagingTest, UserLevel) {
295 ScopedTestNativeMessagingHost test_host;
296 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(true));
298 std::string error_message;
299 native_message_host_ = NativeMessageProcessHost::Create(
300 NULL,
301 ScopedTestNativeMessagingHost::kExtensionId,
302 ScopedTestNativeMessagingHost::kHostName,
303 true,
304 &error_message);
305 native_message_host_->Start(this);
306 ASSERT_TRUE(native_message_host_.get());
308 native_message_host_->OnMessage("{\"text\": \"Hello.\"}");
309 run_loop_.reset(new base::RunLoop());
310 run_loop_->Run();
311 ASSERT_FALSE(last_message_.empty());
312 ASSERT_TRUE(last_message_parsed_);
315 TEST_F(NativeMessagingTest, DisallowUserLevel) {
316 ScopedTestNativeMessagingHost test_host;
317 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(true));
319 std::string error_message;
320 native_message_host_ = NativeMessageProcessHost::Create(
321 NULL,
322 ScopedTestNativeMessagingHost::kExtensionId,
323 ScopedTestNativeMessagingHost::kHostName,
324 false,
325 &error_message);
326 native_message_host_->Start(this);
327 ASSERT_TRUE(native_message_host_.get());
328 run_loop_.reset(new base::RunLoop());
329 run_loop_->Run();
331 // The host should fail to start.
332 ASSERT_TRUE(channel_closed_);
335 } // namespace extensions