diff --git a/CMakeLists.txt b/CMakeLists.txt index a473005..e710cdb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,8 @@ cmake_minimum_required( VERSION 3.5 ) project(archive_cpp_wrapper) +include(CTest) + set(CMAKE_CXX_STANDARD 11) find_package(LibArchive) @@ -88,3 +90,6 @@ install( # alias target add_library(LibArchive::archive_cpp_wrapper ALIAS ${PROJECT_NAME}) + +add_subdirectory(test) + diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..38a71e1 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,51 @@ +#BSD 2-Clause license +# +#Copyright (c) 2014, Domen Vrankar +#Copyright (c) 2021, Tobias Frost +#All rights reserved. +# +#Redistribution and use in source and binary forms, with or without modification, +#are permitted provided that the following conditions are met: +# +#1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +#2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +#ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +#ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +find_package(GTest) + +if(GTEST_FOUND) + add_executable(libarchive_cpp_wrapper_test) + + target_sources(libarchive_cpp_wrapper_test + PRIVATE + test.cpp + ) + + target_link_libraries(libarchive_cpp_wrapper_test + PRIVATE + LibArchive::archive_cpp_wrapper + GTest::gtest_main + ) + + add_test(NAME libarchive_cpp_wrapper_test COMMAND libarchive_cpp_wrapper_test) + + # this avoids that the test fails if it is not built.(cmake will say "not run", not "failed") + set_tests_properties(libarchive_cpp_wrapper_test PROPERTIES REQUIRED_FILES "$") + + set_tests_properties(libarchive_cpp_wrapper_test + PROPERTIES ENVIRONMENT "TEST_RESOURCES=${CMAKE_CURRENT_SOURCE_DIR}/resources/") +endif() diff --git a/test/resources/README b/test/resources/README new file mode 100644 index 0000000..018a320 --- /dev/null +++ b/test/resources/README @@ -0,0 +1,11 @@ +The resources in this directory are generated with : + +# test1.tar.gz + +dd if=/dev/urandom of=file1_random bs=1 count=16k +dd if=/dev/urandom of=file1_random bs=1 count=16k +dd if=/dev/zero of=file3_zero bs=1 count=16k + +tar czf test1.tar.gz file* + + diff --git a/test/resources/dir/file1_random b/test/resources/dir/file1_random new file mode 100644 index 0000000..0a872de Binary files /dev/null and b/test/resources/dir/file1_random differ diff --git a/test/resources/dir/file2_random b/test/resources/dir/file2_random new file mode 100644 index 0000000..6d2b522 Binary files /dev/null and b/test/resources/dir/file2_random differ diff --git a/test/resources/dir/file3_zeros b/test/resources/dir/file3_zeros new file mode 100644 index 0000000..294f401 Binary files /dev/null and b/test/resources/dir/file3_zeros differ diff --git a/test/resources/file1_random b/test/resources/file1_random new file mode 100644 index 0000000..0a872de Binary files /dev/null and b/test/resources/file1_random differ diff --git a/test/resources/file2_random b/test/resources/file2_random new file mode 100644 index 0000000..6d2b522 Binary files /dev/null and b/test/resources/file2_random differ diff --git a/test/resources/file3_zeros b/test/resources/file3_zeros new file mode 100644 index 0000000..294f401 Binary files /dev/null and b/test/resources/file3_zeros differ diff --git a/test/resources/test1.tar.gz b/test/resources/test1.tar.gz new file mode 100644 index 0000000..e30e217 Binary files /dev/null and b/test/resources/test1.tar.gz differ diff --git a/test/resources/test2.tar.gz b/test/resources/test2.tar.gz new file mode 100644 index 0000000..de56711 Binary files /dev/null and b/test/resources/test2.tar.gz differ diff --git a/test/test.cpp b/test/test.cpp new file mode 100644 index 0000000..c564ff5 --- /dev/null +++ b/test/test.cpp @@ -0,0 +1,231 @@ +/* + * testsuite for libarchive_cpp_wrapper + * + * Created on: Feb 8, 2021 + * Author: coldtobi + * LICENSE is BSD-2 clause. + */ + +#include "archive_reader.hpp" +#include "archive_writer.hpp" +#include "archive_exception.hpp" + +#include +#include +#include +#include +#include +#include + +namespace ar = ns_archive::ns_reader; + +class LibArchiveWrapperTest : public testing::Test { +public: + LibArchiveWrapperTest() = default; + virtual ~LibArchiveWrapperTest() { + if (!HasFailure()) { + for (auto const &x : cleanup_files) { + ::unlink(x.c_str()); + } + } + } + + // Let the test know about archives to de deleted after the test + // (will only be deleted if it succeded, not on failures.) + void deleteAfterTest(const std::string &cleanup) { + cleanup_files.insert(cleanup); + } + + std::string getResourceDir() { + auto resources = ::getenv("TEST_RESOURCES"); + if(!resources) return ""; + std::string ret(resources); + if(ret.size() && (ret.at(ret.size()-1) != '/')) { ret += '/'; } + return ret; + } + + std::string getcwd() { + auto cwd = ::getcwd(nullptr, 0); + if(!cwd) { return "/tmp/"; } + std::string ret(cwd); + if(ret.size() && (ret.at(ret.size()-1) != '/')) { ret += '/'; } + free(cwd); + return ret; + } + +protected: + std::set cleanup_files; + +}; + +TEST_F(LibArchiveWrapperTest, TestReadSimpleArchive) { + // Reading an archive. + auto dutarchive = getResourceDir() + "test1.tar.gz"; + + std::cerr << dutarchive << std::endl; + + std::set expected_files{"file1_random", "file2_random", "file3_zeros"}; + try + { + std::ifstream fs(dutarchive); + ns_archive::reader reader = ns_archive::reader::make_reader(fs, 32000); + + for(auto entry : reader) + { + std::string filename = entry->get_header_value_pathname(); + std::string comparefile = getResourceDir() + filename; + bool isdirectory = (filename.size() && filename[filename.size()-1] == '/'); + + auto it = expected_files.find(filename); + ASSERT_TRUE(it != expected_files.end()); + struct stat sb; + auto statret = stat(comparefile.c_str(),&sb); + EXPECT_EQ(0, statret); + + EXPECT_TRUE(S_ISDIR(sb.st_mode) == isdirectory); + + if (!isdirectory) { + EXPECT_EQ(sb.st_size, entry->get_header_value_size()); + auto original = std::ifstream(comparefile, std::ios::in | std::ios::binary); + auto *buf = new char[sb.st_size]; + auto *buf2 = new char[sb.st_size]; + auto &entrystream = entry->get_stream(); + original.read(buf, sb.st_size); + entrystream.read(buf2, sb.st_size); + EXPECT_TRUE(original.good()); + EXPECT_TRUE(entrystream.good()); + EXPECT_EQ(0,::memcmp(buf, buf2, sb.st_size)); + delete[] buf, buf2; + } + expected_files.erase(filename); + } + } + catch(ns_archive::archive_exception& e) + { + ADD_FAILURE() << e.what(); + } + + EXPECT_EQ(0, expected_files.size()); +} + + +TEST_F(LibArchiveWrapperTest, TestReadDirectoryArchive) { + // Reading an archive. + auto dutarchive = getResourceDir() + "test2.tar.gz"; + + std::set expected_files{"dir/", "dir/file1_random", "dir/file2_random", "dir/file3_zeros"}; + + try + { + std::ifstream fs(dutarchive); + ns_archive::reader reader = ns_archive::reader::make_reader(fs, 32000); + + for(auto entry : reader) + { + std::string filename = entry->get_header_value_pathname(); + std::string comparefile = getResourceDir() + filename; + + bool isdirectory = (filename.size() && filename[filename.size()-1] == '/'); + + auto it = expected_files.find(filename); + ASSERT_TRUE(it != expected_files.end()); + struct stat sb; + auto statret = stat(comparefile.c_str(),&sb); + EXPECT_EQ(0, statret); + + EXPECT_TRUE(S_ISDIR(sb.st_mode) == isdirectory); + + if (!isdirectory) { + EXPECT_EQ(sb.st_size, entry->get_header_value_size()); + auto original = std::ifstream(comparefile, std::ios::in | std::ios::binary); + auto *buf = new char[sb.st_size]; + auto *buf2 = new char[sb.st_size]; + auto &entrystream = entry->get_stream(); + original.read(buf, sb.st_size); + entrystream.read(buf2, sb.st_size); + EXPECT_TRUE(original.good()); + EXPECT_TRUE(entrystream.good()); + EXPECT_EQ(0,::memcmp(buf, buf2, sb.st_size)); + delete[] buf, buf2; + } + expected_files.erase(filename); + } + } + catch(ns_archive::archive_exception& e) + { + ADD_FAILURE() << e.what(); + } + + EXPECT_EQ(0, expected_files.size()); +} + +TEST_F(LibArchiveWrapperTest, TestCreateDirectoryArchive) { + auto dutarchive = getcwd() + "test_create.tar.gz"; + + std::cerr << "DUT: " << dutarchive << std::endl; + std::set expected_files{"dir/file1_random", "dir/file2_random", "dir/file3_zeros"}; + + // Create an archive. + try { + std::ofstream outfs(dutarchive, std::ios::trunc | std::ios::out); + ns_archive::writer writer = ns_archive::writer::make_writer(outfs, 10240); + + for(const auto& x: expected_files) { + std::ifstream file(getResourceDir() + x); + ASSERT_TRUE(file.good()); + ns_archive::entry out_entry(file); + out_entry.set_header_value_pathname(x); + writer.add_entry(out_entry); + } + } catch (ns_archive::archive_exception& e) + { + ADD_FAILURE() << e.what(); + } + + // Read back the archive and compare the files. + try + { + std::ifstream fs(dutarchive); + deleteAfterTest(dutarchive); + ns_archive::reader reader = ns_archive::reader::make_reader(fs, 32000); + + for(auto entry : reader) + { + std::string filename = entry->get_header_value_pathname(); + std::string comparefile = getResourceDir() + filename; + + bool isdirectory = (filename.size() && filename[filename.size()-1] == '/'); + + auto it = expected_files.find(filename); + ASSERT_TRUE(it != expected_files.end()); + struct stat sb; + auto statret = stat(comparefile.c_str(),&sb); + EXPECT_EQ(0, statret); + + EXPECT_TRUE(S_ISDIR(sb.st_mode) == isdirectory); + + if (!isdirectory) { + EXPECT_EQ(sb.st_size, entry->get_header_value_size()); + auto original = std::ifstream(comparefile, std::ios::in | std::ios::binary); + auto *buf = new char[sb.st_size]; + auto *buf2 = new char[sb.st_size]; + auto &entrystream = entry->get_stream(); + original.read(buf, sb.st_size); + entrystream.read(buf2, sb.st_size); + EXPECT_TRUE(original.good()); + EXPECT_TRUE(entrystream.good()); + EXPECT_EQ(0,::memcmp(buf, buf2, sb.st_size)); + delete[] buf, buf2; + } + expected_files.erase(filename); + } + } + catch(ns_archive::archive_exception& e) + { + ADD_FAILURE() << e.what() << " Archive: " << dutarchive; + } + + EXPECT_EQ(0, expected_files.size()); + std::cerr << dutarchive << std::endl; + +}