summaryrefslogtreecommitdiffstats
path: root/base/zygote_manager_unittest.cc
diff options
context:
space:
mode:
authordkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-06 19:59:36 +0000
committerdkegel@google.com <dkegel@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-06-06 19:59:36 +0000
commit4883a4e4209bab557f4ba40a002936acf755205f (patch)
tree8b1aef5a4e1fe01ee5d4e178838dec8e98356177 /base/zygote_manager_unittest.cc
parente2ffeae017f1c85d472e3f04e62e4cba21f6e9a8 (diff)
downloadchromium_src-4883a4e4209bab557f4ba40a002936acf755205f.zip
chromium_src-4883a4e4209bab557f4ba40a002936acf755205f.tar.gz
chromium_src-4883a4e4209bab557f4ba40a002936acf755205f.tar.bz2
Prototype implementation of zygotes.
Limitations that need addressing still: - Doesn't forcibly terminate children that should have exited but haven't Enable with env var ENABLE_ZYGOTE_MANAGER=1. BUG=11841 TEST= start the browser, then make chrome and all .pak files unreadable; or alternately, start an installed browser, and uninstall the browser while it's running. Then create a new tab and browse to two new sites. Here's an example script to hide and unhide the .pak files (note: do not move the directory they're in, that doesn't work): #!/bin/sh chmod_all() { chmod $1 sconsbuild/Debug/chrome for path in . locales obj/chrome/app/intermediate/repack obj/global_intermediate/* themes do chmod $1 sconsbuild/Debug/$path/*.pak done } case $1 in hide) chmod_all 000 ;; show) chmod_all 755 ;; esac Review URL: http://codereview.chromium.org/115773 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@17840 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/zygote_manager_unittest.cc')
-rw-r--r--base/zygote_manager_unittest.cc266
1 files changed, 266 insertions, 0 deletions
diff --git a/base/zygote_manager_unittest.cc b/base/zygote_manager_unittest.cc
new file mode 100644
index 0000000..d879d1a
--- /dev/null
+++ b/base/zygote_manager_unittest.cc
@@ -0,0 +1,266 @@
+// Copyright (c) 2009 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/zygote_manager.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
+#include "base/file_util.h"
+#include "base/process_util.h"
+#include "base/scoped_ptr.h"
+#include "base/string_util.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+using file_util::Delete;
+using file_util::WriteFile;
+using file_util::ReadFileToString;
+using file_util::GetCurrentDirectory;
+
+#if defined(OS_LINUX)
+// ZygoteManager is only used on Linux at the moment
+
+typedef testing::Test ZygoteManagerTest;
+
+TEST_F(ZygoteManagerTest, Ping) {
+ base::ZygoteManager zm;
+
+ scoped_ptr< std::vector<std::string> > new_argv;
+ new_argv.reset(zm.Start());
+ EXPECT_TRUE(new_argv.get() == NULL);
+ // Measure round trip time
+ base::TimeDelta delta;
+ EXPECT_EQ(zm.Ping(&delta), true);
+ EXPECT_LT(delta.InMilliseconds(), 5000);
+}
+
+TEST_F(ZygoteManagerTest, SpawnChild) {
+ base::ZygoteManager zm;
+ const int kDummyChildExitCode = 39;
+
+ scoped_ptr< std::vector<std::string> > new_argv;
+ new_argv.reset(zm.Start());
+ if (new_argv.get() == NULL) {
+ // original process
+ // Launch a child process
+ std::vector<std::string> myargs;
+ myargs.push_back(std::string("0thArg"));
+ myargs.push_back(std::string("1stArg"));
+ base::file_handle_mapping_vector no_files;
+ pid_t child = zm.LongFork(myargs, no_files);
+ EXPECT_NE(child, -1);
+ EXPECT_NE(child, 0);
+ LOG(INFO) << "child pid " << child;
+
+ // ZygoteManager doesn't support waiting for exit status
+ int status;
+ int err = HANDLE_EINTR(waitpid(child, &status, 0));
+ EXPECT_EQ(-1, err);
+ EXPECT_EQ(ECHILD, errno);
+ } else {
+ LOG(INFO) << "Hello from child!";
+ // child process
+ std::string arg0(new_argv.get()->at(0));
+ std::string arg1(new_argv.get()->at(1));
+ EXPECT_EQ(arg0, std::string("0thArg"));
+ EXPECT_EQ(arg1, std::string("1stArg"));
+ exit(kDummyChildExitCode);
+ }
+}
+
+TEST_F(ZygoteManagerTest, MapFile) {
+ base::ZygoteManager zm;
+ const int kDummyChildExitCode = 39;
+ const int kSpecialFDSlot = 5;
+
+ scoped_ptr< std::vector<std::string> > new_argv;
+ new_argv.reset(zm.Start());
+ if (new_argv.get() == NULL) {
+ // original process
+ // Launch a child process
+ std::vector<std::string> myargs;
+ myargs.push_back(std::string("0thArg"));
+ myargs.push_back(std::string("1stArg"));
+ base::file_handle_mapping_vector fds_to_map;
+ int fd = open("/tmp/zygote_manager_unittest.tmp", O_CREAT|O_RDWR|O_TRUNC, 0644);
+ fds_to_map.push_back(std::pair<int, int>(fd, kSpecialFDSlot));
+ pid_t child = zm.LongFork(myargs, fds_to_map);
+ EXPECT_NE(child, -1);
+ EXPECT_NE(child, 0);
+ // FIXME: really wait for child
+ sleep(3);
+ char buf[100];
+
+ // Expect fd to be seeked to end, so reading without seeking should fail
+ off_t loc = lseek(fd, 0L, SEEK_CUR);
+ EXPECT_EQ(3, loc);
+
+ memset(buf, 0, sizeof(buf));
+ int nread = read(fd, buf, 5);
+ EXPECT_EQ(0, nread);
+
+ // Try again from beginning
+ lseek(fd, 0L, SEEK_SET);
+ memset(buf, 0, sizeof(buf));
+ nread = read(fd, buf, 2);
+ EXPECT_EQ(2, nread);
+ EXPECT_EQ(strncmp(buf, "hi", 2), 0);
+ close(fd);
+ } else {
+ // child process
+ // Write three bytes; this happens to seek the file descriptor to the end.
+ int nwritten = write(kSpecialFDSlot, "hi\n", 3);
+ EXPECT_EQ(3, nwritten);
+ exit(kDummyChildExitCode);
+ }
+}
+
+TEST_F(ZygoteManagerTest, OpenFile) {
+ base::ZygoteManager zm;
+
+ scoped_ptr< std::vector<std::string> > new_argv;
+ new_argv.reset(zm.Start());
+ EXPECT_EQ(NULL, new_argv.get());
+
+ const char kSomeText[] = "foobar\n";
+
+ // Verify that we disallow nonabsolute paths.
+ FilePath badfilepath(FilePath::kCurrentDirectory);
+ badfilepath = badfilepath.AppendASCII("zygote_manager_test.pak");
+ ASSERT_FALSE(badfilepath.IsAbsolute());
+ EXPECT_NE(-1, WriteFile(badfilepath, kSomeText, strlen(kSomeText)));
+ std::string badfilename = WideToASCII(badfilepath.ToWStringHack());
+ int fd = zm.OpenFile(badfilename);
+ EXPECT_EQ(-1, fd);
+ EXPECT_TRUE(Delete(badfilepath, false));
+
+ // Verify that we disallow non-plain files.
+ ASSERT_TRUE(GetCurrentDirectory(&badfilepath));
+ badfilepath = badfilepath.AppendASCII("zygote_manager_test.pak");
+ std::string badfilenameA = WideToASCII(badfilepath.ToWStringHack());
+ EXPECT_EQ(0, mkfifo(badfilenameA.c_str(), 0644));
+ badfilename = WideToASCII(badfilepath.ToWStringHack());
+ fd = zm.OpenFile(badfilename);
+ ASSERT_EQ(-1, fd);
+ EXPECT_TRUE(Delete(badfilepath, false));
+
+ // Verify that we disallow files not ending in .pak.
+ ASSERT_TRUE(GetCurrentDirectory(&badfilepath));
+ badfilepath = badfilepath.AppendASCII("zygote_manager_test.tmp");
+ ASSERT_NE(-1, WriteFile(badfilepath, kSomeText, strlen(kSomeText)));
+ badfilename = WideToASCII(badfilepath.ToWStringHack());
+ fd = zm.OpenFile(badfilename);
+ ASSERT_EQ(-1, fd);
+ EXPECT_TRUE(Delete(badfilepath, false));
+
+ // Verify that we disallow files in /etc
+ badfilepath = FilePath(FILE_PATH_LITERAL("/"));
+ badfilepath = badfilepath.AppendASCII("etc");
+ badfilepath = badfilepath.AppendASCII("hosts");
+ EXPECT_TRUE(badfilepath.IsAbsolute());
+ badfilename = WideToASCII(badfilepath.ToWStringHack());
+ fd = zm.OpenFile(badfilename);
+ ASSERT_EQ(-1, fd);
+
+ // Verify that we disallow files in /dev
+ badfilepath = FilePath(FILE_PATH_LITERAL("/"));
+ badfilepath = badfilepath.AppendASCII("dev");
+ badfilepath = badfilepath.AppendASCII("tty");
+ EXPECT_TRUE(badfilepath.IsAbsolute());
+ badfilename = WideToASCII(badfilepath.ToWStringHack());
+ fd = zm.OpenFile(badfilename);
+ ASSERT_EQ(-1, fd);
+
+ // Verify that we allow absolute paths with filename ending in .pak,
+ // and that we can open them a second time even if they were
+ // deleted after we opened them the first time.
+ // Because of our restrictive filename checks, can't put
+ // test file in /tmp, so put it in current directory.
+ FilePath goodfilepath;
+ ASSERT_TRUE(GetCurrentDirectory(&goodfilepath));
+ goodfilepath = goodfilepath.AppendASCII("zygote_manager_test.pak");
+ ASSERT_NE(-1, WriteFile(goodfilepath, kSomeText, strlen(kSomeText)));
+ std::string goodfilename = WideToASCII(goodfilepath.ToWStringHack());
+ for (int i = 0; i < 2; i++) {
+ fd = zm.OpenFile(goodfilename);
+ ASSERT_NE(-1, fd);
+ char buf[sizeof(kSomeText)];
+ // Can't use read because it depends on file position.
+ // (In practice these files are mmapped.)
+ int nread = pread(fd, buf, strlen(kSomeText), 0);
+ ASSERT_EQ(strlen(kSomeText), static_cast<size_t>(nread));
+ EXPECT_EQ(0, strncmp(buf, kSomeText, strlen(kSomeText)));
+ EXPECT_EQ(0, close(fd));
+
+ // oddly, our Delete returns true for nonexistant files.
+ EXPECT_EQ(true, Delete(goodfilepath, false));
+ }
+}
+
+TEST_F(ZygoteManagerTest, ChildOpenFile) {
+ base::ZygoteManager zm;
+ const int kDummyChildExitCode = 39;
+
+ const char kSomeText[] = "foobar\n";
+
+ FilePath resultfilepath(FILE_PATH_LITERAL("/tmp"));
+ resultfilepath = resultfilepath.AppendASCII("zygote_manager_test_result.tmp");
+ EXPECT_EQ(true, Delete(resultfilepath, false));
+
+ scoped_ptr< std::vector<std::string> > new_argv;
+ new_argv.reset(zm.Start());
+ if (new_argv.get() == NULL) {
+ // original process
+ // Launch a child process
+ std::vector<std::string> myargs;
+ base::file_handle_mapping_vector no_files;
+ pid_t child = zm.LongFork(myargs, no_files);
+ EXPECT_NE(child, -1);
+ EXPECT_NE(child, 0);
+ LOG(INFO) << "child pid " << child;
+
+ // Wait for resultfile to be created
+ std::string result;
+ int nloops = 0;
+ while (!ReadFileToString(resultfilepath, &result)) {
+ sleep(1);
+ ++nloops;
+ ASSERT_NE(10, nloops);
+ }
+ ASSERT_EQ(result, std::string(kSomeText));
+
+ EXPECT_EQ(true, Delete(resultfilepath, false));
+
+ } else {
+ LOG(INFO) << "Hello from child!";
+
+ FilePath goodfilepath;
+ ASSERT_TRUE(GetCurrentDirectory(&goodfilepath));
+ goodfilepath = goodfilepath.AppendASCII("zygote_manager_test.pak");
+ ASSERT_NE(-1, WriteFile(goodfilepath, kSomeText, strlen(kSomeText)));
+ std::string goodfilename = WideToASCII(goodfilepath.ToWStringHack());
+ int fd = zm.OpenFile(goodfilename);
+ EXPECT_EQ(true, Delete(goodfilepath, false));
+ ASSERT_NE(-1, fd);
+ char buf[sizeof(kSomeText)];
+ // Can't use read because it depends on file position.
+ // (In practice these files are mmapped.)
+ int nread = pread(fd, buf, strlen(kSomeText), 0);
+ ASSERT_EQ(strlen(kSomeText), static_cast<size_t>(nread));
+ EXPECT_EQ(0, strncmp(buf, kSomeText, strlen(kSomeText)));
+ EXPECT_EQ(0, close(fd));
+
+ ASSERT_NE(-1, WriteFile(resultfilepath, kSomeText, strlen(kSomeText)));
+
+ exit(kDummyChildExitCode);
+ }
+}
+
+#endif