]> git.localhorst.tv Git - blank.git/commitdiff
first test for actual program binary
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 18 Nov 2016 14:15:21 +0000 (15:15 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Fri, 18 Nov 2016 14:15:21 +0000 (15:15 +0100)
src/app/Process.hpp
src/app/proc.cpp
src/io/LineBuffer.hpp [new file with mode: 0644]
src/shared/net.cpp
tst/app/ProcessTest.cpp
tst/integration/ServerTest.cpp [new file with mode: 0644]
tst/integration/ServerTest.hpp [new file with mode: 0644]
tst/integration/TestServer.cpp [new file with mode: 0644]
tst/integration/TestServer.hpp [new file with mode: 0644]

index 985b661aac967819adf89a9ebcb42fb4fc3fc0f7..3efe96383ef36bae5956c18754a26ba3d7385447 100644 (file)
@@ -33,6 +33,9 @@ public:
        /// @return the number of bytes read
        std::size_t ReadErr(void *buffer, std::size_t max_len);
 
+       /// ask the process nicely to terminate
+       /// (except on win32)
+       void Terminate();
        /// wait until the process exits and fetch its exit status
        int Join();
 
index 33d1a14c9c83a8126d4bd039897aaa6a278b58cb..3137b0433a990e839a691a3d59f5d023e1c22cf7 100644 (file)
@@ -1,14 +1,16 @@
 #include "Process.hpp"
 
 #ifdef _WIN32
-#  error "TODO: windows implementation of Process"
+#  include <tchar.h>
+#  include <windows.h>
 #else
-#  include <cstdio>
 #  include <fcntl.h>
+#  include <signal.h>
 #  include <unistd.h>
 #  include <sys/wait.h>
 #endif
 
+#include <cstdio>
 #include <stdexcept>
 
 using namespace std;
@@ -28,6 +30,7 @@ struct Process::Impl {
        size_t ReadOut(void *buffer, size_t max_len);
        size_t ReadErr(void *buffer, size_t max_len);
 
+       void Terminate();
        int Join();
 
 #ifdef _WIN32
@@ -72,6 +75,12 @@ size_t Process::ReadErr(void *buffer, size_t max_len) {
        return impl->ReadErr(buffer, max_len);
 }
 
+void Process::Terminate() {
+       if (!joined) {
+               impl->Terminate();
+       }
+}
+
 int Process::Join() {
        if (joined) {
                return status;
@@ -266,6 +275,16 @@ size_t Process::Impl::ReadErr(void *buffer, size_t max_len) {
 #endif
 }
 
+void Process::Impl::Terminate() {
+#ifdef _WIN32
+       if (!TerminateProcess(pi.hProcess, -1)) {
+#else
+       if (kill(pid, SIGTERM) == -1) {
+#endif
+               throw runtime_error("failed to terminate child process");
+       }
+}
+
 int Process::Impl::Join() {
 #ifdef _WIN32
        CloseHandle(fd_in[1]);
diff --git a/src/io/LineBuffer.hpp b/src/io/LineBuffer.hpp
new file mode 100644 (file)
index 0000000..4fcc3ca
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef BLANK_IO_LINEBUFFER_HPP_
+#define BLANK_IO_LINEBUFFER_HPP_
+
+#include <algorithm>
+#include <cassert>
+#include <string>
+
+
+namespace blank {
+
+template<std::size_t size>
+class LineBuffer {
+
+public:
+       explicit LineBuffer(char term = '\n') noexcept
+       : buffer{0}
+       , term(term)
+       , head(buffer) { }
+
+       char *begin() noexcept {
+               return buffer;
+       }
+       const char *begin() const noexcept {
+               return buffer;
+       }
+       char *end() noexcept {
+               return head;
+       }
+       const char *end() const noexcept {
+               return head;
+       }
+
+       /// extract one line from the buffer, terminator not included
+       /// @return false if the buffer does not contain a complete line
+       bool Extract(std::string &line) {
+               char *line_end = std::find(begin(), end(), term);
+               if (line_end == end()) {
+                       return false;
+               }
+               line.assign(begin(), line_end);
+               ++line_end;
+               std::move(line_end, end(), begin());
+               return true;
+       }
+
+       /// get a pointer to append data to the buffer
+       /// it is safe to write at most Remain() bytes
+       char *WriteHead() noexcept {
+               return head;
+       }
+
+       // call when data has been written to WriteHead()
+       void Update(std::size_t written) {
+               assert(written <= Remain());
+               head += written;
+       }
+
+       std::size_t Remain() const noexcept {
+               return std::distance(end(), buffer + size);
+       }
+
+private:
+       char buffer[size];
+       char term;
+       char *head;
+
+};
+
+}
+
+#endif
index 17b2e8d1ea2066b27f27870254b33e201d26bb6d..e0b7fc7816823dfb33a7ee61882a5541450bceb0 100644 (file)
@@ -60,6 +60,7 @@ CommandBuffer::~CommandBuffer() {
 
 
 void CommandBuffer::Error(const string &msg) {
+       // TODO: prefix each line in message/error/broadcast
        write_buffer += " ! ";
        write_buffer += msg;
        write_buffer += '\n';
index 778b7f6d63e5d486725e821dcf37c5a4ba4a4268..db181aafc3c1e433ae97189ccea594affda33201 100644 (file)
@@ -21,7 +21,7 @@ void ProcessTest::tearDown() {
 
 void ProcessTest::testExit() {
 #ifdef __WIN32
-#  error "TODO: implemente Process tests for windows"
+#  error "TODO: implement Process tests for windows"
 #else
 
        {
diff --git a/tst/integration/ServerTest.cpp b/tst/integration/ServerTest.cpp
new file mode 100644 (file)
index 0000000..fbec21a
--- /dev/null
@@ -0,0 +1,25 @@
+#include "ServerTest.hpp"
+
+#include "TestServer.hpp"
+
+CPPUNIT_TEST_SUITE_REGISTRATION(blank::test::ServerTest);
+
+
+namespace blank {
+namespace test {
+
+void ServerTest::setUp() {
+
+}
+
+void ServerTest::tearDown() {
+
+}
+
+
+void ServerTest::testStartup() {
+       TestServer server;
+}
+
+}
+}
diff --git a/tst/integration/ServerTest.hpp b/tst/integration/ServerTest.hpp
new file mode 100644 (file)
index 0000000..bc8336d
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef BLANK_TEST_INTEGRATION_SERVERTEST_HPP_
+#define BLANK_TEST_INTEGRATION_SERVERTEST_HPP_
+
+#include <cppunit/extensions/HelperMacros.h>
+
+
+namespace blank {
+namespace test {
+
+class ServerTest
+: public CppUnit::TestFixture {
+
+CPPUNIT_TEST_SUITE(ServerTest);
+
+CPPUNIT_TEST(testStartup);
+
+CPPUNIT_TEST_SUITE_END();
+
+public:
+       void setUp();
+       void tearDown();
+
+       void testStartup();
+
+};
+
+}
+}
+
+#endif
diff --git a/tst/integration/TestServer.cpp b/tst/integration/TestServer.cpp
new file mode 100644 (file)
index 0000000..cb5819d
--- /dev/null
@@ -0,0 +1,73 @@
+#include "TestServer.hpp"
+
+#include <iostream>
+
+
+namespace blank {
+namespace test {
+
+TestServer::TestServer()
+: dir()
+, proc(
+       "./blank" BLANK_SUFFIX,
+       { "blank", "--server", "--save-path", dir.Path(), "--cmd-port", "12354" },
+       { })
+, conn()
+, serv_buf()
+, sock_buf() {
+       // wait for command service startup
+       // TODO: timeouts for reading from process
+       WaitOutputLine("listening on TCP port 12354");
+       // connect to command service
+       conn = tcp::Socket("localhost", 12354);
+}
+
+TestServer::~TestServer() {
+       proc.Terminate();
+}
+
+
+void TestServer::WaitOutputLine(const std::string &expected) {
+       std::string line;
+       while (true) {
+               if (!serv_buf.Extract(line)) {
+                       // buffer exhausted, fetch more data
+                       serv_buf.Update(proc.ReadOut(serv_buf.WriteHead(), serv_buf.Remain()));
+                       continue;
+               }
+               if (line == expected) {
+                       return;
+               } else {
+                       std::cerr << "ignoring line: " << line << std::endl;
+               }
+       }
+}
+
+void TestServer::WaitCommandMessage(const std::string &line) {
+       WaitCommandLine(" > " + line);
+}
+
+void TestServer::WaitCommandError(const std::string &line) {
+       WaitCommandLine(" ! " + line);
+}
+
+void TestServer::WaitCommandBroadcast(const std::string &line) {
+       WaitCommandLine(" @ " + line);
+}
+
+void TestServer::WaitCommandLine(const std::string &expected) {
+       std::string line;
+       while (true) {
+               if (!serv_buf.Extract(line)) {
+                       // buffer exhausted, fetch more data
+                       serv_buf.Update(conn.Recv(serv_buf.WriteHead(), serv_buf.Remain()));
+                       continue;
+               }
+               if (line == expected) {
+                       return;
+               }
+       }
+}
+
+}
+}
diff --git a/tst/integration/TestServer.hpp b/tst/integration/TestServer.hpp
new file mode 100644 (file)
index 0000000..9ac31d7
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef BLANK_TEST_INTEGRATION_TESTSERVER_HPP_
+#define BLANK_TEST_INTEGRATION_TESTSERVER_HPP_
+
+#include "app/Process.hpp"
+#include "io/filesystem.hpp"
+#include "io/LineBuffer.hpp"
+#include "net/tcp.hpp"
+
+
+namespace blank {
+namespace test {
+
+class TestServer {
+
+public:
+       TestServer();
+       ~TestServer();
+
+public:
+       /// wait until server writes given line to stdout
+       void WaitOutputLine(const std::string &line);
+
+       /// wait for given message on the command service
+       void WaitCommandMessage(const std::string &line);
+       /// wait for given error on the command service
+       void WaitCommandError(const std::string &line);
+       /// wait for given broadcast on the command service
+       void WaitCommandBroadcast(const std::string &line);
+       /// wait for given line on the command service
+       void WaitCommandLine(const std::string &line);
+
+       /// send command line to server
+       void SendCommand(const std::string &);
+
+private:
+       TempDir dir;
+       Process proc;
+       tcp::Socket conn;
+       size_t head;
+       LineBuffer<BUFSIZ> serv_buf;
+       LineBuffer<BUFSIZ> sock_buf;
+
+};
+
+}
+}
+
+#endif