blob: 9efa68f230113c05d1b33bcccbf276c9e6b1ccb7 [file] [log] [blame]
// 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/cpp/output_stream_file.h"
#include "mojo/services/files/interfaces/file.mojom.h"
#include "mojo/services/files/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