Browse Source

Add integration test framework (with 2 tests).

Run integration tests using "make check-igr". This complements the unit
tests ("make check").
Davin McCall 4 years ago
parent
commit
82f06b95ff

+ 5 - 1
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
 

+ 4 - 0
src/Makefile

@@ -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)

+ 10 - 0
src/igr-tests/Makefile

@@ -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

+ 4 - 0
src/igr-tests/basic/basic.sh

@@ -0,0 +1,4 @@
+#!/bin/sh
+# basic test; record our run
+
+echo "ran" > ./basic-ran

+ 16 - 0
src/igr-tests/basic/run-test.sh

@@ -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

+ 2 - 0
src/igr-tests/basic/sd/basic

@@ -0,0 +1,2 @@
+type = process
+command = ./basic.sh

+ 3 - 0
src/igr-tests/environ/checkenv.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "$TEST_VAR_ONE" >> ./env-record

+ 1 - 0
src/igr-tests/environ/environment1

@@ -0,0 +1 @@
+TEST_VAR_ONE=hello

+ 1 - 0
src/igr-tests/environ/environment2

@@ -0,0 +1 @@
+TEST_VAR_ONE=goodbye

+ 21 - 0
src/igr-tests/environ/run-test.sh

@@ -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

+ 2 - 0
src/igr-tests/environ/sd/checkenv

@@ -0,0 +1,2 @@
+type = process
+command = ./checkenv.sh

+ 116 - 0
src/igr-tests/igr-runner.cc

@@ -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;
+}