aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-10-16 10:09:55 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2020-10-16 10:12:47 +0300
commita632bb8e796be52929f1541d305910d704e55076 (patch)
tree6a03b50fcb16ef1713f6cbbe5f2377b2fdc10990
parentecho: make it really UTF-16 (diff)
downloadwinapi-common-a632bb8e796be52929f1541d305910d704e55076.tar.gz
winapi-common-a632bb8e796be52929f1541d305910d704e55076.zip
Process: support pipe redirection
-rw-r--r--.appveyor.yml2
-rw-r--r--include/winapi/file.hpp28
-rw-r--r--include/winapi/handle.hpp51
-rw-r--r--include/winapi/pipe.hpp26
-rw-r--r--include/winapi/process.hpp12
-rw-r--r--include/winapi/stream.hpp42
-rw-r--r--src/file.cpp75
-rw-r--r--src/handle.cpp153
-rw-r--r--src/pipe.cpp46
-rw-r--r--src/process.cpp44
-rw-r--r--src/stream.cpp41
-rw-r--r--test/unit_tests/CMakeLists.txt2
-rw-r--r--test/unit_tests/process.cpp51
13 files changed, 522 insertions, 51 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index da5c40a..2624062 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -45,7 +45,7 @@ after_build:
- appveyor.exe PushArtifact "%APPVEYOR_PROJECT_NAME%-%PLATFORM%-%CONFIGURATION%.zip"
test_script:
- - '"%install_dir%\bin\winapi-common-unit-tests.exe" -- "--test_exe=%install_dir%\bin\winapi-common-test-echo.exe"'
+ - '"%install_dir%\bin\winapi-common-unit-tests.exe" -- "--echo_exe=%install_dir%\bin\winapi-common-test-echo.exe"'
for:
# Build Release on master only to speed things up:
diff --git a/include/winapi/file.hpp b/include/winapi/file.hpp
new file mode 100644
index 0000000..60a8f1d
--- /dev/null
+++ b/include/winapi/file.hpp
@@ -0,0 +1,28 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include "handle.hpp"
+
+#include <string>
+#include <utility>
+
+namespace winapi {
+
+class File : private Handle {
+public:
+ explicit File(Handle&& handle) : Handle{std::move(handle)} {}
+
+ static Handle open_for_reading(const std::string&);
+ static Handle open_for_writing(const std::string&);
+
+ using Handle::close;
+
+ using Handle::read;
+ using Handle::write;
+};
+
+} // namespace winapi
diff --git a/include/winapi/handle.hpp b/include/winapi/handle.hpp
index 9714db3..af37424 100644
--- a/include/winapi/handle.hpp
+++ b/include/winapi/handle.hpp
@@ -5,50 +5,57 @@
#pragma once
-#include "workarounds.hpp"
-
#include <boost/config.hpp>
#include <windows.h>
-#include <cassert>
+#include <cstddef>
#include <memory>
#include <utility>
+#include <vector>
namespace winapi {
class Handle {
public:
Handle() = default;
+ explicit Handle(HANDLE);
+
+ Handle(Handle&& other) BOOST_NOEXCEPT_OR_NOTHROW;
+ Handle& operator=(Handle other) BOOST_NOEXCEPT_OR_NOTHROW;
+
+ void swap(Handle& other) BOOST_NOEXCEPT_OR_NOTHROW;
+
+ explicit operator HANDLE() const { return m_impl.get(); }
+
+ bool is_invalid() const;
+
+ void close();
+
+ bool is_std() const;
+ static Handle std_in();
+ static Handle std_out();
+ static Handle std_err();
- explicit Handle(HANDLE raw) : impl{raw} {}
+ typedef std::vector<unsigned char> Buffer;
- Handle(Handle&& other) BOOST_NOEXCEPT_OR_NOTHROW { swap(other); }
+ Buffer read() const;
- Handle& operator=(Handle other) BOOST_NOEXCEPT_OR_NOTHROW {
- swap(other);
- return *this;
- }
+ BOOST_STATIC_CONSTEXPR std::size_t max_chunk_size = 16 * 1024;
+ bool read_chunk(Buffer& read_chunk) const;
- void swap(Handle& other) BOOST_NOEXCEPT_OR_NOTHROW {
- using std::swap;
- swap(impl, other.impl);
- }
+ void write(const void*, std::size_t nb) const;
+ void write(const Buffer& buffer) const;
- explicit operator HANDLE() const { return impl.get(); }
+ void inherit(bool yes = true) const;
+ void dont_inherit() const { inherit(false); }
private:
struct Close {
- void operator()(HANDLE raw) const {
- if (raw == NULL || raw == INVALID_HANDLE_VALUE)
- return;
- const auto ret = ::CloseHandle(raw);
- assert(ret);
- WINAPI_UNUSED_PARAMETER(ret);
- }
+ void operator()(HANDLE) const;
};
- std::unique_ptr<void, Close> impl;
+ std::unique_ptr<void, Close> m_impl;
Handle(const Handle&) = delete;
};
diff --git a/include/winapi/pipe.hpp b/include/winapi/pipe.hpp
new file mode 100644
index 0000000..ebd99e8
--- /dev/null
+++ b/include/winapi/pipe.hpp
@@ -0,0 +1,26 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include "handle.hpp"
+
+namespace winapi {
+
+class Pipe {
+public:
+ Pipe();
+
+ Handle& read_end() { return m_read_end; }
+ const Handle& read_end() const { return m_read_end; }
+ Handle& write_end() { return m_write_end; }
+ const Handle& write_end() const { return m_write_end; }
+
+private:
+ Handle m_read_end;
+ Handle m_write_end;
+};
+
+} // namespace winapi
diff --git a/include/winapi/process.hpp b/include/winapi/process.hpp
index d20ac38..22317ca 100644
--- a/include/winapi/process.hpp
+++ b/include/winapi/process.hpp
@@ -3,8 +3,11 @@
// For details, see https://github.com/egor-tensin/winapi-common.
// Distributed under the MIT License.
+#pragma once
+
#include "cmd_line.hpp"
#include "handle.hpp"
+#include "stream.hpp"
#include <utility>
@@ -12,7 +15,16 @@ namespace winapi {
class Process {
public:
+ struct IO {
+ process::Stdin std_in;
+ process::Stdout std_out;
+ process::Stderr std_err;
+
+ void close();
+ };
+
static Process create(const CommandLine&);
+ static Process create(const CommandLine&, IO);
void wait();
diff --git a/include/winapi/stream.hpp b/include/winapi/stream.hpp
new file mode 100644
index 0000000..cf85508
--- /dev/null
+++ b/include/winapi/stream.hpp
@@ -0,0 +1,42 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#pragma once
+
+#include "handle.hpp"
+#include "pipe.hpp"
+
+#include <string>
+#include <utility>
+
+namespace winapi {
+namespace process {
+
+struct Stream {
+ Stream(Handle&& handle) : handle{std::move(handle)} {}
+
+ Handle handle;
+};
+
+struct Stdin : Stream {
+ Stdin();
+ explicit Stdin(const std::string& file);
+ explicit Stdin(Pipe&);
+};
+
+struct Stdout : Stream {
+ Stdout();
+ explicit Stdout(const std::string& file);
+ explicit Stdout(Pipe&);
+};
+
+struct Stderr : Stream {
+ Stderr();
+ explicit Stderr(const std::string& file);
+ explicit Stderr(Pipe&);
+};
+
+} // namespace process
+} // namespace winapi
diff --git a/src/file.cpp b/src/file.cpp
new file mode 100644
index 0000000..cb9be81
--- /dev/null
+++ b/src/file.cpp
@@ -0,0 +1,75 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/error.hpp>
+#include <winapi/file.hpp>
+#include <winapi/handle.hpp>
+#include <winapi/utf8.hpp>
+
+#include <cstring>
+#include <string>
+
+namespace winapi {
+namespace {
+
+struct CreateFileParams {
+ static CreateFileParams read() {
+ CreateFileParams params;
+ params.dwDesiredAccess = GENERIC_READ;
+ params.dwShareMode = FILE_SHARE_READ;
+ params.dwCreationDisposition = OPEN_EXISTING;
+ return params;
+ }
+
+ static CreateFileParams write() {
+ CreateFileParams params;
+ params.dwDesiredAccess = GENERIC_WRITE;
+ params.dwShareMode = FILE_SHARE_READ;
+ params.dwCreationDisposition = OPEN_ALWAYS;
+ return params;
+ }
+
+ DWORD dwDesiredAccess = 0;
+ DWORD dwShareMode = 0;
+ DWORD dwCreationDisposition = 0;
+
+private:
+ CreateFileParams() = default;
+};
+
+Handle open_file(const std::string& path, const CreateFileParams& params) {
+ const auto unicode_path = LR"(\\?\)" + widen(path);
+
+ SECURITY_ATTRIBUTES attributes;
+ std::memset(&attributes, 0, sizeof(attributes));
+ attributes.nLength = sizeof(attributes);
+ attributes.bInheritHandle = TRUE;
+
+ const auto handle = ::CreateFileW(unicode_path.c_str(),
+ params.dwDesiredAccess,
+ params.dwShareMode,
+ &attributes,
+ params.dwCreationDisposition,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ throw error::windows(GetLastError(), "CreateFileW");
+ }
+
+ return Handle{handle};
+}
+
+} // namespace
+
+Handle File::open_for_reading(const std::string& path) {
+ return open_file(path, CreateFileParams::read());
+}
+
+Handle File::open_for_writing(const std::string& path) {
+ return open_file(path, CreateFileParams::write());
+}
+
+} // namespace winapi
diff --git a/src/handle.cpp b/src/handle.cpp
new file mode 100644
index 0000000..a6ab3a5
--- /dev/null
+++ b/src/handle.cpp
@@ -0,0 +1,153 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/error.hpp>
+#include <winapi/handle.hpp>
+#include <winapi/workarounds.hpp>
+
+#include <boost/config.hpp>
+
+#include <windows.h>
+
+#include <cassert>
+#include <cstddef>
+#include <sstream>
+#include <stdexcept>
+
+namespace winapi {
+namespace {
+
+std::runtime_error write_file_incomplete(std::size_t expected, std::size_t actual) {
+ std::ostringstream oss;
+ oss << "WriteFile could only write " << actual << " bytes instead of " << expected;
+ return std::runtime_error{oss.str()};
+}
+
+bool is_invalid_handle(HANDLE handle) {
+ return handle == NULL || handle == INVALID_HANDLE_VALUE;
+}
+
+bool is_std_handle(HANDLE handle) {
+ return handle == ::GetStdHandle(STD_INPUT_HANDLE) ||
+ handle == ::GetStdHandle(STD_OUTPUT_HANDLE) ||
+ handle == ::GetStdHandle(STD_ERROR_HANDLE);
+}
+
+} // namespace
+
+Handle::Handle(HANDLE impl) : m_impl{impl} {}
+
+Handle::Handle(Handle&& other) BOOST_NOEXCEPT_OR_NOTHROW {
+ swap(other);
+}
+
+Handle& Handle::operator=(Handle other) BOOST_NOEXCEPT_OR_NOTHROW {
+ swap(other);
+ return *this;
+}
+
+void Handle::swap(Handle& other) BOOST_NOEXCEPT_OR_NOTHROW {
+ using std::swap;
+ swap(m_impl, other.m_impl);
+}
+
+bool Handle::is_invalid() const {
+ return !m_impl || is_invalid_handle(m_impl.get());
+}
+
+void Handle::close() {
+ m_impl.reset();
+}
+
+bool Handle::is_std() const {
+ return is_std_handle(m_impl.get());
+}
+
+Handle Handle::std_in() {
+ return Handle{::GetStdHandle(STD_INPUT_HANDLE)};
+}
+
+Handle Handle::std_out() {
+ return Handle{::GetStdHandle(STD_OUTPUT_HANDLE)};
+}
+
+Handle Handle::std_err() {
+ return Handle{::GetStdHandle(STD_ERROR_HANDLE)};
+}
+
+bool Handle::read_chunk(Buffer& buffer) const {
+ buffer.resize(max_chunk_size);
+ DWORD nb_read = 0;
+
+ const auto ret = ::ReadFile(m_impl.get(), buffer.data(), buffer.size(), &nb_read, NULL);
+
+ buffer.resize(nb_read);
+
+ if (ret) {
+ return nb_read != 0;
+ }
+
+ const auto ec = GetLastError();
+
+ switch (ec) {
+ case ERROR_BROKEN_PIPE:
+ // We've been reading from an anonymous pipe, and it's been closed.
+ return false;
+ default:
+ throw error::windows(ec, "ReadFile");
+ }
+}
+
+Handle::Buffer Handle::read() const {
+ Buffer buffer;
+ Buffer chunk;
+
+ while (true) {
+ const auto next = read_chunk(chunk);
+
+ buffer.reserve(buffer.size() + chunk.size());
+ buffer.insert(buffer.cend(), chunk.cbegin(), chunk.cend());
+
+ if (!next) {
+ break;
+ }
+ }
+
+ return buffer;
+}
+
+void Handle::write(const void* data, std::size_t nb) const {
+ DWORD nb_written = 0;
+
+ const auto ret = ::WriteFile(m_impl.get(), data, nb, &nb_written, NULL);
+
+ if (!ret) {
+ throw error::windows(GetLastError(), "WriteFile");
+ }
+
+ if (nb != nb_written) {
+ throw write_file_incomplete(nb, nb_written);
+ }
+}
+
+void Handle::write(const Buffer& buffer) const {
+ write(buffer.data(), buffer.size());
+}
+
+void Handle::inherit(bool yes) const {
+ if (!::SetHandleInformation(m_impl.get(), HANDLE_FLAG_INHERIT, yes ? 1 : 0)) {
+ throw error::windows(GetLastError(), "SetHandleInformation");
+ }
+}
+
+void Handle::Close::operator()(HANDLE impl) const {
+ if (is_invalid_handle(impl) || is_std_handle(impl))
+ return;
+ const auto ret = ::CloseHandle(impl);
+ assert(ret);
+ WINAPI_UNUSED_PARAMETER(ret);
+}
+
+} // namespace winapi
diff --git a/src/pipe.cpp b/src/pipe.cpp
new file mode 100644
index 0000000..a00a8f6
--- /dev/null
+++ b/src/pipe.cpp
@@ -0,0 +1,46 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/error.hpp>
+#include <winapi/handle.hpp>
+#include <winapi/pipe.hpp>
+
+#include <boost/config.hpp>
+
+#include <windows.h>
+
+#include <cstring>
+
+namespace winapi {
+namespace {
+
+void create_pipe(Handle& read_end, Handle& write_end) {
+ HANDLE h_read_end = INVALID_HANDLE_VALUE;
+ HANDLE h_write_end = INVALID_HANDLE_VALUE;
+
+ SECURITY_ATTRIBUTES attributes;
+ std::memset(&attributes, 0, sizeof(attributes));
+ attributes.nLength = sizeof(attributes);
+ attributes.bInheritHandle = TRUE;
+
+ BOOST_STATIC_CONSTEXPR DWORD buffer_size = 16 * 1024;
+
+ const auto ret = ::CreatePipe(&h_read_end, &h_write_end, &attributes, buffer_size);
+
+ if (!ret) {
+ throw error::windows(GetLastError(), "CreatePipe");
+ }
+
+ read_end = Handle{h_read_end};
+ write_end = Handle{h_write_end};
+}
+
+} // namespace
+
+Pipe::Pipe() {
+ create_pipe(m_read_end, m_write_end);
+}
+
+} // namespace winapi
diff --git a/src/process.cpp b/src/process.cpp
index 1771328..5887b05 100644
--- a/src/process.cpp
+++ b/src/process.cpp
@@ -21,42 +21,62 @@ namespace {
typedef std::vector<wchar_t> EscapedCommandLine;
-Handle create_process(EscapedCommandLine cmd_line) {
- BOOST_STATIC_CONSTEXPR DWORD flags = CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT;
+Handle create_process(EscapedCommandLine cmd_line, Process::IO& io) {
+ BOOST_STATIC_CONSTEXPR DWORD flags = /*CREATE_NO_WINDOW | */ CREATE_UNICODE_ENVIRONMENT;
STARTUPINFOW startup_info;
std::memset(&startup_info, 0, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
+ startup_info.dwFlags = STARTF_USESTDHANDLES;
+ startup_info.hStdInput = static_cast<HANDLE>(io.std_in.handle);
+ startup_info.hStdOutput = static_cast<HANDLE>(io.std_out.handle);
+ startup_info.hStdError = static_cast<HANDLE>(io.std_err.handle);
PROCESS_INFORMATION child_info;
std::memset(&child_info, 0, sizeof(child_info));
const auto ret = ::CreateProcessW(
- NULL, cmd_line.data(), NULL, NULL, FALSE, flags, NULL, NULL, &startup_info, &child_info);
+ NULL, cmd_line.data(), NULL, NULL, TRUE, flags, NULL, NULL, &startup_info, &child_info);
if (!ret) {
throw error::windows(GetLastError(), "CreateProcessW");
}
- Handle h_process{child_info.hProcess};
- Handle h_thread{child_info.hThread};
+ io.close();
- return std::move(h_process);
+ Handle process{child_info.hProcess};
+ Handle thread{child_info.hThread};
+
+ return std::move(process);
}
EscapedCommandLine escape_command_line(const CommandLine& cmd_line) {
- const auto whole = widen(cmd_line.join());
- return {whole.cbegin(), whole.cend()};
+ const auto unicode_cmd_line = widen(cmd_line.join());
+ EscapedCommandLine buffer;
+ buffer.reserve(unicode_cmd_line.size() + 1);
+ buffer.assign(unicode_cmd_line.cbegin(), unicode_cmd_line.cend());
+ buffer.emplace_back(L'\0');
+ return buffer;
}
-Handle create_process(const CommandLine& cmd_line) {
- return create_process(escape_command_line(cmd_line));
+Handle create_process(const CommandLine& cmd_line, Process::IO& io) {
+ return create_process(escape_command_line(cmd_line), io);
}
} // namespace
+void Process::IO::close() {
+ std_in.handle.close();
+ std_out.handle.close();
+ std_err.handle.close();
+}
+
Process Process::create(const CommandLine& cmd_line) {
- return Process{create_process(cmd_line)};
+ return create(cmd_line, {});
+}
+
+Process Process::create(const CommandLine& cmd_line, IO io) {
+ return Process{create_process(cmd_line, io)};
}
void Process::wait() {
@@ -64,7 +84,7 @@ void Process::wait() {
switch (ret) {
case WAIT_OBJECT_0:
- m_handle = Handle{};
+ m_handle.close();
return;
case WAIT_FAILED:
throw error::windows(GetLastError(), "WaitForSingleObject");
diff --git a/src/stream.cpp b/src/stream.cpp
new file mode 100644
index 0000000..b31635e
--- /dev/null
+++ b/src/stream.cpp
@@ -0,0 +1,41 @@
+// Copyright (c) 2020 Egor Tensin <Egor.Tensin@gmail.com>
+// This file is part of the "winapi-common" project.
+// For details, see https://github.com/egor-tensin/winapi-common.
+// Distributed under the MIT License.
+
+#include <winapi/file.hpp>
+#include <winapi/handle.hpp>
+#include <winapi/stream.hpp>
+
+#include <string>
+#include <utility>
+
+namespace winapi {
+namespace process {
+
+Stdin::Stdin() : Stream{Handle::std_in()} {}
+
+Stdout::Stdout() : Stream{Handle::std_out()} {}
+
+Stderr::Stderr() : Stream{Handle::std_err()} {}
+
+Stdin::Stdin(const std::string& path) : Stream{File::open_for_reading(path)} {}
+
+Stdout::Stdout(const std::string& path) : Stream{File::open_for_writing(path)} {}
+
+Stderr::Stderr(const std::string& path) : Stream{File::open_for_writing(path)} {}
+
+Stdin::Stdin(Pipe& pipe) : Stream{std::move(pipe.read_end())} {
+ pipe.write_end().dont_inherit();
+}
+
+Stdout::Stdout(Pipe& pipe) : Stream{std::move(pipe.write_end())} {
+ pipe.read_end().dont_inherit();
+}
+
+Stderr::Stderr(Pipe& pipe) : Stream{std::move(pipe.write_end())} {
+ pipe.read_end().dont_inherit();
+}
+
+} // namespace process
+} // namespace winapi
diff --git a/test/unit_tests/CMakeLists.txt b/test/unit_tests/CMakeLists.txt
index 33a6101..4580a57 100644
--- a/test/unit_tests/CMakeLists.txt
+++ b/test/unit_tests/CMakeLists.txt
@@ -1,6 +1,6 @@
file(GLOB unit_tests_src "*.cpp")
add_executable(unit_tests ${unit_tests_src})
-target_link_libraries(unit_tests PRIVATE winapi_common)
+target_link_libraries(unit_tests PRIVATE winapi_common winapi_utf8)
set_target_properties(unit_tests PROPERTIES OUTPUT_NAME winapi-common-unit-tests)
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
diff --git a/test/unit_tests/process.cpp b/test/unit_tests/process.cpp
index 69327e1..57deef0 100644
--- a/test/unit_tests/process.cpp
+++ b/test/unit_tests/process.cpp
@@ -4,52 +4,73 @@
// Distributed under the MIT License.
#include <winapi/cmd_line.hpp>
+#include <winapi/pipe.hpp>
#include <winapi/process.hpp>
+#include <winapi/utf8.hpp>
#include <boost/test/unit_test.hpp>
#include <stdexcept>
#include <string>
+#include <utility>
+
+using namespace winapi;
namespace {
-class WhereTestExe {
+class WithEchoExe {
public:
- WhereTestExe() : m_test_exe(find_test_exe()) {}
+ WithEchoExe() : m_echo_exe(find_echo_exe()) {}
- const std::string& get_test_exe() { return m_test_exe; }
+ const std::string& get_echo_exe() { return m_echo_exe; }
private:
- static std::string find_test_exe() {
- static const std::string param_prefix{"--test_exe="};
- const auto cmd_line = winapi::CommandLine::query();
+ static std::string find_echo_exe() {
+ static const std::string prefix{"--echo_exe="};
+ return find_param_value(prefix);
+ }
+
+ static std::string find_param_value(const std::string& param_prefix) {
+ const auto cmd_line = CommandLine::query();
const auto& args = cmd_line.get_args();
for (const auto& arg : args) {
if (arg.rfind(param_prefix, 0) == 0) {
return arg.substr(param_prefix.length());
}
}
- throw std::runtime_error{"couldn't find parameter --test_exe"};
+ throw std::runtime_error{"couldn't find parameter " + param_prefix};
}
- const std::string m_test_exe;
+ const std::string m_echo_exe;
};
} // namespace
BOOST_AUTO_TEST_SUITE(process_tests)
-BOOST_AUTO_TEST_CASE(create_dir) {
- const winapi::CommandLine cmd_line{"cmd.exe", {"/c", "dir"}};
- auto process = winapi::Process::create(cmd_line);
+BOOST_FIXTURE_TEST_CASE(create_echo, WithEchoExe) {
+ const CommandLine cmd_line{get_echo_exe()};
+ auto process = Process::create(cmd_line);
+ process.wait();
+ BOOST_TEST(true, "Successfully created test process");
+}
+
+BOOST_FIXTURE_TEST_CASE(create_echo_with_args, WithEchoExe) {
+ const CommandLine cmd_line{get_echo_exe(), {"1", "2", "3"}};
+ auto process = Process::create(cmd_line);
process.wait();
}
-BOOST_FIXTURE_TEST_CASE(create_test_exe, WhereTestExe) {
- const winapi::CommandLine cmd_line{get_test_exe()};
- auto process = winapi::Process::create(cmd_line);
+BOOST_FIXTURE_TEST_CASE(create_echo_pipe, WithEchoExe) {
+ const CommandLine cmd_line{get_echo_exe(), {"1", "2", "3"}};
+ Process::IO io;
+ Pipe stdout_pipe;
+ io.std_out = process::Stdout{stdout_pipe};
+ auto process = Process::create(cmd_line, std::move(io));
+ const auto output = stdout_pipe.read_end().read();
process.wait();
- BOOST_TEST(true, "Successfully created test process");
+ const auto utf8 = narrow(output);
+ BOOST_TEST(utf8 == "1\r\n2\r\n3\r\n");
}
BOOST_AUTO_TEST_SUITE_END()