From 1c10872ea2b59ccbe9aee3229e6d357057ec4fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Blin?= <sebastien.blin@savoirfairelinux.com> Date: Mon, 12 Mar 2018 15:26:14 -0400 Subject: [PATCH] tests: add unit tests structure to opendht + Unit testing requires cppunit. + add options for CMake and Autotools to build opendht_unit_tests. + add sample tests for InfoHash + modify Dockerfiles and Travis build to run `make test` + Change lowbit signature to return an int instead of an unsigned. --- .gitignore | 4 + .travis.yml | 2 +- CMakeLists.txt | 27 ++++++- Makefile.am | 2 +- cmake/FindCppunit.cmake | 28 +++++++ configure.ac | 6 ++ docker/DockerfileDeps | 2 +- docker/DockerfileDepsLlvm | 4 +- docker/DockerfileTravis | 6 +- docker/DockerfileTravisLlvm | 6 +- docker/DockerfileTravisProxy | 3 + include/opendht/infohash.h | 2 +- tests/Makefile.am | 9 +++ tests/infohashtester.cpp | 145 +++++++++++++++++++++++++++++++++++ tests/infohashtester.h | 70 +++++++++++++++++ tests/tests_runner.cpp | 35 +++++++++ 16 files changed, 342 insertions(+), 9 deletions(-) create mode 100644 cmake/FindCppunit.cmake create mode 100644 tests/Makefile.am create mode 100644 tests/infohashtester.cpp create mode 100644 tests/infohashtester.h create mode 100644 tests/tests_runner.cpp diff --git a/.gitignore b/.gitignore index c79e0d15..0dd6ebd5 100644 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ Makefile.in # Python *.pyc +*.stamp python/opendht.cpp python/setup.py @@ -60,3 +61,6 @@ doc/Doxyfile # vim swap files *.swp *.swo + +# build dir +build diff --git a/.travis.yml b/.travis.yml index daaf24c0..de9c3720 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,7 +58,7 @@ script: else options+='-DOPENDHT_PUSH_NOTIFICATIONS=OFF '; fi - docker run opendht-proxy /bin/sh -c "cd /root/opendht && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=ON -DOPENDHT_LTO=ON $options .. && make -j8 && make install"; + docker run opendht-proxy /bin/sh -c "cd /root/opendht && mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=ON -DOPENDHT_LTO=ON -DOPENDHT_TESTS=ON $options .. && make -j8 && ./opendht_unit_tests && make install"; fi - | diff --git a/CMakeLists.txt b/CMakeLists.txt index cdd49bf7..443a9963 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,9 @@ option (OPENDHT_PROXY_SERVER "Enable DHT proxy server, use Restbed and jsoncpp" option (OPENDHT_PUSH_NOTIFICATIONS "Enable push notifications support" OFF) option (OPENDHT_PROXY_SERVER_IDENTITY "Allow clients to use the node identity" OFF) option (OPENDHT_PROXY_CLIENT "Enable DHT proxy client, use Restbed and jsoncpp" OFF) -option (OPENDHT_INDEX "Build DHT indexation feature" ON) +option (OPENDHT_INDEX "Build DHT indexation feature" OFF) +option (OPENDHT_TESTS "Add unit tests executable" OFF) + find_package(Doxygen) option (OPENDHT_DOCUMENTATION "Create and install the HTML based API documentation (requires Doxygen)" ${DOXYGEN_FOUND}) @@ -292,3 +294,26 @@ install (DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX}) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendht.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) install (EXPORT opendht DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht FILE opendhtConfig.cmake) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/opendhtConfigVersion.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/opendht) + +# Unit tests +IF(OPENDHT_TESTS) + FIND_PACKAGE(Cppunit REQUIRED) + # unit testing + add_executable(opendht_unit_tests + tests/tests_runner.cpp + tests/infohashtester.h + tests/infohashtester.cpp + ) + if (OPENDHT_SHARED) + TARGET_LINK_LIBRARIES(opendht_unit_tests opendht) + else () + TARGET_LINK_LIBRARIES(opendht_unit_tests opendht-static) + endif () + TARGET_LINK_LIBRARIES(opendht_unit_tests + ${CMAKE_THREAD_LIBS_INIT} + ${CPPUNIT_LIBRARIES} + ${GNUTLS_LIBRARIES} + ) + enable_testing() + add_test(TEST opendht_unit_tests) +ENDIF() diff --git a/Makefile.am b/Makefile.am index 37485650..d16800ad 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,7 +12,7 @@ if USE_CYTHON SUBDIRS += python endif -SUBDIRS += doc +SUBDIRS += doc tests ACLOCAL_AMFLAGS = -I m4 diff --git a/cmake/FindCppunit.cmake b/cmake/FindCppunit.cmake new file mode 100644 index 00000000..d734a73d --- /dev/null +++ b/cmake/FindCppunit.cmake @@ -0,0 +1,28 @@ +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_CPPUNIT "cppunit") + +FIND_PATH(CPPUNIT_INCLUDE_DIRS + NAMES cppunit/TestCase.h + HINTS ${PC_CPPUNIT_INCLUDE_DIR} + ${CMAKE_INSTALL_PREFIX}/include + PATHS + /usr/local/include + /usr/include +) + +FIND_LIBRARY(CPPUNIT_LIBRARIES + NAMES cppunit + HINTS ${PC_CPPUNIT_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + PATHS + ${CPPUNIT_INCLUDE_DIRS}/../lib + /usr/local/lib + /usr/lib +) + +LIST(APPEND CPPUNIT_LIBRARIES ${CMAKE_DL_LIBS}) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(CPPUNIT DEFAULT_MSG CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS) +MARK_AS_ADVANCED(CPPUNIT_LIBRARIES CPPUNIT_INCLUDE_DIRS) diff --git a/configure.ac b/configure.ac index 604d985e..26112757 100644 --- a/configure.ac +++ b/configure.ac @@ -182,6 +182,11 @@ AM_COND_IF([PROXY_CLIENT_OR_SERVER], [ LDFLAGS="${LDFLAGS} ${Jsoncpp_LIBS} -lrestbed" ], []) +AC_ARG_ENABLE([tests], AS_HELP_STRING([--enable-tests], [Enable tests]), build_tests=yes, build_tests=no) +AM_CONDITIONAL(OPENDHT_TESTS, test x$build_tests == xyes) +AM_COND_IF([OPENDHT_TESTS], [ + PKG_CHECK_MODULES([CppUnit], [cppunit >= 1.12]) +]) AC_CONFIG_FILES([doc/Doxyfile doc/Makefile]) @@ -197,5 +202,6 @@ AM_COND_IF([USE_CYTHON], [ AC_CONFIG_FILES([Makefile src/Makefile tools/Makefile + tests/Makefile opendht.pc]) AC_OUTPUT diff --git a/docker/DockerfileDeps b/docker/DockerfileDeps index ed0f5582..9b7dc466 100644 --- a/docker/DockerfileDeps +++ b/docker/DockerfileDeps @@ -1,3 +1,3 @@ FROM ubuntu:17.04 MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com> -RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev python3-setuptools && apt-get clean \ No newline at end of file +RUN apt-get update && apt-get install -y build-essential cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev libcppunit-dev python3-setuptools && apt-get clean diff --git a/docker/DockerfileDepsLlvm b/docker/DockerfileDepsLlvm index ab2dedc3..5dc9fb56 100644 --- a/docker/DockerfileDepsLlvm +++ b/docker/DockerfileDepsLlvm @@ -1,7 +1,7 @@ FROM ubuntu:17.04 MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com> RUN apt-get update \ - && apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev python3-setuptools \ + && apt-get install -y llvm llvm-dev clang make cmake git wget libncurses5-dev libreadline-dev nettle-dev libgnutls28-dev libuv1-dev libmsgpack-dev libargon2-0-dev cython3 python3-dev python3-setuptools libcppunit-dev \ && apt-get remove -y gcc g++ && apt-get autoremove -y && apt-get clean ENV CC cc -ENV CXX c++ \ No newline at end of file +ENV CXX c++ diff --git a/docker/DockerfileTravis b/docker/DockerfileTravis index 693827eb..840e4ccd 100644 --- a/docker/DockerfileTravis +++ b/docker/DockerfileTravis @@ -1,5 +1,9 @@ FROM aberaud/opendht-deps MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com> + +RUN apt-get install -y libcppunit-dev # temp while aberaud/opendht-deps doesn't have this + COPY . /root/opendht RUN cd /root/opendht && mkdir build && cd build \ - && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On .. && make -j8 && make install + && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_LTO=On -DOPENDHT_TESTS=ON .. \ + && make -j8 && ./opendht_unit_tests && make install diff --git a/docker/DockerfileTravisLlvm b/docker/DockerfileTravisLlvm index c32a9fd8..b4a443e6 100644 --- a/docker/DockerfileTravisLlvm +++ b/docker/DockerfileTravisLlvm @@ -1,5 +1,9 @@ FROM aberaud/opendht-deps-llvm MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com> + +RUN apt-get install -y libcppunit-dev # temp while aberaud/opendht-deps doesn't have this + COPY . /root/opendht RUN cd /root/opendht && mkdir build && cd build \ - && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On .. && make -j8 && make install + && cmake -DCMAKE_INSTALL_PREFIX=/usr -DOPENDHT_PYTHON=On -DOPENDHT_TESTS=ON .. \ + && make -j8 && ./opendht_unit_tests && make install diff --git a/docker/DockerfileTravisProxy b/docker/DockerfileTravisProxy index b543e61a..551217b7 100644 --- a/docker/DockerfileTravisProxy +++ b/docker/DockerfileTravisProxy @@ -1,3 +1,6 @@ FROM opendht-deps-proxy MAINTAINER Adrien Béraud <adrien.beraud@savoirfairelinux.com> + +RUN apt-get install -y libcppunit-dev # temp while aberaud/opendht-deps doesn't have this + COPY . /root/opendht diff --git a/include/opendht/infohash.h b/include/opendht/infohash.h index ade90dc3..e82613f5 100644 --- a/include/opendht/infohash.h +++ b/include/opendht/infohash.h @@ -129,7 +129,7 @@ public: * Find the lowest 1 bit in an id. * Result will allways be lower than 8*N */ - inline unsigned lowbit() const { + inline int lowbit() const { int i, j; for(i = N-1; i >= 0; i--) if(data_[i] != 0) diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 00000000..16660000 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,9 @@ +if OPENDHT_TESTS +bin_PROGRAMS = opendht_unit_tests + +AM_CPPFLAGS = -I../include + +opendht_unit_tests_SOURCES = tests_runner.cpp infohashtester.cpp +opendht_unit_tests_HEADERS = infohashtester.h +opendht_unit_tests_LDFLAGS = -lopendht -lcppunit -L@top_builddir@/src/.libs @Argon2_LDFLAGS@ @GnuTLS_LIBS@ +endif diff --git a/tests/infohashtester.cpp b/tests/infohashtester.cpp new file mode 100644 index 00000000..1d8f116c --- /dev/null +++ b/tests/infohashtester.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "infohashtester.h" + +// std +#include <iostream> +#include <string> + +// opendht +#include "infohash.h" + +namespace test { +CPPUNIT_TEST_SUITE_REGISTRATION(InfoHashTester); + +void +InfoHashTester::setUp() { + +} + +void +InfoHashTester::testConstructors() { + // Default constructor creates a null infohash + auto nullHash = dht::InfoHash(); + CPPUNIT_ASSERT(nullHash.size() == 20); + CPPUNIT_ASSERT(!nullHash); + // Build from a uint8_t. if length to short, should get a null infohash + uint8_t to_short[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}; + auto infohash = dht::InfoHash(to_short, 8); + CPPUNIT_ASSERT(infohash.size() == 20); + CPPUNIT_ASSERT_EQUAL(infohash.toString(), + std::string("0000000000000000000000000000000000000000")); + // Build from a uint8_t. if length is enough, data should contains the uint8_t + uint8_t enough[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}; + infohash = dht::InfoHash(enough, 20); + CPPUNIT_ASSERT(infohash.size() == 20); + const auto* data = infohash.data(); + for (auto i = 0; i < 20; ++i) { + CPPUNIT_ASSERT_EQUAL(enough[i], data[i]); + } + // if too long, should be cutted to 20 + uint8_t tooLong[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, + 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb0}; + infohash = dht::InfoHash(tooLong, 21); + CPPUNIT_ASSERT(infohash.size() == 20); + const auto* data2 = infohash.data(); + for (auto i = 0; i < 20; ++i) { + CPPUNIT_ASSERT_EQUAL(enough[i], data2[i]); + } + // Build from string + auto infohashFromStr = dht::InfoHash("0102030405060708090A0102030405060708090A"); + CPPUNIT_ASSERT(infohashFromStr.size() == 20); + const auto* dataStr = infohashFromStr.data(); + for (auto i = 0; i < 20; ++i) { + CPPUNIT_ASSERT_EQUAL((int)dataStr[i], (int)data[i]); + } +} + +void +InfoHashTester::testComperators() { + auto nullHash = dht::InfoHash(); + auto minHash = dht::InfoHash("0000000000000000000000000000000000111110"); + auto maxHash = dht::InfoHash("0111110000000000000000000000000000000000"); + // operator == + CPPUNIT_ASSERT_EQUAL(minHash, minHash); + CPPUNIT_ASSERT_EQUAL(minHash, dht::InfoHash("0000000000000000000000000000000000111110")); + CPPUNIT_ASSERT(!(minHash == maxHash)); + // operator != + CPPUNIT_ASSERT(!(minHash != minHash)); + CPPUNIT_ASSERT(!(minHash != dht::InfoHash("0000000000000000000000000000000000111110"))); + CPPUNIT_ASSERT(minHash != maxHash); + // operator< + CPPUNIT_ASSERT(nullHash < minHash); + CPPUNIT_ASSERT(nullHash < maxHash); + CPPUNIT_ASSERT(minHash < maxHash); + CPPUNIT_ASSERT(!(minHash < nullHash)); + CPPUNIT_ASSERT(!(maxHash < nullHash)); + CPPUNIT_ASSERT(!(maxHash < minHash)); + CPPUNIT_ASSERT(!(minHash < minHash)); + // bool() + CPPUNIT_ASSERT(maxHash); + CPPUNIT_ASSERT(!nullHash); + +} + +void +InfoHashTester::testLowBit() { + auto nullHash = dht::InfoHash(); + auto minHash = dht::InfoHash("0000000000000000000000000000000000000010"); + auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000"); + CPPUNIT_ASSERT_EQUAL(nullHash.lowbit(), -1); + CPPUNIT_ASSERT_EQUAL(minHash.lowbit(), 155); + CPPUNIT_ASSERT_EQUAL(maxHash.lowbit(), 7); +} + +void +InfoHashTester::testCommonBits() { + auto nullHash = dht::InfoHash(); + auto minHash = dht::InfoHash("0000000000000000000000000000000000000010"); + auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000"); + CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, nullHash), (unsigned)160); + CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, minHash), (unsigned)155); + CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(nullHash, maxHash), (unsigned)7); + CPPUNIT_ASSERT_EQUAL(dht::InfoHash::commonBits(minHash, maxHash), (unsigned)7); +} + +void +InfoHashTester::testXorCmp() { + auto nullHash = dht::InfoHash(); + auto minHash = dht::InfoHash("0000000000000000000000000000000000000010"); + auto maxHash = dht::InfoHash("0100000000000000000000000000000000000000"); + CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(nullHash, maxHash), -1); + CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(maxHash, nullHash), 1); + CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(minHash, maxHash), -1); + CPPUNIT_ASSERT_EQUAL(minHash.xorCmp(maxHash, minHash), 1); + CPPUNIT_ASSERT_EQUAL(nullHash.xorCmp(minHash, maxHash), -1); + CPPUNIT_ASSERT_EQUAL(nullHash.xorCmp(maxHash, minHash), 1); + // Because hashes are circular in distance. + CPPUNIT_ASSERT_EQUAL(maxHash.xorCmp(nullHash, minHash), -1); + CPPUNIT_ASSERT_EQUAL(maxHash.xorCmp(minHash, nullHash), 1); +} + +void +InfoHashTester::tearDown() { + +} +} // namespace test diff --git a/tests/infohashtester.h b/tests/infohashtester.h new file mode 100644 index 00000000..2558d3af --- /dev/null +++ b/tests/infohashtester.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2018 Savoir-faire Linux Inc. + * + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#pragma once + +// cppunit +#include <cppunit/TestFixture.h> +#include <cppunit/extensions/HelperMacros.h> + +namespace test { + +class InfoHashTester : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(InfoHashTester); + CPPUNIT_TEST(testConstructors); + CPPUNIT_TEST(testComperators); + CPPUNIT_TEST(testLowBit); + CPPUNIT_TEST(testCommonBits); + CPPUNIT_TEST(testXorCmp); + CPPUNIT_TEST_SUITE_END(); + + public: + /** + * Method automatically called before each test by CppUnit + */ + void setUp(); + /** + * Method automatically called after each test CppUnit + */ + void tearDown(); + /** + * Test the differents behaviors of constructors + */ + void testConstructors(); + /** + * Test compare operators + */ + void testComperators(); + /** + * Test lowbit method + */ + void testLowBit(); + /** + * Test commonBits method + */ + void testCommonBits(); + /** + * Test xorCmp operators + */ + void testXorCmp(); + +}; + +} // namespace test diff --git a/tests/tests_runner.cpp b/tests/tests_runner.cpp new file mode 100644 index 00000000..dffdc8c9 --- /dev/null +++ b/tests/tests_runner.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017-2018 Savoir-faire Linux Inc. + * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com> + * + * This program 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 3 of the License, or + * (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <cppunit/ui/text/TestRunner.h> +#include <cppunit/CompilerOutputter.h> +#include <iostream> + +int main(int argc, char** argv) { + CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + CppUnit::Test *suite = registry.makeTest(); + if (suite->countTestCases() == 0) { + std::cout << "No test cases specified for suite" << std::endl; + return 1; + } + CppUnit::TextUi::TestRunner runner; + runner.addTest(suite); + auto result = runner.run() ? 0 : 1; + return result; +} -- GitLab