| Index: services/native_support/process_impl_unittest.cc
|
| diff --git a/services/native_support/process_impl_unittest.cc b/services/native_support/process_impl_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..348a1d84d22ea1265a3d929f7687f90fab80f0ab
|
| --- /dev/null
|
| +++ b/services/native_support/process_impl_unittest.cc
|
| @@ -0,0 +1,142 @@
|
| +// 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 <string.h>
|
| +
|
| +#include <memory>
|
| +#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/public/cpp/system/macros.h"
|
| +#include "mojo/services/files/public/cpp/output_stream_file.h"
|
| +#include "mojo/services/files/public/interfaces/file.mojom.h"
|
| +#include "mojo/services/files/public/interfaces/types.mojom.h"
|
| +#include "services/native_support/process_test_base.h"
|
| +
|
| +namespace native_support {
|
| +namespace {
|
| +
|
| +using ProcessImplTest = ProcessTestBase;
|
| +
|
| +// This also (slightly) tests |Wait()|, since we want to have some evidence that
|
| +// we ran the specified binary (/bin/true versus /bin/false).
|
| +TEST_F(ProcessImplTest, Spawn) {
|
| + mojo::files::Error error;
|
| +
|
| + {
|
| + ProcessControllerPtr process_controller;
|
| + error = mojo::files::ERROR_INTERNAL;
|
| + process()->Spawn("/bin/true", mojo::Array<mojo::String>(),
|
| + 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 = 42;
|
| + process_controller->Wait(Capture(&error, &exit_status));
|
| + ASSERT_TRUE(process_controller.WaitForIncomingResponse());
|
| + EXPECT_EQ(mojo::files::ERROR_OK, error);
|
| + EXPECT_EQ(0, exit_status);
|
| + }
|
| +
|
| + {
|
| + ProcessControllerPtr process_controller;
|
| + error = mojo::files::ERROR_INTERNAL;
|
| + process()->Spawn("/bin/false", mojo::Array<mojo::String>(),
|
| + 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_NE(exit_status, 0);
|
| + }
|
| +}
|
| +
|
| +void QuitMessageLoop() {
|
| + base::MessageLoop::current()->QuitWhenIdle();
|
| +}
|
| +
|
| +void RunMessageLoop() {
|
| + base::MessageLoop::current()->Run();
|
| +}
|
| +
|
| +class CaptureOutputFile : public files_impl::OutputStreamFile::Client {
|
| + public:
|
| + explicit CaptureOutputFile(mojo::InterfaceRequest<mojo::files::File> request)
|
| + : impl_(files_impl::OutputStreamFile::Create(this, request.Pass())) {}
|
| + ~CaptureOutputFile() override {}
|
| +
|
| + const std::string& output() const { return output_; }
|
| + bool is_closed() const { return is_closed_; }
|
| +
|
| + private:
|
| + // |files_impl::OutputStreamFile::Client|:
|
| + void OnDataReceived(const void* bytes, size_t num_bytes) override {
|
| + output_.append(static_cast<const char*>(bytes), num_bytes);
|
| + QuitMessageLoop();
|
| + }
|
| + void OnClosed() override {
|
| + is_closed_ = true;
|
| + QuitMessageLoop();
|
| + }
|
| +
|
| + std::string output_;
|
| + bool is_closed_ = false;
|
| + std::unique_ptr<files_impl::OutputStreamFile> impl_;
|
| +
|
| + MOJO_DISALLOW_COPY_AND_ASSIGN(CaptureOutputFile);
|
| +};
|
| +
|
| +// Spawn a native binary and redirect its stdout to a Mojo "file" that captures
|
| +// it.
|
| +TEST_F(ProcessImplTest, SpawnRedirectStdout) {
|
| + static const char kOutput[] = "hello mojo!";
|
| +
|
| + mojo::files::FilePtr file;
|
| + CaptureOutputFile file_impl(GetProxy(&file));
|
| +
|
| + mojo::Array<mojo::String> argv;
|
| + argv.push_back("/bin/echo");
|
| + argv.push_back(kOutput);
|
| + ProcessControllerPtr process_controller;
|
| + mojo::files::Error error = mojo::files::ERROR_INTERNAL;
|
| + process()->Spawn("/bin/echo", argv.Pass(), mojo::Array<mojo::String>(),
|
| + nullptr, file.Pass(), nullptr, GetProxy(&process_controller),
|
| + Capture(&error));
|
| + ASSERT_TRUE(process().WaitForIncomingResponse());
|
| + EXPECT_EQ(mojo::files::ERROR_OK, error);
|
| +
|
| + // Since |file|'s impl is on our thread, we have to spin our message loop.
|
| + RunMessageLoop();
|
| + // /bin/echo adds a newline (alas, POSIX /bin/echo doesn't specify "-n").
|
| + EXPECT_EQ(std::string(kOutput) + "\n", file_impl.output());
|
| +
|
| + 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(0, exit_status);
|
| +
|
| + // TODO(vtl): Currently, |file| won't be closed until the process controller
|
| + // is closed, even if the child has been waited on and all output read.
|
| + // Possibly this should be changed, but we currently can't since the I/O
|
| + // thread doesn't have facilities for informing us that an FD will never be
|
| + // readable.
|
| + EXPECT_FALSE(file_impl.is_closed());
|
| + process_controller.reset();
|
| + RunMessageLoop();
|
| + EXPECT_TRUE(file_impl.is_closed());
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace native_support
|
|
|