Add integration test framework (with 2 tests).
authorDavin McCall <davmac@davmac.org>
Fri, 5 Jul 2019 11:03:11 +0000 (21:03 +1000)
committerDavin McCall <davmac@davmac.org>
Fri, 5 Jul 2019 11:19:39 +0000 (21:19 +1000)
Run integration tests using "make check-igr". This complements the unit
tests ("make check").

12 files changed:
Makefile
src/Makefile
src/igr-tests/Makefile [new file with mode: 0644]
src/igr-tests/basic/basic.sh [new file with mode: 0755]
src/igr-tests/basic/run-test.sh [new file with mode: 0755]
src/igr-tests/basic/sd/basic [new file with mode: 0644]
src/igr-tests/environ/checkenv.sh [new file with mode: 0755]
src/igr-tests/environ/environment1 [new file with mode: 0644]
src/igr-tests/environ/environment2 [new file with mode: 0644]
src/igr-tests/environ/run-test.sh [new file with mode: 0755]
src/igr-tests/environ/sd/checkenv [new file with mode: 0644]
src/igr-tests/igr-runner.cc [new file with mode: 0644]

index 2f9b70439907d226500f52712022f9e56695d3c6..faa146c6d1666f11a7318e8be0b0381b8ea55cb2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,12 +3,16 @@
 all:
        $(MAKE) -C src all
        @echo "***"
-       @echo "*** Build complete; use \"make check\" to run tests or \"make install\" to install."
+       @echo "*** Build complete; use \"make check\" to run unit tests, \"make check-igr\" for"
+       @echo "*** integration tests, or \"make install\" to install."
        @echo "***"
 
 check:
        $(MAKE) -C src check
 
+check-igr:
+       $(MAKE) -C src check-igr
+
 run-cppcheck:
        $(MAKE) -C src run-cppcheck
 
index edd9cce5ee8e03cf6cfb6cf4c5f1fe3b223a7a38..9b1ca1efaf0753ceb07d7b34cf849600d28d0a3c 100644 (file)
@@ -50,6 +50,9 @@ $(objects): %.o: %.cc
 check:
        $(MAKE) -C tests check
 
+check-igr: dinit dinitctl
+       $(MAKE) -C igr-tests check-igr
+
 run-cppcheck:
        cppcheck --std=c++11 -Iincludes -Idasynq --force --enable=all *.cc 2>../cppcheck-report.txt
 
@@ -66,5 +69,6 @@ clean:
        rm -f dinit dinitctl shutdown mconfig-gen
        rm -f includes/mconfig.h
        $(MAKE) -C tests clean
+       $(MAKE) -C igr-tests clean
 
 -include $(objects:.o=.d)
diff --git a/src/igr-tests/Makefile b/src/igr-tests/Makefile
new file mode 100644 (file)
index 0000000..87719e5
--- /dev/null
@@ -0,0 +1,10 @@
+include ../../mconfig
+
+check-igr: igr-runner
+       ./igr-runner
+
+igr-runner: igr-runner.cc
+       $(CXX) $(CXXOPTS) igr-runner.cc -o igr-runner
+
+clean:
+       rm -f igr-runner basic/basic-ran environ/env-record
diff --git a/src/igr-tests/basic/basic.sh b/src/igr-tests/basic/basic.sh
new file mode 100755 (executable)
index 0000000..a104130
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/sh
+# basic test; record our run
+
+echo "ran" > ./basic-ran
diff --git a/src/igr-tests/basic/run-test.sh b/src/igr-tests/basic/run-test.sh
new file mode 100755 (executable)
index 0000000..09c9e14
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+rm -f ./basic-ran
+
+../../dinit -d sd -u -p socket -q \
+       basic
+
+STATUS=FAIL
+if [ -e basic-ran ]; then
+   if [ "$(cat basic-ran)" = "ran" ]; then
+       STATUS=PASS
+   fi
+fi
+
+if [ $STATUS = PASS ]; then exit 0; fi
+exit 1
diff --git a/src/igr-tests/basic/sd/basic b/src/igr-tests/basic/sd/basic
new file mode 100644 (file)
index 0000000..808dbe3
--- /dev/null
@@ -0,0 +1,2 @@
+type = process
+command = ./basic.sh
diff --git a/src/igr-tests/environ/checkenv.sh b/src/igr-tests/environ/checkenv.sh
new file mode 100755 (executable)
index 0000000..e73d787
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "$TEST_VAR_ONE" >> ./env-record
diff --git a/src/igr-tests/environ/environment1 b/src/igr-tests/environ/environment1
new file mode 100644 (file)
index 0000000..a4b22c2
--- /dev/null
@@ -0,0 +1 @@
+TEST_VAR_ONE=hello
diff --git a/src/igr-tests/environ/environment2 b/src/igr-tests/environ/environment2
new file mode 100644 (file)
index 0000000..8f4b8e3
--- /dev/null
@@ -0,0 +1 @@
+TEST_VAR_ONE=goodbye
diff --git a/src/igr-tests/environ/run-test.sh b/src/igr-tests/environ/run-test.sh
new file mode 100755 (executable)
index 0000000..7803528
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+rm -f ./env-record
+
+../../dinit -d sd -u -p socket -q \
+        -e environment1 \
+       checkenv
+
+../../dinit -d sd -u -p socket -q \
+        -e environment2 \
+       checkenv
+
+STATUS=FAIL
+if [ -e env-record ]; then
+   if [ "$(cat env-record)" = "$(echo hello; echo goodbye)" ]; then
+       STATUS=PASS
+   fi
+fi
+
+if [ $STATUS = PASS ]; then exit 0; fi
+exit 1
diff --git a/src/igr-tests/environ/sd/checkenv b/src/igr-tests/environ/sd/checkenv
new file mode 100644 (file)
index 0000000..a72a00f
--- /dev/null
@@ -0,0 +1,2 @@
+type = process
+command = ./checkenv.sh
diff --git a/src/igr-tests/igr-runner.cc b/src/igr-tests/igr-runner.cc
new file mode 100644 (file)
index 0000000..8d30bbc
--- /dev/null
@@ -0,0 +1,116 @@
+#include <string>
+#include <iostream>
+#include <cstring>
+
+#include <spawn.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+// Integration test suite runner.
+
+int main(int argc, char **argv)
+{
+    const char * const test_dirs[] = { "basic", "environ" };
+    constexpr int num_tests = sizeof(test_dirs) / sizeof(test_dirs[0]);
+
+    int passed = 0;
+    int skipped = 0;
+    int failed = 0;
+
+    bool aborted_run = false;
+
+    std::cout << "============== INTEGRATION TESTS =====================" << std::endl;
+
+    for (int i = 0; i < num_tests; i++) {
+        const char * test_dir = test_dirs[i];
+
+        std::string prog_path = "./run-test.sh";
+        char * const p_argv[2] = { const_cast<char *>(prog_path.c_str()), nullptr };
+
+        std::cout << test_dir << "... ";
+
+        // "Use posix_spawn", they said. "It will be easy", they said.
+
+        if (chdir(test_dir) != 0) {
+            std::cerr << "Couldn't chdir: " << test_dir << ": " << strerror(errno) << std::endl;
+            continue;
+        }
+
+        posix_spawn_file_actions_t p_actions;
+        posix_spawnattr_t p_attr;
+
+        if (posix_spawn_file_actions_init(&p_actions) != 0) {
+            // out of memory?
+            std::cerr << "Error launching process: " << test_dir << "/run-test.sh: " << strerror(errno) << std::endl;
+            aborted_run = true;
+            break;
+        }
+
+        if (posix_spawnattr_init(&p_attr) != 0) {
+            // out of memory?
+            std::cerr << "Error launching process: " << test_dir << "/run-test.sh: " << strerror(errno) << std::endl;
+            aborted_run = true;
+            break;
+        }
+
+        pid_t subproc_pid;
+        if (posix_spawn(&subproc_pid, prog_path.c_str(), &p_actions, &p_attr, p_argv, environ) != 0) {
+            // fail out
+            std::cerr << "Failed to run run-test.sh in " << test_dir << std::endl;
+            continue;
+        }
+
+        int wstatus;
+        if (waitpid(subproc_pid, &wstatus, 0) == -1) {
+            std::cout << "(unknown)" << std::endl;
+            std::cerr << "waitpid() failed" << std::endl;
+            aborted_run = true;
+            break;
+        }
+
+        if (WIFEXITED(wstatus)) {
+            if (WEXITSTATUS(wstatus) == 0) {
+                std::cout << "PASSED" << std::endl;
+                passed++;
+            }
+            else if (WEXITSTATUS(wstatus) == 1) {
+                std::cout << "FAILED" << std::endl;
+                failed++;
+            }
+            else if (WEXITSTATUS(wstatus) == 2) {
+                std::cout << "SKIPPED" << std::endl;
+                skipped++;
+            }
+            else {
+                std::cout << "???" << std::endl;
+            }
+        }
+        else {
+            std::cout << "*** terminated abnormally ***" << std::endl;
+            aborted_run = true;
+            break;
+        }
+
+        posix_spawnattr_destroy(&p_attr);
+        posix_spawn_file_actions_destroy(&p_actions);
+        chdir("..");
+    }
+
+    std::cout << "======================================================" << std::endl;
+
+    if (! aborted_run) {
+        std::cout << "Test run finished.\n"
+                "Passed: " << passed << "\n"
+                "Failed: " << failed;
+        if (failed != 0) {
+            std::cout << " XXX";
+        }
+        std::cout << "\n"
+                "Skipped: " << skipped << std::endl;
+    }
+    else {
+        std::cout << "Test run aborted." << std::endl;
+    }
+
+    return failed == 0 ? 0 : 1;
+}