summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2020-06-06 19:44:36 +0200
committerBent Bisballe Nyeng <deva@aasimon.org>2020-06-06 19:44:36 +0200
commit540f839cb5868dac3eb409f509b85995826cf559 (patch)
tree42d2b9414edb748ad5d32efffcfeced18bf0b0b4
parentfa5985ed620c3cd4c7b9712b6b80a2e2c1a8ba31 (diff)
Add unit-test framework.
-rw-r--r--.gitignore17
-rw-r--r--Makefile.am4
-rw-r--r--configure.ac14
-rw-r--r--test/Makefile.am25
-rw-r--r--test/dgtest.cc37
-rw-r--r--test/dgunit.h241
-rw-r--r--test/logintest.cc (renamed from test/proto.cc)38
-rw-r--r--test/scopedfile.cc70
-rw-r--r--test/scopedfile.h42
-rwxr-xr-xtools/add_file25
10 files changed, 447 insertions, 66 deletions
diff --git a/.gitignore b/.gitignore
index bb8afab..6f306be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,16 +7,19 @@ config.h.in
config.log
config.status
configure
+compile
+config.h.in~
depcomp
install-sh
ltmain.sh
missing
-src/.deps/
-src/Makefile
-src/Makefile.am.test
-src/Makefile.in
+.deps/
+Makefile
+Makefile.in
src/muniad
-src/*.o
+*.o
+*.trs
stamp-h1
-tools/Makefile
-tools/Makefile.in
+test/*test
+test/*.xml
+test/*.log
diff --git a/Makefile.am b/Makefile.am
index 06d6b0e..905feab 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,3 @@
AUTOMAKE_OPTIONS = gnu
-SUBDIRS = src tools
-DISTDIRS = src tools
+SUBDIRS = src test tools
+DISTDIRS = src test tools
diff --git a/configure.ac b/configure.ac
index 4747405..3137788 100644
--- a/configure.ac
+++ b/configure.ac
@@ -32,12 +32,13 @@ dnl Check for getopt
dnl ======================
AC_HAVE_HEADERS(getopt.h)
-#AC_ARG_WITH(test, [ --with-test Build unit tests])
-#if test x$with_test == xyes; then
-# AC_MSG_WARN([*** Building unittests!])
-# AM_PATH_CPPUNIT(1.9.6)
-# AC_OUTPUT(test/Makefile)
-#fi
+dnl ======================
+dnl Compile unit tests
+dnl ======================
+AC_ARG_WITH([test],
+ AS_HELP_STRING([--with-test], [Build unit tests]))
+
+AM_CONDITIONAL([ENABLE_TESTS], [test "x$with_test" = "xyes"])
dnl ======================
dnl Check for eXpat library
@@ -72,5 +73,6 @@ PKG_CHECK_MODULES(LIBWEBSOCKETS, libwebsockets >= 2.1)
AC_OUTPUT(
Makefile
src/Makefile
+ test/Makefile
tools/Makefile)
diff --git a/test/Makefile.am b/test/Makefile.am
index 8cf4ce5..34b750d 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -1,13 +1,20 @@
# Rules for the test code (use `make check` to execute)
-TESTS = proto
+if ENABLE_TESTS
+
+TESTS = logintest
+
+EXTRA_DIST = \
+ dgunit.h \
+ scopedfile.h
check_PROGRAMS = $(TESTS)
-proto_CXXFLAGS = -DOUTPUT=\"proto\" $(CPPUNIT_CFLAGS) \
- -I$(top_srcdir)/src -I$(top_srcdir)/hugin -DDISABLE_HUGIN
-proto_CFLAGS = -DDISABLE_HUGIN
-proto_LDFLAGS = $(CPPUNIT_LIBS)
-proto_SOURCES = \
- $(top_srcdir)/hugin/hugin.c \
- test.cc \
- proto.cc
+logintest_CXXFLAGS = -DOUTPUT=\"logintest\" \
+ $(DEBUG_FLAGS) \
+ -I$(top_srcdir)/src
+logintest_LDFLAGS =
+logintest_SOURCES = \
+ logintest.cc \
+ dgtest.cc
+
+endif
diff --git a/test/dgtest.cc b/test/dgtest.cc
new file mode 100644
index 0000000..21e1543
--- /dev/null
+++ b/test/dgtest.cc
@@ -0,0 +1,37 @@
+/* -*- Mode: c++ -*- */
+/***************************************************************************
+ * dgtest.cc
+ *
+ * Sat Jun 16 15:48:36 CEST 2018
+ * Copyright 2018 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of DrumGizmo.
+ *
+ * DrumGizmo is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * DrumGizmo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with DrumGizmo; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#define DGUNIT_MAIN
+#include "dgunit.h"
+
+#include <fstream>
+
+int main(int argc, char* argv[])
+{
+ std::ofstream xmlfile;
+ xmlfile.open("result_" OUTPUT ".xml");
+ return DGUnit::runTests(xmlfile);
+}
diff --git a/test/dgunit.h b/test/dgunit.h
new file mode 100644
index 0000000..6717310
--- /dev/null
+++ b/test/dgunit.h
@@ -0,0 +1,241 @@
+/* -*- Mode: c++ -*- */
+/***************************************************************************
+ * dgunit.h
+ *
+ * Sat Jun 16 15:44:06 CEST 2018
+ * Copyright 2018 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of DrumGizmo.
+ *
+ * DrumGizmo is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * DrumGizmo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with DrumGizmo; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#pragma once
+
+#include <cstddef>
+#include <iostream>
+#include <list>
+#include <vector>
+#include <functional>
+#include <string>
+#include <sstream>
+#include <fstream>
+#include <cmath>
+
+class DGUnit
+{
+public:
+ DGUnit()
+ {
+ if(DGUnit::suite_list == nullptr)
+ {
+ DGUnit::suite_list = this;
+ return;
+ }
+
+ auto unit = DGUnit::suite_list;
+ while(unit->next_unit)
+ {
+ }
+ unit->next_unit = this;
+ }
+
+ //! Overload to prepare stuff for each of the tests.
+ virtual void setup() {}
+
+ //! Overload to tear down stuff for each of the tests.
+ virtual void teardown() {}
+
+ struct test_result
+ {
+ std::string func;
+ std::string file;
+ std::size_t line;
+ std::string msg;
+ int id;
+ };
+
+ //! Run test
+ //! \param test_suite the name of a test suite or null for all.
+ //! \param test_name the name of a test name inside a test suite. Only valid
+ //! if test_suite is non-null. nullptr for all tests.
+ static int runTests(std::ofstream& out)
+ {
+ std::size_t test_num{0};
+ std::size_t failed{0};
+
+ std::list<test_result> failed_tests;
+ std::list<test_result> successful_tests;
+
+ for(auto suite = DGUnit::suite_list; suite; suite = suite->next_unit)
+ {
+ for(auto test : suite->tests)
+ {
+ ++test_num;
+ try
+ {
+ suite->setup();
+ test.first();
+ suite->teardown();
+ }
+ catch(test_result& result)
+ {
+ std::cout << "F";
+ fflush(stdout);
+ result.id = test_num;
+ result.func = test.second;
+ failed_tests.push_back(result);
+ ++failed;
+ continue;
+ }
+ catch(...)
+ {
+ break; // Uncaught exception. Do not proceed with this test.
+ }
+ std::cout << ".";
+ fflush(stdout);
+ test_result result{test.second};
+ result.id = test_num;
+ successful_tests.push_back(result);
+ }
+ }
+
+ out << "<?xml version=\"1.0\" encoding='ISO-8859-1' standalone='yes' ?>\n";
+ out << "<TestRun>\n";
+ out << " <FailedTests>\n";
+ for(auto test : failed_tests)
+ {
+ out << " <FailedTest id=\"" << test.id << "\">\n";
+ out << " <Name>" << sanitize(test.func) << "</Name>\n";
+ out << " <FailureType>Assertion</FailureType>\n";
+ out << " <Location>\n";
+ out << " <File>" << sanitize(test.file) << "</File>\n";
+ out << " <Line>" << test.line << "</Line>\n";
+ out << " </Location>\n";
+ out << " <Message>" << sanitize(test.msg) << "</Message>\n";
+ out << " </FailedTest>\n";
+ }
+ out << " </FailedTests>\n";
+ out << " <SuccessfulTests>\n";
+ for(auto test : successful_tests)
+ {
+ out << " <Test id=\"" << test.id << "\">\n";
+ out << " <Name>" << sanitize(test.func) << "</Name>\n";
+ out << " </Test>\n";
+
+ }
+ out << " </SuccessfulTests>\n";
+ out << " <Statistics>\n";
+ out << " <Tests>" << (successful_tests.size() + failed_tests.size()) << "</Tests>\n";
+ out << " <FailuresTotal>" << failed_tests.size() << "</FailuresTotal>\n";
+ out << " <Errors>0</Errors>\n";
+ out << " <Failures>" << failed_tests.size() << "</Failures>\n";
+ out << " </Statistics>\n";
+ out << "</TestRun>\n";
+
+ return failed == 0 ? 0 : 1;
+ }
+
+protected:
+ template<typename O, typename F>
+ void registerTest(O* obj, const F& fn, const char* name)
+ {
+ tests.emplace_back(std::make_pair(std::bind(fn, obj), name));
+ }
+ #define DGUNIT_TEST(func) \
+ registerTest(this, &func, #func)
+
+ void dg_assert(bool value, const char* expr,
+ const char* file, std::size_t line)
+ {
+ if(!value)
+ {
+ std::stringstream ss;
+ ss << "assertion failed\n"
+ "- Expression: " << expr << "\n";
+ throw test_result{"", file, line, ss.str()};
+ }
+ }
+ //! Convenience macro to pass along filename and linenumber
+ #define DGUNIT_ASSERT(value) \
+ dg_assert(value, #value, __FILE__, __LINE__)
+
+ void assert_equal(double expected, double value,
+ const char* file, std::size_t line)
+ {
+ if(std::fabs(expected - value) > 0.0000001)
+ {
+ std::stringstream ss;
+ ss << "equality assertion failed\n"
+ "- Expected: " << expected << "\n"
+ "- Actual : " << value << "\n";
+ throw test_result{"", file, line, ss.str()};
+ }
+ }
+ template<typename T>
+ void assert_equal(T expected, T value,
+ const char* file, std::size_t line)
+ {
+ if(expected != value)
+ {
+ std::stringstream ss;
+ ss << "equality assertion failed\n"
+ "- Expected: " << expected << "\n"
+ "- Actual : " << value << "\n";
+ throw test_result{"", file, line, ss.str()};
+ }
+ }
+ //! Convenience macro to pass along filename and linenumber
+ #define DGUNIT_ASSERT_EQUAL(expected, value) \
+ assert_equal(expected, value, __FILE__, __LINE__)
+
+private:
+ static std::string sanitize(const std::string& input)
+ {
+ std::string output;
+ for(auto c : input)
+ {
+ switch(c)
+ {
+ case '"':
+ output += "&quot;";
+ break;
+ case '&':
+ output += "&amp;";
+ break;
+ case '<':
+ output += "&lt;";
+ break;
+ case '>':
+ output += "&gt;";
+ break;
+ default:
+ output += c;
+ break;
+ }
+ }
+ return output;
+ }
+
+ static DGUnit* suite_list;
+ DGUnit* next_unit{nullptr};
+ std::vector<std::pair<std::function<void()>, const char*>> tests;
+};
+
+#ifdef DGUNIT_MAIN
+DGUnit* DGUnit::suite_list{nullptr};
+#endif
diff --git a/test/proto.cc b/test/logintest.cc
index 0cc8da9..f4dc192 100644
--- a/test/proto.cc
+++ b/test/logintest.cc
@@ -1,44 +1,46 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set et sw=2 ts=2: */
/***************************************************************************
- * engine.cc
+ * logintest.cc
*
- * Fri Nov 29 18:09:02 CET 2013
- * Copyright 2013 Bent Bisballe Nyeng
+ * Sat Jun 6 19:32:12 CEST 2020
+ * Copyright 2020 Bent Bisballe Nyeng
* deva@aasimon.org
****************************************************************************/
/*
- * This file is part of DrumGizmo.
+ * This file is part of Munia.
*
- * DrumGizmo is free software; you can redistribute it and/or modify
+ * Munia is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
- * DrumGizmo is distributed in the hope that it will be useful,
+ * Munia is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with DrumGizmo; if not, write to the Free Software
+ * along with Munia; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
-#include <cppunit/extensions/HelperMacros.h>
+#include "dgunit.h"
-class test_proto_class : public CppUnit::TestFixture
+class LoginTest
+ : public DGUnit
{
- CPPUNIT_TEST_SUITE(test_proto_class);
- CPPUNIT_TEST(test_proto);
- CPPUNIT_TEST_SUITE_END();
-
public:
- void setUp() {}
- void tearDown() {}
- void test_proto() {
+ LoginTest()
+ {
+ DGUNIT_TEST(LoginTest::test);
+ }
+
+ void test()
+ {
+ DGUNIT_ASSERT_EQUAL(42, 42);
}
};
// Registers the fixture into the 'registry'
-CPPUNIT_TEST_SUITE_REGISTRATION(test_proto_class);
-
+static LoginTest test;
diff --git a/test/scopedfile.cc b/test/scopedfile.cc
new file mode 100644
index 0000000..b03a2bc
--- /dev/null
+++ b/test/scopedfile.cc
@@ -0,0 +1,70 @@
+/* -*- Mode: c++ -*- */
+/***************************************************************************
+ * scopedfile.cc
+ *
+ * Wed Jun 6 15:15:31 CEST 2018
+ * Copyright 2018 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of DrumGizmo.
+ *
+ * DrumGizmo is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * DrumGizmo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with DrumGizmo; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#include "scopedfile.h"
+
+#include <cstdlib>
+#include <unistd.h>
+
+#include <cpp11fix.h>
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#endif
+
+struct Pimpl
+{
+ std::string filename;
+ int fd;
+};
+
+ScopedFile::ScopedFile(const std::string& data)
+ : pimpl(std::make_unique<struct Pimpl>())
+{
+#ifndef _WIN32
+ char templ[] = "/tmp/dg-scoped-file-XXXXXX"; // buffer for filename
+ pimpl->fd = mkstemp(templ);
+#else
+ char templ[] = "dg-scoped-file-XXXXXX"; // buffer for filename
+ _mktemp_s(templ);
+ pimpl->fd = open(templ);
+#endif
+ pimpl->filename = templ;
+ auto sz = write(pimpl->fd, data.data(), data.size());
+ (void)sz;
+ close(pimpl->fd);
+}
+
+ScopedFile::~ScopedFile()
+{
+ unlink(pimpl->filename.data());
+}
+
+std::string ScopedFile::filename() const
+{
+ return pimpl->filename;
+}
diff --git a/test/scopedfile.h b/test/scopedfile.h
new file mode 100644
index 0000000..d66eac4
--- /dev/null
+++ b/test/scopedfile.h
@@ -0,0 +1,42 @@
+/* -*- Mode: c++ -*- */
+/***************************************************************************
+ * scopedfile.h
+ *
+ * Wed Jun 6 15:15:31 CEST 2018
+ * Copyright 2018 Bent Bisballe Nyeng
+ * deva@aasimon.org
+ ****************************************************************************/
+
+/*
+ * This file is part of DrumGizmo.
+ *
+ * DrumGizmo is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * DrumGizmo is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with DrumGizmo; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+#pragma once
+
+#include <string>
+#include <memory>
+
+class ScopedFile
+{
+public:
+ ScopedFile(const std::string& data);
+ ~ScopedFile();
+
+ std::string filename() const;
+
+private:
+ std::unique_ptr<struct Pimpl> pimpl;
+};
diff --git a/tools/add_file b/tools/add_file
index d9deb50..e86c117 100755
--- a/tools/add_file
+++ b/tools/add_file
@@ -62,34 +62,11 @@ function ccfile() {
echo -n $hf >> $1;
echo '"' >> $1;
echo '' >> $1;
-
- local hn=`echo $1 | cut -d'.' -f1 | tr 'a-z.' 'A-Z_'`
- echo "#ifdef TEST_${hn}" >> $1;
- echo "//Additional dependency files" >> $1;
- echo "//deps:" >> $1;
- echo "//Required cflags (autoconf vars may be used)" >> $1;
- echo "//cflags:" >> $1;
- echo "//Required link options (autoconf vars may be used)" >> $1;
- echo "//libs:" >> $1;
- echo "#include \"test.h\"" >> $1;
- echo "" >> $1;
- echo "TEST_BEGIN;" >> $1;
- echo "" >> $1;
- echo "// TODO: Put some testcode here (see test.h for usable macros)." >> $1;
- echo "TEST_TRUE(false, \"No tests yet!\");" >> $1;
- echo "" >> $1;
- echo "TEST_END;" >> $1;
- echo "" >> $1;
- echo "#endif/*TEST_${hn}*/" >> $1;
}
function hfile() {
allfile $1;
- local hn=`echo $1 | tr 'a-z.' 'A-Z_'`
- local pr=`echo $PROJECT | tr 'a-z.' 'A-Z_'`
- echo "#ifndef __${pr}_${hn}__" >> $1;
- echo "#define __${pr}_${hn}__" >> $1;
- echo "#endif/*__${pr}_${hn}__*/" >> $1;
+ echo "#pragma once" >> $1;
}
if [ "$#" = "1" ]; then