Add tests
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Armin Friedl 2020-02-25 22:01:31 +01:00
parent 6a2aa756ca
commit 9cb65ce856
Signed by: armin
GPG key ID: 48C726EEE7FBCBC8
15 changed files with 171 additions and 43 deletions

View file

@ -6,8 +6,14 @@ steps:
- name: build
image: arminfriedl/xwim-build
commands:
- meson wrap install gtest
- meson build
- ninja -C build
- ninja -C build test && ninja -C build coverage
- echo "******** TEST LOGS ***********"
- cat build/meson-logs/testlog.txt
- echo "****** COVERAGE LOGS *********"
- cat build/meson-logs/coverage.txt
trigger:
event:
@ -23,6 +29,7 @@ steps:
- name: build
image: arminfriedl/xwim-build
commands:
- meson wrap install gtest
- meson --buildtype=release build
- ninja -C build
- mkdir xwim-${DRONE_TAG}-x86_64-glibc-linux

View file

@ -790,8 +790,7 @@ WARN_LOGFILE =
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
INPUT = "@PROJECT_SRCDIR@" \
"@PROJECT_BINDIR@"
INPUT = "@PROJECT_SRCDIR@"
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@ -864,7 +863,7 @@ EXCLUDE_SYMBOLS =
# that contain example code fragments that are included (see the \include
# command).
EXAMPLE_PATH = "@PROJECT_EXAMPLESDIR@"
EXAMPLE_PATH =
# If the value of the EXAMPLE_PATH tag contains directories, you can use the
# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
@ -2020,8 +2019,7 @@ SEARCH_INCLUDES = YES
# preprocessor.
# This tag requires that the tag SEARCH_INCLUDES is set to YES.
INCLUDE_PATH = "@PROJECT_SRCDIR@" \
"@PROJECT_BINDIR@"
INCLUDE_PATH = "@PROJECT_SRCDIR@"
# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
# patterns (like *.h and *.hpp) to filter out the header-files in the

View file

@ -14,7 +14,7 @@ else
endif
cdata.set('PROJECT_NAME', meson.project_name())
cdata.set('PROJECT_SRCDIR', join_paths(meson.current_source_dir(),'..','src',meson.project_name()))
cdata.set('PROJECT_SRCDIR', join_paths(meson.current_source_dir(),'..','src'))
cdata.set('PROJECT_DOCDIR', meson.current_source_dir())
cdata.set('PROJECT_TESTDIR', join_paths(meson.current_source_dir(),'..','test'))

View file

@ -1,5 +1,9 @@
project('xwim', 'cpp',
version: '0.2',
default_options: ['cpp_std=c++17'])
default_options: ['cpp_std=c++17',
'warning_level=3',
'b_coverage=true'])
subdir('src')
subdir('doc')
subdir('test')

View file

@ -12,6 +12,7 @@
namespace xwim {
/** Class for interacting with archives */
class Archive {
private:
std::filesystem::path path;
@ -19,7 +20,13 @@ class Archive {
public:
explicit Archive(std::filesystem::path path);
/** Generate an ArchiveSpec by analysing the archive at `path`
*
* @returns ArchiveSpec for the archive
*/
ArchiveSpec check();
/** Extract the archive at `path` according to given ExtractSpec */
void extract(ExtractSpec extract_spec);
};

View file

@ -4,9 +4,16 @@
#include <filesystem>
#include <memory>
#include <fmt/format.h>
namespace xwim {
/** A view into an archive entry
*
* The view is non-owning and the caller must guarantee
* that the parent archive entry is valid when the view
* is accessed.
*/
class ArchiveEntryView {
private:
archive_entry* ae;

View file

@ -1,8 +1,17 @@
/** @file fileformats.hpp
* @brief Handle archive extensions
*/
#pragma once
#include <spdlog/spdlog.h>
namespace logger = spdlog;
#include <filesystem>
#include <set>
#include <string>
namespace xwim {
/** Common archive formats understood by xwim
*
* The underlying libarchive backend retrieves format information by a process
@ -11,21 +20,20 @@ namespace logger = spdlog;
* Stripping extensions via `std::filesystem::path` does not work reliably since
* it gets easily confused by dots in the regular file name.
*/
const std::set<std::string> fileformats{".7z", ".7zip", ".jar", ".tgz",
".bz2", ".bzip2", ".gz", ".gzip",
".rar", ".tar", "xz", ".zip"};
#include <filesystem>
#include <set>
#include <string>
namespace xwim {
const std::set<std::string> fileformats{
".7z", ".7zip", ".jar", ".tgz", ".bz2", ".bzip2", ".gz",
".gzip", ".rar", ".tar", ".tar.gz", ".tar.bz2", ".tar.xz", ".zip"};
inline std::filesystem::path stem(std::filesystem::path& path) {
/** Strip archive extensions from a path
*
* @returns Base filename without archive extensions
*/
inline std::filesystem::path stem(const std::filesystem::path& path) {
std::filesystem::path p_stem{path};
logger::trace("Stemming {}", p_stem.string());
p_stem = p_stem.filename();
while (fileformats.find(p_stem.extension().string()) != fileformats.end()) {
p_stem = p_stem.stem();
logger::trace("Stemmed to {}", p_stem.string());

View file

@ -1,14 +1,14 @@
src = ['main.cpp',
xwim_src = ['main.cpp',
'archive.cpp',
'archive_sys.cpp']
inc = ['archive.hpp',
xwim_inc = ['archive.hpp',
'spec.hpp',
'archive_sys.hpp',
'fileformats.hpp']
libs = [dependency('libarchive', required: true),
dependency('fmt', required: true, static: true),
dependency('spdlog', required: true, static: true)]
xwim_libs = [dependency('libarchive', required: true),
dependency('fmt', required: true),
dependency('spdlog', required: true)]
executable('xwim', src, inc, dependencies: libs)
executable('xwim', xwim_src, xwim_inc, dependencies: xwim_libs)

View file

@ -1,35 +1,57 @@
#pragma once
#include <archive.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <filesystem>
#include <memory>
namespace xwim {
/** Properties of an archive
*
* These properties can be retrieved by analyzing the
* archive. There is no outside-knowledge. All information
* is in the archive.
*/
struct ArchiveSpec {
bool has_single_root = false;
bool is_root_filename = false;
bool is_root_dir = false;
bool has_subarchive = false;
bool has_single_root = false; /** There is only a single file xor a single
folder at the archive's root */
bool is_root_filename = false; /** the name of the (single) root is the same
as the stemmed archive file name. Cannot be
true if `has_single_root` is false */
bool is_root_dir = false; /** The (single) root is a folder. Cnnot be true if
`has_single_root` is false */
bool has_subarchive = false; /** Whether the archive contains sub-archives */
};
/** Properties influencing the extraction process
*
* These properties can be set to influence the extraction
* process accordingly.
*/
struct ExtractSpec {
bool make_dir = false;
std::filesystem::path dirname{};
bool extract_subarchive = false;
bool make_dir = false; /** Create a new directory for extraction at `dirname` */
std::filesystem::path dirname{}; /** The path to a directory for extraction */
bool extract_subarchive = false; /** Recursively extract sub-archives */
};
} // namespace xwim
#if FMT_VERSION < 50300
typedef fmt::basic_parse_context<char> format_parse_context;
#endif
template <>
struct fmt::formatter<xwim::ArchiveSpec> {
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
constexpr auto parse(format_parse_context & ctx) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const xwim::ArchiveSpec& spec, FormatContext& ctx) {
return format_to(ctx.out(),"Archive["
return format_to(ctx.out(),
"Archive["
" .has_single_root={},"
" .is_root_filename={}"
" .is_root_dir={}"
@ -46,11 +68,13 @@ struct fmt::formatter<xwim::ExtractSpec> {
template <typename FormatContext>
auto format(const xwim::ExtractSpec& spec, FormatContext& ctx) {
return format_to(ctx.out(), "Extract["
return format_to(ctx.out(),
"Extract["
" .make_dir={},"
" .dirname={}"
" .extract_subarchive={}"
" ]",
spec.make_dir, spec.dirname.string(), spec.extract_subarchive);
spec.make_dir, spec.dirname.string(),
spec.extract_subarchive);
}
};

4
subprojects/.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

11
test/archive_test.cpp Normal file
View file

@ -0,0 +1,11 @@
#include <gtest/gtest.h>
#include <archive.hpp>
#include <spec.hpp>
TEST(ArchiveTest, ArchiveSpecDetectsSingleRoot) {
xwim::Archive archive("test/archives/root.tar.gz");
xwim::ArchiveSpec spec = archive.check();
ASSERT_TRUE(spec.has_single_root);
}

View file

@ -0,0 +1 @@
configure_file(input: 'root.tar.gz', output: 'root.tar.gz', copy: true)

BIN
test/archives/root.tar.gz Normal file

Binary file not shown.

34
test/fileformats_test.cpp Normal file
View file

@ -0,0 +1,34 @@
#include <gtest/gtest.h>
#include <fileformats.hpp>
#include <string>
TEST(FileformatsTest, StemStripsSingleKnownExtension) {
std::filesystem::path archive_path {"/some/path/to/file.rar"};
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file"});
}
TEST(FileformatsTest, StemStripsMultipleKnownExtensions) {
std::filesystem::path archive_path{"/some/path/to/file.tar.rar.gz.7z.rar"};
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file"});
}
TEST(FileformatsTest, StemStripsOnlyKnownExtension) {
std::filesystem::path archive_path{"/some/path/to/file.ukn.rar"};
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file.ukn"});
}
TEST(FileformatsTest, StemStripsNothingWithoutKnownExtension) {
std::filesystem::path archive_path{"/some/path/to/file.ukn"};
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"file.ukn"});
}
TEST(FileformatsTest, StemStripsNothingWithoutExtension) {
std::filesystem::path archive_path{"/some/path/to/filerar"};
ASSERT_EQ(xwim::stem(archive_path), std::filesystem::path{"filerar"});
}

23
test/meson.build Normal file
View file

@ -0,0 +1,23 @@
# Run tests+coverage with `ninja clean && ninja test && ninja coverage`
gtest_proj = subproject('gtest')
gtest_dep = gtest_proj.get_variable('gtest_main_dep')
xwim_src = ['../src/archive.cpp',
'../src/archive_sys.cpp']
subdir('archives')
archive_test_exe = executable('archive_test_exe',
sources: ['archive_test.cpp', xwim_src],
include_directories: ['../src'],
dependencies: [gtest_dep, xwim_libs])
test('archive test', archive_test_exe)
fileformats_test_exe = executable('fileformats_test_exe',
sources: ['fileformats_test.cpp', xwim_src],
include_directories: ['../src'],
dependencies: [gtest_dep, xwim_libs])
test('fileformats test', fileformats_test_exe)