Revert 285173 "Removed InProcessBrowserTest::CleanUpOnMainThread()"
[chromium-blink-merge.git] / chrome / browser / extensions / api / messaging / native_message_process_host_unittest.cc
blob81e811ba273b661a580901a07faf2e4190c067ab
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 "base/bind.h"
6 #include "base/file_util.h"
7 #include "base/files/file.h"
8 #include "base/files/file_path.h"
9 #include "base/files/scoped_file.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/json/json_reader.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/rand_util.h"
16 #include "base/run_loop.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/test/test_timeouts.h"
19 #include "base/threading/platform_thread.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "base/time/time.h"
22 #include "chrome/browser/extensions/api/messaging/native_message_process_host.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/chrome_version_info.h"
26 #include "chrome/common/extensions/features/feature_channel.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 virtual void Launch(const GURL& origin,
83 const std::string& native_host_name,
84 LaunchedCallback callback) const OVERRIDE {
85 callback.Run(NativeProcessLauncher::RESULT_SUCCESS,
86 base::kNullProcessHandle,
87 read_file_.Pass(), write_file_.Pass());
90 private:
91 mutable base::File read_file_;
92 mutable base::File write_file_;
95 class NativeMessagingTest : public ::testing::Test,
96 public NativeMessageProcessHost::Client,
97 public base::SupportsWeakPtr<NativeMessagingTest> {
98 protected:
99 NativeMessagingTest()
100 : current_channel_(chrome::VersionInfo::CHANNEL_DEV),
101 thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
102 channel_closed_(false) {}
104 virtual void SetUp() OVERRIDE {
105 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
108 virtual void TearDown() OVERRIDE {
109 if (native_message_process_host_.get()) {
110 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE,
111 native_message_process_host_.release());
113 base::RunLoop().RunUntilIdle();
116 virtual void PostMessageFromNativeProcess(
117 int port_id,
118 const std::string& message) OVERRIDE {
119 last_message_ = message;
121 // Parse the message.
122 base::Value* parsed = base::JSONReader::Read(message);
123 base::DictionaryValue* dict_value;
124 if (parsed && parsed->GetAsDictionary(&dict_value)) {
125 last_message_parsed_.reset(dict_value);
126 } else {
127 LOG(ERROR) << "Failed to parse " << message;
128 last_message_parsed_.reset();
129 delete parsed;
132 if (run_loop_)
133 run_loop_->Quit();
136 virtual void CloseChannel(int port_id,
137 const std::string& error_message) OVERRIDE {
138 channel_closed_ = true;
139 if (run_loop_)
140 run_loop_->Quit();
143 protected:
144 std::string FormatMessage(const std::string& message) {
145 uint32_t length = message.length();
146 return std::string(reinterpret_cast<char*>(&length), 4).append(message);
149 base::FilePath CreateTempFileWithMessage(const std::string& message) {
150 base::FilePath filename;
151 if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &filename))
152 return base::FilePath();
154 std::string message_with_header = FormatMessage(message);
155 int bytes_written = base::WriteFile(
156 filename, message_with_header.data(), message_with_header.size());
157 if (bytes_written < 0 ||
158 (message_with_header.size() != static_cast<size_t>(bytes_written))) {
159 return base::FilePath();
161 return filename;
164 base::ScopedTempDir temp_dir_;
165 // Force the channel to be dev.
166 ScopedCurrentChannel current_channel_;
167 scoped_ptr<NativeMessageProcessHost> native_message_process_host_;
168 scoped_ptr<base::RunLoop> run_loop_;
169 content::TestBrowserThreadBundle thread_bundle_;
170 std::string last_message_;
171 scoped_ptr<base::DictionaryValue> last_message_parsed_;
172 bool channel_closed_;
175 // Read a single message from a local file.
176 TEST_F(NativeMessagingTest, SingleSendMessageRead) {
177 base::FilePath temp_output_file = temp_dir_.path().AppendASCII("output");
178 base::FilePath temp_input_file = CreateTempFileWithMessage(kTestMessage);
179 ASSERT_FALSE(temp_input_file.empty());
181 scoped_ptr<NativeProcessLauncher> launcher =
182 FakeLauncher::Create(temp_input_file, temp_output_file).Pass();
183 native_message_process_host_ = NativeMessageProcessHost::CreateWithLauncher(
184 AsWeakPtr(), ScopedTestNativeMessagingHost::kExtensionId, "empty_app.py",
185 0, launcher.Pass());
186 ASSERT_TRUE(native_message_process_host_.get());
187 run_loop_.reset(new base::RunLoop());
188 run_loop_->RunUntilIdle();
190 if (last_message_.empty()) {
191 run_loop_.reset(new base::RunLoop());
192 native_message_process_host_->ReadNowForTesting();
193 run_loop_->Run();
195 EXPECT_EQ(kTestMessage, last_message_);
198 // Tests sending a single message. The message should get written to
199 // |temp_file| and should match the contents of single_message_request.msg.
200 TEST_F(NativeMessagingTest, SingleSendMessageWrite) {
201 base::FilePath temp_output_file = temp_dir_.path().AppendASCII("output");
203 base::File read_file;
204 #if defined(OS_WIN)
205 base::string16 pipe_name = base::StringPrintf(
206 L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", base::RandUint64());
207 base::File write_handle(
208 CreateNamedPipeW(pipe_name.c_str(),
209 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
210 FILE_FLAG_FIRST_PIPE_INSTANCE,
211 PIPE_TYPE_BYTE, 1, 0, 0, 5000, NULL));
212 ASSERT_TRUE(write_handle.IsValid());
213 base::File read_handle(
214 CreateFileW(pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
215 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL));
216 ASSERT_TRUE(read_handle.IsValid());
218 read_file = read_handle.Pass();
219 #else // defined(OS_WIN)
220 base::PlatformFile pipe_handles[2];
221 ASSERT_EQ(0, pipe(pipe_handles));
222 read_file = base::File(pipe_handles[0]);
223 base::File write_file(pipe_handles[1]);
224 #endif // !defined(OS_WIN)
226 scoped_ptr<NativeProcessLauncher> launcher =
227 FakeLauncher::CreateWithPipeInput(read_file.Pass(),
228 temp_output_file).Pass();
229 native_message_process_host_ = NativeMessageProcessHost::CreateWithLauncher(
230 AsWeakPtr(), ScopedTestNativeMessagingHost::kExtensionId, "empty_app.py",
231 0, launcher.Pass());
232 ASSERT_TRUE(native_message_process_host_.get());
233 base::RunLoop().RunUntilIdle();
235 native_message_process_host_->Send(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));
256 native_message_process_host_ = NativeMessageProcessHost::Create(
257 NULL, AsWeakPtr(), ScopedTestNativeMessagingHost::kExtensionId,
258 ScopedTestNativeMessagingHost::kHostName, 0, false);
259 ASSERT_TRUE(native_message_process_host_.get());
261 native_message_process_host_->Send("{\"text\": \"Hello.\"}");
262 run_loop_.reset(new base::RunLoop());
263 run_loop_->Run();
264 ASSERT_FALSE(last_message_.empty());
265 ASSERT_TRUE(last_message_parsed_);
267 std::string expected_url = std::string("chrome-extension://") +
268 ScopedTestNativeMessagingHost::kExtensionId + "/";
269 int id;
270 EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id));
271 EXPECT_EQ(1, id);
272 std::string text;
273 EXPECT_TRUE(last_message_parsed_->GetString("echo.text", &text));
274 EXPECT_EQ("Hello.", text);
275 std::string url;
276 EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url));
277 EXPECT_EQ(expected_url, url);
279 native_message_process_host_->Send("{\"foo\": \"bar\"}");
280 run_loop_.reset(new base::RunLoop());
281 run_loop_->Run();
282 EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id));
283 EXPECT_EQ(2, id);
284 EXPECT_TRUE(last_message_parsed_->GetString("echo.foo", &text));
285 EXPECT_EQ("bar", text);
286 EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url));
287 EXPECT_EQ(expected_url, url);
290 TEST_F(NativeMessagingTest, UserLevel) {
291 ScopedTestNativeMessagingHost test_host;
292 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(true));
294 native_message_process_host_ = NativeMessageProcessHost::Create(
295 NULL, AsWeakPtr(), ScopedTestNativeMessagingHost::kExtensionId,
296 ScopedTestNativeMessagingHost::kHostName, 0, true);
297 ASSERT_TRUE(native_message_process_host_.get());
299 native_message_process_host_->Send("{\"text\": \"Hello.\"}");
300 run_loop_.reset(new base::RunLoop());
301 run_loop_->Run();
302 ASSERT_FALSE(last_message_.empty());
303 ASSERT_TRUE(last_message_parsed_);
306 TEST_F(NativeMessagingTest, DisallowUserLevel) {
307 ScopedTestNativeMessagingHost test_host;
308 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(true));
310 native_message_process_host_ = NativeMessageProcessHost::Create(
311 NULL, AsWeakPtr(), ScopedTestNativeMessagingHost::kExtensionId,
312 ScopedTestNativeMessagingHost::kHostName, 0, false);
313 ASSERT_TRUE(native_message_process_host_.get());
314 run_loop_.reset(new base::RunLoop());
315 run_loop_->Run();
317 // The host should fail to start.
318 ASSERT_TRUE(channel_closed_);
321 } // namespace extensions