Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(749)

Unified Diff: services/native_support/process_controller_impl_unittest.cc

Issue 1321253010: Add a "native_support" service. (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: fix android? Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « services/native_support/process_controller_impl.cc ('k') | services/native_support/process_impl.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: services/native_support/process_controller_impl_unittest.cc
diff --git a/services/native_support/process_controller_impl_unittest.cc b/services/native_support/process_controller_impl_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..123c266d9a21566165a6388fc5e213e6c4b31703
--- /dev/null
+++ b/services/native_support/process_controller_impl_unittest.cc
@@ -0,0 +1,196 @@
+// Copyright 2015 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 <signal.h>
+
+#include <string>
+#include <vector>
+
+#include "base/message_loop/message_loop.h"
+#include "mojo/public/cpp/bindings/interface_request.h"
+#include "mojo/public/cpp/bindings/type_converter.h"
+#include "mojo/services/files/public/cpp/input_stream_file.h"
+#include "mojo/services/files/public/cpp/output_stream_file.h"
+#include "mojo/services/files/public/interfaces/types.mojom.h"
+#include "services/native_support/process_test_base.h"
+
+namespace native_support {
+namespace {
+
+using ProcessControllerImplTest = ProcessTestBase;
+
+void QuitMessageLoop() {
+ base::MessageLoop::current()->QuitWhenIdle();
+}
+
+void RunMessageLoop() {
+ base::MessageLoop::current()->Run();
+}
+
+// Note: We already tested success (zero) versus failure (non-zero) exit
+// statuses in |ProcessControllerTest.Spawn|.
+TEST_F(ProcessControllerImplTest, Wait) {
+ mojo::files::Error error;
+
+ {
+ ProcessControllerPtr process_controller;
+ error = mojo::files::ERROR_INTERNAL;
+ const char kPath[] = "/bin/sh";
+ mojo::Array<mojo::String> argv;
+ argv.push_back(kPath);
+ argv.push_back("-c");
+ argv.push_back("exit 42");
+ process()->Spawn(kPath, argv.Pass(), mojo::Array<mojo::String>(), nullptr,
+ nullptr, nullptr, GetProxy(&process_controller),
+ Capture(&error));
+ ASSERT_TRUE(process().WaitForIncomingResponse());
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+
+ error = mojo::files::ERROR_INTERNAL;
+ int32_t exit_status = 0;
+ process_controller->Wait(Capture(&error, &exit_status));
+ ASSERT_TRUE(process_controller.WaitForIncomingResponse());
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+ EXPECT_EQ(42, exit_status);
+ }
+}
+
+// An output file stream that captures output and quits when "ready" is seen.
+class QuitOnReadyFile : public files_impl::OutputStreamFile::Client {
+ public:
+ explicit QuitOnReadyFile(mojo::InterfaceRequest<mojo::files::File> request)
+ : impl_(files_impl::OutputStreamFile::Create(this, request.Pass())) {}
+ ~QuitOnReadyFile() override {}
+
+ bool got_ready() const { return got_ready_; }
+
+ private:
+ // |files_impl::OutputStreamFile::Client|:
+ void OnDataReceived(const void* bytes, size_t num_bytes) override {
+ output_.append(static_cast<const char*>(bytes), num_bytes);
+ if (output_.find("ready") != std::string::npos) {
+ got_ready_ = true;
+ QuitMessageLoop();
+ }
+ }
+ void OnClosed() override { QuitMessageLoop(); }
+
+ std::string output_;
+ bool got_ready_ = false;
+ std::unique_ptr<files_impl::OutputStreamFile> impl_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(QuitOnReadyFile);
+};
+
+// An input file stream that ignores reads (never completing them).
+class IgnoreReadsFile : public files_impl::InputStreamFile::Client {
+ public:
+ explicit IgnoreReadsFile(mojo::InterfaceRequest<mojo::files::File> request)
+ : impl_(files_impl::InputStreamFile::Create(this, request.Pass())) {}
+ ~IgnoreReadsFile() override {}
+
+ private:
+ // |files_impl::InputStreamFile::Client|:
+ bool RequestData(size_t max_num_bytes,
+ mojo::files::Error* error,
+ mojo::Array<uint8_t>* data,
+ const RequestDataCallback& callback) override {
+ // Don't let |callback| die, because we probably have assertions "ensuring"
+ // that response callbacks get called.
+ callbacks_.push_back(callback);
+ return false;
+ }
+ void OnClosed() override { QuitMessageLoop(); }
+
+ std::vector<RequestDataCallback> callbacks_;
+ std::unique_ptr<files_impl::InputStreamFile> impl_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(IgnoreReadsFile);
+};
+
+TEST_F(ProcessControllerImplTest, Kill) {
+ // We want to run bash and have it set a trap for SIGINT, which it'll handle
+ // by quitting with code 42. We need to make sure that bash started and set
+ // the trap before we kill our child. Thus we have bash echo "ready" and wait
+ // for it to do so (and thus we need to capture/examine stdout).
+ //
+ // We want bash to pause, so that we can kill it before it quits. We can't use
+ // "sleep" since it's not a builtin (so bash would wait for it before dying).
+ // So instead we use "read" with a timeout (note that "-t" is a bash
+ // extension, and not specified by POSIX for /bin/sh). Thus we have to give
+ // something for stdin (otherwise, it'd come from /dev/null and the read would
+ // be completed immediately).
+ mojo::files::FilePtr ifile;
+ IgnoreReadsFile ifile_impl(GetProxy(&ifile));
+ mojo::files::FilePtr ofile;
+ QuitOnReadyFile ofile_impl(GetProxy(&ofile));
+
+ ProcessControllerPtr process_controller;
+ mojo::files::Error error = mojo::files::ERROR_INTERNAL;
+ const char kPath[] = "/bin/bash";
+ mojo::Array<mojo::String> argv;
+ argv.push_back(kPath);
+ argv.push_back("-c");
+ argv.push_back("trap 'exit 42' INT; echo ready; read -t30; exit 1");
+ process()->Spawn(kPath, argv.Pass(), mojo::Array<mojo::String>(),
+ ifile.Pass(), ofile.Pass(), nullptr,
+ GetProxy(&process_controller), Capture(&error));
+ ASSERT_TRUE(process().WaitForIncomingResponse());
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+
+ // |ofile_impl| will quit the message loop once it sees "ready".
+ RunMessageLoop();
+ ASSERT_TRUE(ofile_impl.got_ready());
+
+ // Send SIGINT.
+ error = mojo::files::ERROR_INTERNAL;
+ process_controller->Kill(static_cast<int32_t>(SIGINT), Capture(&error));
+ ASSERT_TRUE(process_controller.WaitForIncomingResponse());
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+
+ error = mojo::files::ERROR_INTERNAL;
+ int32_t exit_status = 0;
+ process_controller->Wait(Capture(&error, &exit_status));
+ ASSERT_TRUE(process_controller.WaitForIncomingResponse());
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+ EXPECT_EQ(42, exit_status);
+}
+
+TEST_F(ProcessControllerImplTest, DestroyingControllerKills) {
+ // We want to make sure that we've exec-ed before killing, so we do what we do
+ // in |ProcessControllerImplTest.Kill| (without the trap).
+ {
+ mojo::files::FilePtr ifile;
+ IgnoreReadsFile ifile_impl(GetProxy(&ifile));
+ mojo::files::FilePtr ofile;
+ QuitOnReadyFile ofile_impl(GetProxy(&ofile));
+
+ ProcessControllerPtr process_controller;
+ mojo::files::Error error = mojo::files::ERROR_INTERNAL;
+ const char kPath[] = "/bin/bash";
+ mojo::Array<mojo::String> argv;
+ argv.push_back(kPath);
+ argv.push_back("-c");
+ argv.push_back("echo ready; read -t30");
+ process()->Spawn(kPath, argv.Pass(), mojo::Array<mojo::String>(),
+ ifile.Pass(), ofile.Pass(), nullptr,
+ GetProxy(&process_controller), Capture(&error));
+ ASSERT_TRUE(process().WaitForIncomingResponse());
+ EXPECT_EQ(mojo::files::ERROR_OK, error);
+
+ // |ofile_impl| will quit the message loop once it sees "ready".
+ RunMessageLoop();
+ ASSERT_TRUE(ofile_impl.got_ready());
+ }
+
+ // The child should be killed.
+ // TODO(vtl): It's pretty hard to verify that the child process was actually
+ // killed. This could be done, e.g., by having the child trap SIGTERM and
+ // writing something to a file, and then separately checking for that file.
+ // For now now, just be happy if it doesn't crash. (I've actually verified it
+ // "manually", but automation is hard.)
+}
+
+} // namespace
+} // namespace native_support
« no previous file with comments | « services/native_support/process_controller_impl.cc ('k') | services/native_support/process_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698