diff --git a/.gitignore b/.gitignore index 8103521d0..1be4c5204 100644 --- a/.gitignore +++ b/.gitignore @@ -47,4 +47,5 @@ rapidjson/ .idea .vscode build64/ -build2/ \ No newline at end of file +build2/ +default.profraw diff --git a/CMakeLists.txt b/CMakeLists.txt index 3da1ac2a6..cc4f55f8a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ option(JEMALLOC "use jemalloc. Not required on BSD" ) option(DEBIAN "build for debian" ) option(TESTNET "testnet build" ) option(WITH_SHARED "build shared library") +option(WITH_COVERAGE "generate coverage data") if(WIN32) set(CMAKE_CXX_STANDARD 17) @@ -44,6 +45,16 @@ add_compile_options(-Wvla) add_compile_options($<$:-fpermissive>) add_compile_options(-Wno-unused-function -Wno-deprecated-declarations -Wno-unknown-pragmas) +if (WITH_COVERAGE) + if (USING_CLANG) + add_compile_options( -fprofile-instr-generate -fcoverage-mapping ) + link_libraries( -fprofile-instr-generate ) + else() + add_compile_options( --coverage -g0 ) + link_libraries( --coverage ) + endif() +endif() + # these vars are set by the cmake toolchain spec if (WOW64_CROSS_COMPILE OR WIN64_CROSS_COMPILE) # dynamic linking does this all the time @@ -619,6 +630,7 @@ set(TEST_SRC test/test_llarp_dns.cpp test/test_llarp_dnsd.cpp test/test_llarp_encrypted_frame.cpp + test/test_llarp_router.cpp test/test_llarp_router_contact.cpp test/util/test_llarp_util_aligned.cpp test/util/test_llarp_util_bencode.cpp diff --git a/Makefile b/Makefile index 4798a360e..5e670c314 100644 --- a/Makefile +++ b/Makefile @@ -60,6 +60,9 @@ CLANG ?= OFF CROSS ?= OFF # build liblokinet-shared.so SHARED_LIB ?= ON +# enable generating coverage +COVERAGE ?= OFF +COVERAGE_OUTDIR ?= "$(TMPDIR)/lokinet-coverage" # cmake generator type CMAKE_GEN ?= Unix Makefiles @@ -71,6 +74,8 @@ CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake ANALYZE_CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "$(SCAN_BUILD) cmake -G'$(CMAKE_GEN)' -DCMAKE_CROSSCOMPILING=$(CROSS) -DUSING_CLANG=$(CLANG) -DSTATIC_LINK=$(STATIC_LINK) -DUSE_NETNS=$(NETNS) -DUSE_AVX2=$(AVX2) -DUSE_LIBABYSS=$(JSONRPC) -DNON_PC_TARGET=$(NON_PC_TARGET) -DWITH_SHARED=$(SHARED_LIB) '$(REPO)'") +COVERAGE_CONFIG_CMD = $(shell /bin/echo -n "cd '$(BUILD_ROOT)' && " ; /bin/echo -n "cmake -G'$(CMAKE_GEN)' -DCMAKE_CROSSCOMPILING=$(CROSS) -DUSING_CLANG=$(CLANG) -DSTATIC_LINK=$(STATIC_LINK) -DUSE_NETNS=$(NETNS) -DUSE_AVX2=$(AVX2) -DUSE_LIBABYSS=$(JSONRPC) -DNON_PC_TARGET=$(NON_PC_TARGET) -DWITH_SHARED=$(SHARED_LIB) -DWITH_COVERAGE=yes '$(REPO)'") + TARGETS = $(REPO)/lokinet SIGS = $(TARGETS:=.sig) EXE = $(BUILD_ROOT)/lokinet @@ -192,6 +197,21 @@ analyze-config: clean analyze: analyze-config cd '$(BUILD_ROOT)' && $(SCAN_BUILD) $(MAKE) +coverage-config: clean + mkdir -p '$(BUILD_ROOT)' + $(COVERAGE_CONFIG_CMD) + +coverage: coverage-config + $(MAKE) -C $(BUILD_ROOT) -j 12 + $(TEST_EXE) || true # continue even if tests fail + mkdir -p "$(COVERAGE_OUTDIR)" +ifeq ($(CLANG),OFF) + gcovr -r . --branches --html --html-details -o "$(COVERAGE_OUTDIR)/lokinet.html" +else + llvm-profdata merge default.profraw -output $(BUILD_ROOT)/profdata + llvm-cov show -format=html -output-dir="$(COVERAGE_OUTDIR)" -instr-profile "$(BUILD_ROOT)/profdata" "$(BUILD_ROOT)/testAll" $(shell find ./llarp -type f) +endif + lint: $(LINT_CHECK) %.cpp-check: %.cpp @@ -209,13 +229,13 @@ debian-configure: debian: debian-configure $(MAKE) -C '$(BUILD_ROOT)' - cp $(EXE) lokinet + cp $(EXE) lokinet cp $(BUILD_ROOT)/rcutil lokinet-rcutil debian-test: $(TEST_EXE) -install: +install: $(MAKE) -C '$(BUILD_ROOT)' install fuzz-configure: clean diff --git a/llarp/crypto.cpp b/llarp/crypto.cpp index 7be3d2ede..4c49583a6 100644 --- a/llarp/crypto.cpp +++ b/llarp/crypto.cpp @@ -26,7 +26,9 @@ namespace llarp std::ifstream f; f.open(fname, std::ios::binary); if(!f.is_open()) + { return false; + } size_t sz = 0; f.seekg(0, std::ios::end); sz = f.tellg(); @@ -41,7 +43,9 @@ namespace llarp byte_t tmp[128]; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); if(sz > sizeof(tmp)) + { return false; + } f.read((char*)tmp, sz); return BDecode(&buf); } @@ -52,7 +56,9 @@ namespace llarp byte_t tmp[128]; auto buf = llarp::StackBuffer< decltype(tmp) >(tmp); if(!BEncode(&buf)) + { return false; + } std::ofstream f; f.open(fname, std::ios::binary); diff --git a/llarp/ev/ev_kqueue.hpp b/llarp/ev/ev_kqueue.hpp index 8edd93382..ba3adfcf6 100644 --- a/llarp/ev/ev_kqueue.hpp +++ b/llarp/ev/ev_kqueue.hpp @@ -21,7 +21,7 @@ namespace llarp { - int + inline int tcp_conn::read(byte_t* buf, size_t sz) { if(sz == 0) @@ -53,14 +53,14 @@ namespace llarp return 0; } - void + inline void tcp_conn::flush_write() { connected(); ev_io::flush_write(); } - ssize_t + inline ssize_t tcp_conn::do_write(void* buf, size_t sz) { if(_shouldClose) @@ -74,7 +74,7 @@ namespace llarp #endif } - void + inline void tcp_conn::connect() { socklen_t slen = sizeof(sockaddr_in); @@ -100,7 +100,7 @@ namespace llarp } } - int + inline int tcp_serv::read(byte_t*, size_t) { int new_fd = ::accept(fd, nullptr, nullptr); diff --git a/readme.md b/readme.md index 914360a26..044c370b2 100644 --- a/readme.md +++ b/readme.md @@ -16,6 +16,7 @@ Build requirements: * CMake * C++ 17 capable C++ compiler * rapidjson (if enabling jsonrpc server) +* gcovr (if generating test coverage with gcc) ### Linux @@ -27,22 +28,22 @@ build: $ make -j8 install: - + $ sudo make install ### FreeBSD -build: +build: $ pkg install wget cmake git $ git clone https://github.com/loki-project/loki-network $ cd loki-network $ gmake -j8 - + install (root): - - # gmake install + + # gmake install ## Windows @@ -55,10 +56,10 @@ build (where `$ARCH` is your platform - `i686` or `x86_64`): $ cmake .. -DCMAKE_BUILD_TYPE=[Debug|Release] -DSTATIC_LINK=ON -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DDNS_PORT=53 -G 'Unix Makefiles' install (elevated) to `$PROGRAMFILES/lokinet` or `$ProgramFiles(x86)/lokinet`: - + $ make install -if cross-compiling, install mingw-w64 from your distro's package manager, or [build from source](https://sourceforge.net/p/mingw-w64/wiki2/Cross%20Win32%20and%20Win64%20compiler/), then: +if cross-compiling, install mingw-w64 from your distro's package manager, or [build from source](https://sourceforge.net/p/mingw-w64/wiki2/Cross%20Win32%20and%20Win64%20compiler/), then: $ mkdir -p build; cd build $ export COMPILER=clang # if using clang for windows diff --git a/test/test_llarp_router.cpp b/test/test_llarp_router.cpp new file mode 100644 index 000000000..0dde08de1 --- /dev/null +++ b/test/test_llarp_router.cpp @@ -0,0 +1,123 @@ + +#include +#include + +#include +#include + +#include + +std::string +randFilename() +{ + static const char alphabet[] = "abcdefghijklmnopqrstuvwxyz"; + + std::random_device rd; + std::uniform_int_distribution< size_t > dist{0, sizeof(alphabet) - 2}; + + std::string filename; + for(size_t i = 0; i < 5; ++i) + { + filename.push_back(alphabet[dist(rd)]); + } + + filename.push_back('.'); + + for(size_t i = 0; i < 5; ++i) + { + filename.push_back(alphabet[dist(rd)]); + } + + return filename; +} + +struct FileGuard +{ + const fs::path &p; + + explicit FileGuard(const fs::path &_p) : p(_p) + { + } + + ~FileGuard() + { + if(fs::exists(fs::status(p))) + { + fs::remove(p); + } + } +}; + +using FindOrCreateFunc = std::function< bool(llarp::Crypto *, const fs::path &, + llarp::SecretKey &) >; + +struct FindOrCreate : public ::testing::TestWithParam< FindOrCreateFunc > +{ + FindOrCreate() : crypto(llarp::Crypto::sodium{}) + { + } + + llarp::Crypto crypto; +}; + +// Concerns +// - file missing +// - file empty +// - happy path + +TEST_P(FindOrCreate, find_file_missing) +{ + // File missing. Should create a new file + llarp::SecretKey key; + fs::path p = randFilename(); + ASSERT_FALSE(fs::exists(fs::status(p))); + + FileGuard guard(p); + + ASSERT_TRUE(GetParam()(&crypto, p, key)); + ASSERT_TRUE(fs::exists(fs::status(p))); + ASSERT_FALSE(key.IsZero()); +} + +TEST_P(FindOrCreate, find_file_empty) +{ + // File empty. + llarp::SecretKey key; + fs::path p = randFilename(); + ASSERT_FALSE(fs::exists(fs::status(p))); + + std::fstream f; + f.open(p.string(), std::ios::out); + f.close(); + + FileGuard guard(p); + + ASSERT_FALSE(GetParam()(&crypto, p, key)); + // Verify we didn't delete an invalid file + ASSERT_TRUE(fs::exists(fs::status(p))); +} + +TEST_P(FindOrCreate, happy_path) +{ + // happy path. + llarp::SecretKey key; + fs::path p = randFilename(); + ASSERT_FALSE(fs::exists(fs::status(p))); + + std::ofstream f; + f.open(p.string(), std::ios::out); + std::fill_n(std::ostream_iterator< byte_t >(f), key.size(), 0x20); + f.close(); + + FileGuard guard(p); + + ASSERT_TRUE(GetParam()(&crypto, p, key)); + // Verify we didn't delete the file + ASSERT_TRUE(fs::exists(fs::status(p))); +} + +FindOrCreateFunc findOrCreateFunc[] = {llarp_findOrCreateEncryption, + llarp_findOrCreateIdentity}; + +INSTANTIATE_TEST_CASE_P(TestRouter, FindOrCreate, + ::testing::ValuesIn(findOrCreateFunc));