test: Initial testing framework

Configure with --enable-testtools.

BLD_ROOT is now the top build directory.

Current test requires ttynvt to be started (by root) like
 # BLD_ROOT/src/ttynvt -d -E -S localhost:1234 -n ttyNVT99
Device and server name parameters must be as shown above.

Run tests like
 $ make -C BLD_ROOT test 	# Builds the other programs too
 $ make -C BLD_ROOT/test	# Only running tests
 $ make -C BLD_ROOT/test V=1 RUN_OPTS="--gtest_filter=Test1.t1"
This commit is contained in:
Kim Woelders
2024-11-08 06:58:55 +01:00
parent 55c5d0d226
commit 1f516288ab
7 changed files with 377 additions and 1 deletions
+48
View File
@@ -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)
+70
View File
@@ -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 <stdarg.h>
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);
}
+25
View File
@@ -0,0 +1,25 @@
#ifndef TEST_H
#define TEST_H 1
#include <gtest/gtest.h>
#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 */
+6
View File
@@ -0,0 +1,6 @@
#include "test.h"
TEST(Misc, dummy)
{
EXPECT_EQ(1, 1);
}
+216
View File
@@ -0,0 +1,216 @@
#include "test.h"
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/wait.h>
#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);
}