aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/test/unit_tests/shared/command.hpp
diff options
context:
space:
mode:
authorEgor Tensin <Egor.Tensin@gmail.com>2020-10-24 21:33:44 +0300
committerEgor Tensin <Egor.Tensin@gmail.com>2020-10-27 00:11:41 +0300
commita864099ba77157090c4cd12817245122c163ec24 (patch)
treef15b1f2801219fa9af6111fa28591858d87711ec /test/unit_tests/shared/command.hpp
parentProcess: add termination methods (diff)
downloadwinapi-common-a864099ba77157090c4cd12817245122c163ec24.tar.gz
winapi-common-a864099ba77157090c4cd12817245122c163ec24.zip
rework Process API & tests
* Add separate classes ProcessParameters & ShellParameters for Process::create() and Process::shell() methods. * Add a separate "worker" executable. It's used in tests via a fairly complicated scheme, receiving orders to execute via a shared memory region. * Add tests that utilize the Console API, reading console window's screen buffer directly, making for more reliable tests & broader coverage.
Diffstat (limited to 'test/unit_tests/shared/command.hpp')
-rw-r--r--test/unit_tests/shared/command.hpp142
1 files changed, 142 insertions, 0 deletions
diff --git a/test/unit_tests/shared/command.hpp b/test/unit_tests/shared/command.hpp
new file mode 100644
index 0000000..de7c704
--- /dev/null
+++ b/test/unit_tests/shared/command.hpp
@@ -0,0 +1,142 @@
+// 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 "fixed_size.hpp"
+
+#include <winapi/shmem.hpp>
+
+#include <boost/config.hpp>
+#include <boost/interprocess/sync/interprocess_condition.hpp>
+#include <boost/interprocess/sync/interprocess_mutex.hpp>
+#include <boost/interprocess/sync/scoped_lock.hpp>
+
+#include <windows.h>
+
+#include <cstddef>
+#include <exception>
+#include <functional>
+#include <stdexcept>
+
+namespace worker {
+
+struct StdHandles {
+ HANDLE in;
+ HANDLE out;
+ HANDLE err;
+};
+
+class Command {
+public:
+ BOOST_STATIC_CONSTEXPR auto SHMEM_NAME = "shmem-test-cmd";
+
+ typedef winapi::SharedObject<Command> Shared;
+
+ static Shared create() { return Shared::create(SHMEM_NAME); }
+ static Shared open() { return Shared::open(SHMEM_NAME); }
+
+ typedef boost::interprocess::interprocess_mutex mutex;
+ typedef boost::interprocess::interprocess_condition condition_variable;
+ typedef boost::interprocess::scoped_lock<mutex> lock;
+
+ enum Action {
+ EXIT = 1,
+ GET_CONSOLE_WINDOW,
+ IS_WINDOW_VISIBLE,
+ GET_STD_HANDLES,
+ TEST_WRITE,
+ GET_CONSOLE_BUFFER,
+ };
+
+ union Args {
+ std::size_t numof_lines;
+ };
+
+ union Result {
+ HWND console_window;
+ bool is_window_visible;
+ StdHandles std_handles;
+ fixed_size::StringList<> console_buffer;
+ };
+
+ typedef std::function<void(Args&)> SetArgs;
+ typedef std::function<void(const Result&)> ReadResult;
+
+ void get_result(Action action, const SetArgs& set_args, const ReadResult& read_result) {
+ {
+ lock lck{m_mtx};
+ m_action = action;
+ set_args(m_args);
+
+ m_action_requested = true;
+ m_result_ready = false;
+ m_error_occured = false;
+ }
+ m_cv.notify_all();
+
+ lock lck{m_mtx};
+ m_cv.wait(lck, [this]() { return m_error_occured || m_result_ready; });
+
+ const auto error = m_error_occured;
+
+ m_action_requested = false;
+ m_result_ready = false;
+ m_error_occured = false;
+
+ if (error) {
+ throw std::runtime_error{"Worker error: " + m_error.extract()};
+ }
+
+ read_result(m_result);
+ }
+
+ void get_result(Action action, const ReadResult& read_result) {
+ return get_result(
+ action, [](Args&) {}, read_result);
+ }
+
+ void get_result(Action action) {
+ return get_result(action, [](const Result&) {});
+ }
+
+ typedef std::function<void(Action, const Args&, Result&)> ProcessAction;
+
+ void process_action(const ProcessAction& callback) {
+ {
+ lock lck{m_mtx};
+ m_cv.wait(lck, [this]() { return m_action_requested; });
+
+ m_action_requested = false;
+ m_result_ready = false;
+ m_error_occured = false;
+
+ try {
+ callback(m_action, m_args, m_result);
+ m_result_ready = true;
+ } catch (const std::exception& e) {
+ m_error_occured = true;
+ m_error = m_error.convert(e.what());
+ }
+ }
+ m_cv.notify_all();
+ }
+
+private:
+ Action m_action;
+ Args m_args;
+ bool m_action_requested = false;
+
+ Result m_result;
+ bool m_result_ready = false;
+
+ bool m_error_occured = false;
+ fixed_size::String<> m_error;
+
+ mutex m_mtx;
+ condition_variable m_cv;
+};
+
+} // namespace worker