From: Daniel Karbach Date: Fri, 18 Nov 2016 15:39:53 +0000 (+0100) Subject: test for invoking with unknown argument X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=307482574d3acac09e38263de521a1826ceec7ce;p=blank.git test for invoking with unknown argument --- diff --git a/src/app/Process.hpp b/src/app/Process.hpp index 3efe963..490cf07 100644 --- a/src/app/Process.hpp +++ b/src/app/Process.hpp @@ -10,13 +10,17 @@ namespace blank { class Process { +public: + using Arguments = std::vector; + using Environment = std::vector; + public: /// launch process executing the file at given path with /// given arguments and environment Process( const std::string &path, - const std::vector &args, - const std::vector &env); + const Arguments &args, + const Environment &env); ~Process(); public: diff --git a/src/app/proc.cpp b/src/app/proc.cpp index 3137b04..f30c0cb 100644 --- a/src/app/proc.cpp +++ b/src/app/proc.cpp @@ -22,8 +22,8 @@ struct Process::Impl { Impl( const string &path_in, - const vector &args, - const vector &env); + const Arguments &args, + const Environment &env); ~Impl(); size_t WriteIn(const void *buffer, size_t max_len); @@ -50,8 +50,8 @@ struct Process::Impl { Process::Process( const string &path, - const vector &args, - const vector &env) + const Arguments &args, + const Environment &env) : impl(new Impl(path, args, env)) , joined(false) , status(0) { @@ -94,8 +94,8 @@ int Process::Join() { Process::Impl::Impl( const string &path_in, - const vector &args, - const vector &env + const Arguments &args, + const Environment &env ) { const char *path = path_in.c_str(); char *envp[env.size() + 1]; diff --git a/tst/integration/InvocationTest.cpp b/tst/integration/InvocationTest.cpp new file mode 100644 index 0000000..0d4c52c --- /dev/null +++ b/tst/integration/InvocationTest.cpp @@ -0,0 +1,27 @@ +#include "InvocationTest.hpp" + +#include "TestInstance.hpp" + +CPPUNIT_TEST_SUITE_REGISTRATION(blank::test::InvocationTest); + + +namespace blank { +namespace test { + +void InvocationTest::setUp() { + +} + +void InvocationTest::tearDown() { + +} + + +void InvocationTest::testUnknownArg() { + TestInstance prog({ "--worscht" }); + prog.AssertErrorLine("unknown option --worscht"); + prog.AssertExitStatus(1); +} + +} +} diff --git a/tst/integration/InvocationTest.hpp b/tst/integration/InvocationTest.hpp new file mode 100644 index 0000000..cca53dd --- /dev/null +++ b/tst/integration/InvocationTest.hpp @@ -0,0 +1,30 @@ +#ifndef BLANK_TEST_INTEGRATION_INVOCATIONTEST_HPP_ +#define BLANK_TEST_INTEGRATION_INVOCATIONTEST_HPP_ + +#include + + +namespace blank { +namespace test { + +class InvocationTest +: public CppUnit::TestFixture { + +CPPUNIT_TEST_SUITE(InvocationTest); + +CPPUNIT_TEST(testUnknownArg); + +CPPUNIT_TEST_SUITE_END(); + +public: + void setUp(); + void tearDown(); + + void testUnknownArg(); + +}; + +} +} + +#endif diff --git a/tst/integration/ServerTest.cpp b/tst/integration/ServerTest.cpp index fbec21a..9cee9ee 100644 --- a/tst/integration/ServerTest.cpp +++ b/tst/integration/ServerTest.cpp @@ -1,6 +1,6 @@ #include "ServerTest.hpp" -#include "TestServer.hpp" +#include "TestInstance.hpp" CPPUNIT_TEST_SUITE_REGISTRATION(blank::test::ServerTest); @@ -18,7 +18,7 @@ void ServerTest::tearDown() { void ServerTest::testStartup() { - TestServer server; + TestInstance server({ "--server" }, true); } } diff --git a/tst/integration/TestInstance.cpp b/tst/integration/TestInstance.cpp new file mode 100644 index 0000000..f9bc727 --- /dev/null +++ b/tst/integration/TestInstance.cpp @@ -0,0 +1,147 @@ +#include "TestInstance.hpp" + +#include + +using namespace std; + + +namespace blank { +namespace test { + +namespace { + +Process::Arguments combine_args(const TempDir &dir, const Process::Arguments &in, bool cmd) { + Process::Arguments out; + out.reserve(in.size() + (cmd ? 5 : 3)); + out.emplace_back("blank"); + out.insert(out.end(), in.begin(), in.end()); + out.emplace_back("--save-path"); + out.emplace_back(dir.Path()); + if (cmd) { + out.emplace_back("--cmd-port"); + out.emplace_back("12354"); + } + return out; +} + +} + +TestInstance::TestInstance(const Process::Arguments &args, bool cmd) +: dir() +, proc( + "./blank" BLANK_SUFFIX, + combine_args(dir, args, cmd), + { }) +, conn() +, out_buf() +, err_buf() +, cmd_buf() { + if (cmd) { + // 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); + } +} + +TestInstance::~TestInstance() { + proc.Terminate(); +} + + +void TestInstance::WriteInput(const string &data) { + const char *i = data.c_str(); + const char *end = i + data.length(); + while (i != end) { + size_t len = proc.WriteIn(i, end - i); + i += len; + } +} + +void TestInstance::ReadOutputLine(string &line) { + while (!out_buf.Extract(line)) { + // buffer exhausted, fetch more data + out_buf.Update(proc.ReadOut(out_buf.WriteHead(), out_buf.Remain())); + } +} + +void TestInstance::AssertOutputLine(const string &expected) { + string line; + ReadOutputLine(line); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected line in stdout", + expected, line); +} + +void TestInstance::WaitOutputLine(const string &expected) { + string line; + while (true) { + ReadOutputLine(line); + if (line == expected) { + return; + } + } +} + +void TestInstance::ReadErrorLine(string &line) { + while (!err_buf.Extract(line)) { + // buffer exhausted, fetch more data + err_buf.Update(proc.ReadErr(err_buf.WriteHead(), err_buf.Remain())); + } +} + +void TestInstance::AssertErrorLine(const string &expected) { + string line; + ReadErrorLine(line); + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected line in stderr", + expected, line); +} + +void TestInstance::WaitErrorLine(const string &expected) { + string line; + while (true) { + ReadErrorLine(line); + if (line == expected) { + return; + } + } +} + + +void TestInstance::AssertExitStatus(int expected) { + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "unexpected line in stderr", + expected, proc.Join()); +} + + +void TestInstance::WaitCommandMessage(const string &line) { + WaitCommandLine(" > " + line); +} + +void TestInstance::WaitCommandError(const string &line) { + WaitCommandLine(" ! " + line); +} + +void TestInstance::WaitCommandBroadcast(const string &line) { + WaitCommandLine(" @ " + line); +} + +void TestInstance::WaitCommandLine(const string &expected) { + string line; + while (true) { + if (!cmd_buf.Extract(line)) { + // buffer exhausted, fetch more data + cmd_buf.Update(conn.Recv(cmd_buf.WriteHead(), cmd_buf.Remain())); + continue; + } + if (line == expected) { + return; + } + } +} + +} +} diff --git a/tst/integration/TestInstance.hpp b/tst/integration/TestInstance.hpp new file mode 100644 index 0000000..3bd23d4 --- /dev/null +++ b/tst/integration/TestInstance.hpp @@ -0,0 +1,76 @@ +#ifndef BLANK_TEST_INTEGRATION_TESTINSTANCE_HPP_ +#define BLANK_TEST_INTEGRATION_TESTINSTANCE_HPP_ + +#include "app/Process.hpp" +#include "io/filesystem.hpp" +#include "io/LineBuffer.hpp" +#include "net/tcp.hpp" + + +namespace blank { +namespace test { + +class TestInstance { + +public: + /// Launch a blank instance with given arguments (program name + /// will be prepended by the constructor). + /// If connect is true, a command service connection will be + /// established during construction. + explicit TestInstance(const Process::Arguments &args, bool connect = false); + ~TestInstance(); + +public: + /// Write given text to program's stdin. + /// Data is not modified, so if you want to push a line, be + /// sure to include a newline character. + void WriteInput(const std::string &data); + + /// read next line from programs stdout + void ReadOutputLine(std::string &line); + /// assert that the next line the program writes to stdout will + /// be the given one (without a trailing newline character) + void AssertOutputLine(const std::string &line); + /// wait until program writes given line to stdout + void WaitOutputLine(const std::string &line); + + /// read next line from programs stderr + void ReadErrorLine(std::string &line); + /// assert that the next line the program writes to stderr will + /// be the given one (without a trailing newline character) + void AssertErrorLine(const std::string &line); + /// wait until program writes given line to stderr + void WaitErrorLine(const std::string &line); + + /// make sure the process terminated with given status + void AssertExitStatus(int expected); + + // the following methods are only valid when a command service is connected + + /// 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 program + void SendCommand(const std::string &); + +private: + TempDir dir; + Process proc; + tcp::Socket conn; + size_t head; + LineBuffer out_buf; + LineBuffer err_buf; + LineBuffer cmd_buf; + +}; + +} +} + +#endif diff --git a/tst/integration/TestServer.cpp b/tst/integration/TestServer.cpp deleted file mode 100644 index cb5819d..0000000 --- a/tst/integration/TestServer.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "TestServer.hpp" - -#include - - -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 deleted file mode 100644 index 9ac31d7..0000000 --- a/tst/integration/TestServer.hpp +++ /dev/null @@ -1,48 +0,0 @@ -#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 serv_buf; - LineBuffer sock_buf; - -}; - -} -} - -#endif