Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / extensions / api / messaging / native_message_process_host_unittest.cc
blobe8fa3f317d61a7bdfe34a08f7b09230304809799
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/files/file.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.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/message_loop/message_loop.h"
14 #include "base/rand_util.h"
15 #include "base/run_loop.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/test/test_timeouts.h"
18 #include "base/threading/platform_thread.h"
19 #include "base/threading/sequenced_worker_pool.h"
20 #include "base/time/time.h"
21 #include "chrome/browser/extensions/api/messaging/native_message_process_host.h"
22 #include "chrome/browser/extensions/api/messaging/native_messaging_test_util.h"
23 #include "chrome/browser/extensions/api/messaging/native_process_launcher.h"
24 #include "chrome/common/chrome_version_info.h"
25 #include "chrome/common/extensions/features/feature_channel.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "extensions/common/extension.h"
29 #include "testing/gtest/include/gtest/gtest.h"
31 #if defined(OS_WIN)
32 #include <windows.h>
33 #include "base/win/scoped_handle.h"
34 #else
35 #include <unistd.h>
36 #endif
38 using content::BrowserThread;
40 namespace {
42 const char kTestMessage[] = "{\"text\": \"Hello.\"}";
44 } // namespace
46 namespace extensions {
48 class FakeLauncher : public NativeProcessLauncher {
49 public:
50 FakeLauncher(base::File read_file, base::File write_file)
51 : read_file_(read_file.Pass()),
52 write_file_(write_file.Pass()) {
55 static scoped_ptr<NativeProcessLauncher> Create(base::FilePath read_file,
56 base::FilePath write_file) {
57 int read_flags = base::File::FLAG_OPEN | base::File::FLAG_READ;
58 int write_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE;
59 #if !defined(OS_POSIX)
60 read_flags |= base::File::FLAG_ASYNC;
61 write_flags |= base::File::FLAG_ASYNC;
62 #endif
63 return scoped_ptr<NativeProcessLauncher>(new FakeLauncher(
64 base::File(read_file, read_flags),
65 base::File(write_file, write_flags)));
68 static scoped_ptr<NativeProcessLauncher> CreateWithPipeInput(
69 base::File read_pipe,
70 base::FilePath write_file) {
71 int write_flags = base::File::FLAG_CREATE | base::File::FLAG_WRITE;
72 #if !defined(OS_POSIX)
73 write_flags |= base::File::FLAG_ASYNC;
74 #endif
76 return scoped_ptr<NativeProcessLauncher>(new FakeLauncher(
77 read_pipe.Pass(),
78 base::File(write_file, write_flags)));
81 void Launch(const GURL& origin,
82 const std::string& native_host_name,
83 const LaunchedCallback& callback) const override {
84 callback.Run(NativeProcessLauncher::RESULT_SUCCESS,
85 base::Process(), read_file_.Pass(), write_file_.Pass());
88 private:
89 mutable base::File read_file_;
90 mutable base::File write_file_;
93 class NativeMessagingTest : public ::testing::Test,
94 public NativeMessageHost::Client,
95 public base::SupportsWeakPtr<NativeMessagingTest> {
96 protected:
97 NativeMessagingTest()
98 : current_channel_(chrome::VersionInfo::CHANNEL_DEV),
99 thread_bundle_(content::TestBrowserThreadBundle::IO_MAINLOOP),
100 channel_closed_(false) {}
102 void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); }
104 void TearDown() override {
105 if (native_message_host_.get()) {
106 BrowserThread::DeleteSoon(
107 BrowserThread::IO, FROM_HERE, native_message_host_.release());
109 base::RunLoop().RunUntilIdle();
112 void PostMessageFromNativeHost(const std::string& message) override {
113 last_message_ = message;
115 // Parse the message.
116 base::Value* parsed = base::JSONReader::DeprecatedRead(message);
117 base::DictionaryValue* dict_value;
118 if (parsed && parsed->GetAsDictionary(&dict_value)) {
119 last_message_parsed_.reset(dict_value);
120 } else {
121 LOG(ERROR) << "Failed to parse " << message;
122 last_message_parsed_.reset();
123 delete parsed;
126 if (run_loop_)
127 run_loop_->Quit();
130 void CloseChannel(const std::string& error_message) override {
131 channel_closed_ = true;
132 if (run_loop_)
133 run_loop_->Quit();
136 protected:
137 std::string FormatMessage(const std::string& message) {
138 uint32_t length = message.length();
139 return std::string(reinterpret_cast<char*>(&length), 4).append(message);
142 base::FilePath CreateTempFileWithMessage(const std::string& message) {
143 base::FilePath filename;
144 if (!base::CreateTemporaryFileInDir(temp_dir_.path(), &filename))
145 return base::FilePath();
147 std::string message_with_header = FormatMessage(message);
148 int bytes_written = base::WriteFile(
149 filename, message_with_header.data(), message_with_header.size());
150 if (bytes_written < 0 ||
151 (message_with_header.size() != static_cast<size_t>(bytes_written))) {
152 return base::FilePath();
154 return filename;
157 base::ScopedTempDir temp_dir_;
158 // Force the channel to be dev.
159 ScopedCurrentChannel current_channel_;
160 scoped_ptr<NativeMessageHost> native_message_host_;
161 scoped_ptr<base::RunLoop> run_loop_;
162 content::TestBrowserThreadBundle thread_bundle_;
163 std::string last_message_;
164 scoped_ptr<base::DictionaryValue> last_message_parsed_;
165 bool channel_closed_;
168 // Read a single message from a local file.
169 TEST_F(NativeMessagingTest, SingleSendMessageRead) {
170 base::FilePath temp_output_file = temp_dir_.path().AppendASCII("output");
171 base::FilePath temp_input_file = CreateTempFileWithMessage(kTestMessage);
172 ASSERT_FALSE(temp_input_file.empty());
174 scoped_ptr<NativeProcessLauncher> launcher =
175 FakeLauncher::Create(temp_input_file, temp_output_file).Pass();
176 native_message_host_ = NativeMessageProcessHost::CreateWithLauncher(
177 ScopedTestNativeMessagingHost::kExtensionId,
178 "empty_app.py",
179 launcher.Pass());
180 native_message_host_->Start(this);
181 ASSERT_TRUE(native_message_host_.get());
182 run_loop_.reset(new base::RunLoop());
183 run_loop_->RunUntilIdle();
185 if (last_message_.empty()) {
186 run_loop_.reset(new base::RunLoop());
187 scoped_ptr<NativeMessageProcessHost> native_message_process_host_(
188 static_cast<NativeMessageProcessHost*>(native_message_host_.release()));
189 native_message_process_host_->ReadNowForTesting();
190 run_loop_->Run();
192 EXPECT_EQ(kTestMessage, last_message_);
195 // Tests sending a single message. The message should get written to
196 // |temp_file| and should match the contents of single_message_request.msg.
197 TEST_F(NativeMessagingTest, SingleSendMessageWrite) {
198 base::FilePath temp_output_file = temp_dir_.path().AppendASCII("output");
200 base::File read_file;
201 #if defined(OS_WIN)
202 base::string16 pipe_name = base::StringPrintf(
203 L"\\\\.\\pipe\\chrome.nativeMessaging.out.%llx", base::RandUint64());
204 base::File write_handle = base::File::CreateForAsyncHandle(
205 CreateNamedPipeW(pipe_name.c_str(),
206 PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED |
207 FILE_FLAG_FIRST_PIPE_INSTANCE,
208 PIPE_TYPE_BYTE, 1, 0, 0, 5000, NULL));
209 ASSERT_TRUE(write_handle.IsValid());
210 base::File read_handle = base::File::CreateForAsyncHandle(
211 CreateFileW(pipe_name.c_str(), GENERIC_READ, 0, NULL, OPEN_EXISTING,
212 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL));
213 ASSERT_TRUE(read_handle.IsValid());
215 read_file = read_handle.Pass();
216 #else // defined(OS_WIN)
217 base::PlatformFile pipe_handles[2];
218 ASSERT_EQ(0, pipe(pipe_handles));
219 read_file = base::File(pipe_handles[0]);
220 base::File write_file(pipe_handles[1]);
221 #endif // !defined(OS_WIN)
223 scoped_ptr<NativeProcessLauncher> launcher =
224 FakeLauncher::CreateWithPipeInput(read_file.Pass(),
225 temp_output_file).Pass();
226 native_message_host_ = NativeMessageProcessHost::CreateWithLauncher(
227 ScopedTestNativeMessagingHost::kExtensionId,
228 "empty_app.py",
229 launcher.Pass());
230 native_message_host_->Start(this);
231 ASSERT_TRUE(native_message_host_.get());
232 base::RunLoop().RunUntilIdle();
234 native_message_host_->OnMessage(kTestMessage);
235 base::RunLoop().RunUntilIdle();
237 std::string output;
238 base::TimeTicks start_time = base::TimeTicks::Now();
239 while (base::TimeTicks::Now() - start_time < TestTimeouts::action_timeout()) {
240 ASSERT_TRUE(base::ReadFileToString(temp_output_file, &output));
241 if (!output.empty())
242 break;
243 base::PlatformThread::YieldCurrentThread();
246 EXPECT_EQ(FormatMessage(kTestMessage), output);
249 // Test send message with a real client. The client just echo's back the text
250 // it received.
251 TEST_F(NativeMessagingTest, EchoConnect) {
252 ScopedTestNativeMessagingHost test_host;
253 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(false));
254 std::string error_message;
255 native_message_host_ = NativeMessageProcessHost::Create(
256 NULL,
257 ScopedTestNativeMessagingHost::kExtensionId,
258 ScopedTestNativeMessagingHost::kHostName,
259 false,
260 &error_message);
261 native_message_host_->Start(this);
262 ASSERT_TRUE(native_message_host_.get());
264 native_message_host_->OnMessage("{\"text\": \"Hello.\"}");
265 run_loop_.reset(new base::RunLoop());
266 run_loop_->Run();
267 ASSERT_FALSE(last_message_.empty());
268 ASSERT_TRUE(last_message_parsed_);
270 std::string expected_url = std::string("chrome-extension://") +
271 ScopedTestNativeMessagingHost::kExtensionId + "/";
272 int id;
273 EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id));
274 EXPECT_EQ(1, id);
275 std::string text;
276 EXPECT_TRUE(last_message_parsed_->GetString("echo.text", &text));
277 EXPECT_EQ("Hello.", text);
278 std::string url;
279 EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url));
280 EXPECT_EQ(expected_url, url);
282 native_message_host_->OnMessage("{\"foo\": \"bar\"}");
283 run_loop_.reset(new base::RunLoop());
284 run_loop_->Run();
285 EXPECT_TRUE(last_message_parsed_->GetInteger("id", &id));
286 EXPECT_EQ(2, id);
287 EXPECT_TRUE(last_message_parsed_->GetString("echo.foo", &text));
288 EXPECT_EQ("bar", text);
289 EXPECT_TRUE(last_message_parsed_->GetString("caller_url", &url));
290 EXPECT_EQ(expected_url, url);
293 TEST_F(NativeMessagingTest, UserLevel) {
294 ScopedTestNativeMessagingHost test_host;
295 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(true));
297 std::string error_message;
298 native_message_host_ = NativeMessageProcessHost::Create(
299 NULL,
300 ScopedTestNativeMessagingHost::kExtensionId,
301 ScopedTestNativeMessagingHost::kHostName,
302 true,
303 &error_message);
304 native_message_host_->Start(this);
305 ASSERT_TRUE(native_message_host_.get());
307 native_message_host_->OnMessage("{\"text\": \"Hello.\"}");
308 run_loop_.reset(new base::RunLoop());
309 run_loop_->Run();
310 ASSERT_FALSE(last_message_.empty());
311 ASSERT_TRUE(last_message_parsed_);
314 TEST_F(NativeMessagingTest, DisallowUserLevel) {
315 ScopedTestNativeMessagingHost test_host;
316 ASSERT_NO_FATAL_FAILURE(test_host.RegisterTestHost(true));
318 std::string error_message;
319 native_message_host_ = NativeMessageProcessHost::Create(
320 NULL,
321 ScopedTestNativeMessagingHost::kExtensionId,
322 ScopedTestNativeMessagingHost::kHostName,
323 false,
324 &error_message);
325 native_message_host_->Start(this);
326 ASSERT_TRUE(native_message_host_.get());
327 run_loop_.reset(new base::RunLoop());
328 run_loop_->Run();
330 // The host should fail to start.
331 ASSERT_TRUE(channel_closed_);
334 } // namespace extensions