diff --git a/.gitignore b/.gitignore index c79e0d153e35d6b063097a957b056815a9c8f10e..0dd6ebd5a1b1c6d937a86849046800be4b0be583 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 daaf24c01d39adb7cb75ee2eb727acfb5c3d2c6d..de9c3720416066a8c52efcde933ee7917cea23e4 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 cdd49bf72e26f7e042c8bec12d805ea7d4c36720..443a9963846813601463cb70cba6933f7c04a9e2 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 374856504330a1b9904ccd37a17dbef310fffd12..d16800ad7d144dc71f81773f9d3a058348739b4c 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 0000000000000000000000000000000000000000..d734a73dc56918dada84d91aa6816d9c0b0df12a --- /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 604d985e7bc704fef398bb7549259c6dab6dc4e0..2611275746b619722724f28a67e985f9eea0569d 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 ed0f55821420d6e9bda2a27e1ad70758704e52e3..9b7dc46621cc6bfc2ac08d97460140f42ca04445 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 ab2dedc390f86f965af2511c5908d06d86f6a7ec..5dc9fb564490643da97e93a91900e0878377c6fe 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 693827eb0dc0469e9fac2b57b3216de29f59c319..840e4ccd2b622c243ba6585c8a267b500ac2f9cd 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 c32a9fd8df95f649ee4463436a20458e6dd9a6bf..b4a443e6a55857de9869fe2ab39d34c28388e881 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 b543e61afba4947878732fd959019f84bbf0bc51..551217b7e676215a393e41f6d821c424f5aaca10 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 ade90dc30c333353650bce15f9e5c9e6046d1607..e82613f5c177310a5becd75253969fff3ef26593 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 0000000000000000000000000000000000000000..16660000032237f5fbcabc352b013a4a9049972c --- /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 0000000000000000000000000000000000000000..1d8f116c48562cb0431f27c5cb954ed350a7d485 --- /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 0000000000000000000000000000000000000000..2558d3afc6fcffeb0c2fb16a49875688ea30579c --- /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 0000000000000000000000000000000000000000..dffdc8c9ecd2c307f56bebf2c0bafa31d9a9cd50 --- /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; +}