diff --git a/Makefile.am b/Makefile.am index f8b13c8..512f38a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,3 +9,11 @@ if BUILD_TESTTOOLS SUBDIRS += nvtsrv SUBDIRS += nvttest endif +if BUILD_TEST +SUBDIRS += test +endif + +.PHONY: $(SUBDIRS) test +$(SUBDIRS) test: + $(MAKE) -C $@ +test: $(SUBDIRS) diff --git a/configure.ac b/configure.ac index a638f3a..b307c6c 100644 --- a/configure.ac +++ b/configure.ac @@ -6,10 +6,10 @@ m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC +AC_PROG_CXX AC_PROG_INSTALL -define([AC_LIBTOOL_LANG_CXX_CONFIG], [:])dnl define([AC_LIBTOOL_LANG_F77_CONFIG], [:])dnl define([AC_LIBTOOL_LANG_GCJ_CONFIG], [:])dnl @@ -31,6 +31,8 @@ AC_ARG_ENABLE(testtools, enable_testtools=no) AM_CONDITIONAL([BUILD_TESTTOOLS], [test "x$enable_testtools" = "xyes"]) +AM_CONDITIONAL(BUILD_TEST, false) + CFLAGS_WARN="$CFLAGS_WARN -Wall -Wextra -Werror -Wno-unused-parameter" CFLAGS_WARN="$CFLAGS_WARN -Wmissing-prototypes -Wmissing-declarations -Wstrict-prototypes" CFLAGS_WARN="$CFLAGS_WARN -Waggregate-return -Wpointer-arith -Wshadow -Wwrite-strings" @@ -44,6 +46,7 @@ lib/Makefile src/Makefile nvtsrv/Makefile nvttest/Makefile +test/Makefile ]) AC_OUTPUT diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..f2a860f --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,48 @@ +# Unit test makefile +# +.NOTPARALLEL: + +noinst_PROGRAMS = $(GTESTS) + + GTEST_LIBS = -lgtest -lstdc++ + + GTESTS = test_misc test_nvttest + + AM_CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter + AM_CFLAGS += $(CFLAGS_ASAN) + + AM_CXXFLAGS = $(AM_CFLAGS) + + AM_CPPFLAGS = -I $(top_builddir) + AM_CPPFLAGS += -D TOP_SRC_DIR='"$(top_srcdir)"' -D TOP_BLD_DIR='"$(top_builddir)"' + AM_CPPFLAGS += -D BUILD_TEST=1 + + LIBS += $(GTEST_LIBS) + + TEST_COMMON = test.cpp test.h + +test_misc_SOURCES = $(TEST_COMMON) test_misc.cpp +test_misc_LDADD = $(LIBS) + +test_nvttest_SOURCES = $(TEST_COMMON) test_nvttest.cpp +test_nvttest_LDADD = $(LIBS) + + TESTS_RUN = $(addprefix run-, $(GTESTS)) + + VG_PROG = valgrind --leak-check=full + +all-local: run + +.PHONY: run $(TESTS_RUN) +run: $(TESTS_RUN) +$(TESTS_RUN): run-%: % +# $(TEST_ENV) ./.libs/$* $(RUN_OPTS) + $(TEST_ENV) ./$* $(RUN_OPTS) + + TESTS_RUN_VG = $(addprefix run-vg-, $(GTESTS)) + +.PHONY: run-vg $(TESTS_RUN_VG) +run-vg: $(TESTS_RUN_VG) +$(TESTS_RUN_VG): run-vg-%: % +# $(TEST_ENV) $(VG_PROG) ./.libs/$* $(RUN_OPTS) + $(TEST_ENV) $(VG_PROG) ./$* $(RUN_OPTS) diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 0000000..74f155a --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,70 @@ +/* + * General test stuff + * + * - The main() function + * - Some printing functions + */ +#include "test.h" + +int debug = 0; + +static bool ttyout = false; + +int main(int argc, char **argv) +{ + const char *s; + + ttyout = isatty(STDOUT_FILENO); + + ::testing::InitGoogleTest(&argc, argv); + + for (argc--, argv++; argc > 0; argc--, argv++) + { + s = argv[0]; + if (*s++ != '-') + break; + again: + switch (*s++) + { + case 'd': + debug++; + goto again; + } + } + + return RUN_ALL_TESTS(); +} + +#include + +void _pr_text(const char *col, const char *fmt, va_list args) +{ + char fmtx[1024]; + + if (ttyout) + snprintf(fmtx, sizeof(fmtx), "%s[ ] - %s%s\n", + col, fmt, COL_RST); + else + snprintf(fmtx, sizeof(fmtx), "[ ] - %s\n", fmt); + fmt = fmtx; + + vprintf(fmt, args); +} + +void pr_text(const char *col, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _pr_text(col, fmt, args); + va_end(args); +} + +void pr_info(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _pr_text(COL_YEL, fmt, args); + va_end(args); +} diff --git a/test/test.h b/test/test.h new file mode 100644 index 0000000..a966371 --- /dev/null +++ b/test/test.h @@ -0,0 +1,25 @@ +#ifndef TEST_H +#define TEST_H 1 + +#include + +#include "config.h" + +#define D(...) do{ if (debug) printf(__VA_ARGS__); }while(0) +#define D2(...) do{ if (debug > 1) printf(__VA_ARGS__); }while(0) + +extern int debug; + +#define COL_RST "\x1B[0m" +#define COL_RED "\x1B[31m" +#define COL_GRN "\x1B[32m" +#define COL_YEL "\x1B[33m" +#define COL_BLU "\x1B[34m" +#define COL_MAG "\x1B[35m" +#define COL_CYN "\x1B[36m" +#define COL_WHT "\x1B[37m" + +void pr_text(const char *col, const char *fmt, ...); +void pr_info(const char *fmt, ...); + +#endif /* TEST_H */ diff --git a/test/test_misc.cpp b/test/test_misc.cpp new file mode 100644 index 0000000..56a8d8f --- /dev/null +++ b/test/test_misc.cpp @@ -0,0 +1,6 @@ +#include "test.h" + +TEST(Misc, dummy) +{ + EXPECT_EQ(1, 1); +} diff --git a/test/test_nvttest.cpp b/test/test_nvttest.cpp new file mode 100644 index 0000000..1f71a8d --- /dev/null +++ b/test/test_nvttest.cpp @@ -0,0 +1,216 @@ +#include "test.h" + +#include +#include +#include +#include +#include +#include + +#define NVT_NAME "ttyNVT99" +#define NVT_DEV "/dev/" NVT_NAME + +#define NVT_SRV TOP_BLD_DIR "/nvtsrv/nvtsrv" +#define NVT_TEST TOP_BLD_DIR "/nvttest/nvttest" + +#define NVT_OPTS "-D" NVT_NAME + +#define LAUNCH_NVTTEST(...) proc_launch(NVT_TEST, NVT_OPTS, __VA_ARGS__) + +pid_t proc_spawn(char **argv) +{ + pid_t pid; + char buf[1024]; + + for (int i = 0, len = 0; argv[i]; i++) + len += snprintf(buf + len, sizeof(buf) - len, "%s ", argv[i]); + pr_info("Launching: %s", buf); + + pid = fork(); + if (pid > 0) + { + pr_info("... ok, pid = %d", pid); + return pid; + } + + // Child + + execv(argv[0], argv); + + pr_info("Launch failed: %m"); + + EXPECT_EQ(1, 0); + exit(1); +} + +void proc_waitfor(const char *name, pid_t pid) +{ + int err, wstatus; + + pr_info("Wait for %s (pid %d) to finish", name, pid); + + wstatus = 0; + err = waitpid(pid, &wstatus, 0); + if (err > 0) + pr_info("... ok, status = %x", wstatus); + else + pr_info("... error: %m"); + + EXPECT_GT(err, 0); + EXPECT_EQ(WEXITSTATUS(wstatus), 0); +} + +static pid_t proc_launch(const char *prog, ...) +{ + char *argv[16], *arg; + int i; + pid_t pid; + va_list args; + + va_start(args, prog); + + arg = (char *)prog; + i = 0; + argv[i++] = arg; + for (; arg != NULL;) + { + arg = va_arg(args, char *); + + argv[i++] = arg; + } + + va_end(args); + + pid = proc_spawn(argv); + + return pid; +} + +bool ttynvt_check() +{ + struct stat st; + bool ok; + + ok = stat(NVT_DEV, &st) == 0 && S_ISCHR(st.st_mode); + if (!ok) + { + fprintf(stderr, "\n%s" + "************************************************************\n" + "*** Invalid device file: '%s'\n" + "*** Ensure that ttynvt is launched correcly, e.g.:\n" + "# ttynvt -d -E -S localhost:1234 -n %s\n" + "*** ttynvt must run as root, and the device and server names\n" + "*** must be as shown above.\n" + "************************************************************\n" + "%s\n", COL_CYN, NVT_DEV, NVT_NAME, COL_RST); + } + return ok; +} + + + +TEST(Test1, t1) +{ + int err; + pid_t pid_srv, pid; + + ASSERT_TRUE(ttynvt_check()); + + pid_srv = proc_launch(NVT_SRV, "-de", "localhost:1234", NULL); + ASSERT_GT(pid_srv, 0); + + usleep(100000); + + pid = LAUNCH_NVTTEST(NULL); + ASSERT_GT(pid, 0); + + proc_waitfor("nvttest", pid); + + pr_info("Terminate nvtsrv"); + err = kill(pid_srv, SIGTERM); + EXPECT_EQ(err, 0); + + proc_waitfor("nvtsrv", pid_srv); +} + + + +/* Using fixture */ + +/**INDENT-OFF**/ +class Test2:public::testing::Test { + pid_t pid_srv; + + protected: + Test2() { } + virtual ~Test2() { } + + virtual void SetUp() { + + // Ensure ttynvt is running + ASSERT_TRUE(ttynvt_check()); + + // NB! Quiet nvtsrv + pid_srv = proc_launch(NVT_SRV, "-qe", "localhost:1234", NULL); + ASSERT_GT(pid_srv, 0); + + usleep(100000); + } + + virtual void TearDown() { + int err; + + pr_info("Terminate nvtsrv"); + err = kill(pid_srv, SIGTERM); + EXPECT_EQ(err, 0); + + proc_waitfor("nvtsrv", pid_srv); + } +}; +/**INDENT-ON**/ + +TEST_F(Test2, t1) +{ + pid_t pid; + + pid = LAUNCH_NVTTEST(NULL); + ASSERT_GT(pid, 0); + + proc_waitfor("nvttest", pid); +} + +// Multiple threads +TEST_F(Test2, t2) +{ + pid_t pid; + + // NB! Quiet nvttest + pid = LAUNCH_NVTTEST("-t5", "-q", NULL); + ASSERT_GT(pid, 0); + + proc_waitfor("nvttest", pid); +} + +// Multiple loops +TEST_F(Test2, t3) +{ + pid_t pid; + + // NB! Quiet nvttest + pid = LAUNCH_NVTTEST("-n5", "-q", NULL); + ASSERT_GT(pid, 0); + + proc_waitfor("nvttest", pid); +} + +// Multiple threads and loops +TEST_F(Test2, t4) +{ + pid_t pid; + + // NB! Quiet nvttest + pid = LAUNCH_NVTTEST("-n5", "-t5", "-q", NULL); + ASSERT_GT(pid, 0); + + proc_waitfor("nvttest", pid); +}