OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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. |
| 4 |
| 5 #include <string.h> |
| 6 |
| 7 #include <memory> |
| 8 #include <vector> |
| 9 |
| 10 #include "base/message_loop/message_loop.h" |
| 11 #include "mojo/public/cpp/bindings/interface_request.h" |
| 12 #include "mojo/public/cpp/bindings/type_converter.h" |
| 13 #include "mojo/public/cpp/system/macros.h" |
| 14 #include "mojo/services/files/public/cpp/output_stream_file.h" |
| 15 #include "mojo/services/files/public/interfaces/file.mojom.h" |
| 16 #include "mojo/services/files/public/interfaces/types.mojom.h" |
| 17 #include "services/native_support/process_test_base.h" |
| 18 |
| 19 namespace native_support { |
| 20 namespace { |
| 21 |
| 22 using ProcessImplTest = ProcessTestBase; |
| 23 |
| 24 // This also (slightly) tests |Wait()|, since we want to have some evidence that |
| 25 // we ran the specified binary (/bin/true versus /bin/false). |
| 26 TEST_F(ProcessImplTest, Spawn) { |
| 27 mojo::files::Error error; |
| 28 |
| 29 { |
| 30 ProcessControllerPtr process_controller; |
| 31 error = mojo::files::ERROR_INTERNAL; |
| 32 process()->Spawn("/bin/true", mojo::Array<mojo::String>(), |
| 33 mojo::Array<mojo::String>(), nullptr, nullptr, nullptr, |
| 34 GetProxy(&process_controller), Capture(&error)); |
| 35 ASSERT_TRUE(process().WaitForIncomingResponse()); |
| 36 EXPECT_EQ(mojo::files::ERROR_OK, error); |
| 37 |
| 38 error = mojo::files::ERROR_INTERNAL; |
| 39 int32_t exit_status = 42; |
| 40 process_controller->Wait(Capture(&error, &exit_status)); |
| 41 ASSERT_TRUE(process_controller.WaitForIncomingResponse()); |
| 42 EXPECT_EQ(mojo::files::ERROR_OK, error); |
| 43 EXPECT_EQ(0, exit_status); |
| 44 } |
| 45 |
| 46 { |
| 47 ProcessControllerPtr process_controller; |
| 48 error = mojo::files::ERROR_INTERNAL; |
| 49 process()->Spawn("/bin/false", mojo::Array<mojo::String>(), |
| 50 mojo::Array<mojo::String>(), nullptr, nullptr, nullptr, |
| 51 GetProxy(&process_controller), Capture(&error)); |
| 52 ASSERT_TRUE(process().WaitForIncomingResponse()); |
| 53 EXPECT_EQ(mojo::files::ERROR_OK, error); |
| 54 |
| 55 error = mojo::files::ERROR_INTERNAL; |
| 56 int32_t exit_status = 0; |
| 57 process_controller->Wait(Capture(&error, &exit_status)); |
| 58 ASSERT_TRUE(process_controller.WaitForIncomingResponse()); |
| 59 EXPECT_EQ(mojo::files::ERROR_OK, error); |
| 60 EXPECT_NE(exit_status, 0); |
| 61 } |
| 62 } |
| 63 |
| 64 void QuitMessageLoop() { |
| 65 base::MessageLoop::current()->QuitWhenIdle(); |
| 66 } |
| 67 |
| 68 void RunMessageLoop() { |
| 69 base::MessageLoop::current()->Run(); |
| 70 } |
| 71 |
| 72 class CaptureOutputFile : public files_impl::OutputStreamFile::Client { |
| 73 public: |
| 74 explicit CaptureOutputFile(mojo::InterfaceRequest<mojo::files::File> request) |
| 75 : impl_(files_impl::OutputStreamFile::Create(this, request.Pass())) {} |
| 76 ~CaptureOutputFile() override {} |
| 77 |
| 78 const std::string& output() const { return output_; } |
| 79 bool is_closed() const { return is_closed_; } |
| 80 |
| 81 private: |
| 82 // |files_impl::OutputStreamFile::Client|: |
| 83 void OnDataReceived(const void* bytes, size_t num_bytes) override { |
| 84 output_.append(static_cast<const char*>(bytes), num_bytes); |
| 85 QuitMessageLoop(); |
| 86 } |
| 87 void OnClosed() override { |
| 88 is_closed_ = true; |
| 89 QuitMessageLoop(); |
| 90 } |
| 91 |
| 92 std::string output_; |
| 93 bool is_closed_ = false; |
| 94 std::unique_ptr<files_impl::OutputStreamFile> impl_; |
| 95 |
| 96 MOJO_DISALLOW_COPY_AND_ASSIGN(CaptureOutputFile); |
| 97 }; |
| 98 |
| 99 // Spawn a native binary and redirect its stdout to a Mojo "file" that captures |
| 100 // it. |
| 101 TEST_F(ProcessImplTest, SpawnRedirectStdout) { |
| 102 static const char kOutput[] = "hello mojo!"; |
| 103 |
| 104 mojo::files::FilePtr file; |
| 105 CaptureOutputFile file_impl(GetProxy(&file)); |
| 106 |
| 107 mojo::Array<mojo::String> argv; |
| 108 argv.push_back("/bin/echo"); |
| 109 argv.push_back(kOutput); |
| 110 ProcessControllerPtr process_controller; |
| 111 mojo::files::Error error = mojo::files::ERROR_INTERNAL; |
| 112 process()->Spawn("/bin/echo", argv.Pass(), mojo::Array<mojo::String>(), |
| 113 nullptr, file.Pass(), nullptr, GetProxy(&process_controller), |
| 114 Capture(&error)); |
| 115 ASSERT_TRUE(process().WaitForIncomingResponse()); |
| 116 EXPECT_EQ(mojo::files::ERROR_OK, error); |
| 117 |
| 118 // Since |file|'s impl is on our thread, we have to spin our message loop. |
| 119 RunMessageLoop(); |
| 120 // /bin/echo adds a newline (alas, POSIX /bin/echo doesn't specify "-n"). |
| 121 EXPECT_EQ(std::string(kOutput) + "\n", file_impl.output()); |
| 122 |
| 123 error = mojo::files::ERROR_INTERNAL; |
| 124 int32_t exit_status = 0; |
| 125 process_controller->Wait(Capture(&error, &exit_status)); |
| 126 ASSERT_TRUE(process_controller.WaitForIncomingResponse()); |
| 127 EXPECT_EQ(mojo::files::ERROR_OK, error); |
| 128 EXPECT_EQ(0, exit_status); |
| 129 |
| 130 // TODO(vtl): Currently, |file| won't be closed until the process controller |
| 131 // is closed, even if the child has been waited on and all output read. |
| 132 // Possibly this should be changed, but we currently can't since the I/O |
| 133 // thread doesn't have facilities for informing us that an FD will never be |
| 134 // readable. |
| 135 EXPECT_FALSE(file_impl.is_closed()); |
| 136 process_controller.reset(); |
| 137 RunMessageLoop(); |
| 138 EXPECT_TRUE(file_impl.is_closed()); |
| 139 } |
| 140 |
| 141 } // namespace |
| 142 } // namespace native_support |
OLD | NEW |