lib: unit tests: improve / better coverage

This commit is contained in:
Dirk-Jan C. Binnema 2023-09-13 23:02:53 +03:00
parent 7c16d080d2
commit 9dcbe1d96c
15 changed files with 304 additions and 147 deletions

View File

@ -79,6 +79,16 @@ test_basic()
}
}
static void
test_read_only()
{
MemDb db{true/*read-only*/};
Config conf_db{db};
auto res = conf_db.set<Config::Id::MaxMessageSize>(12345);
g_assert_false(!!res);
}
int
main(int argc, char* argv[])
{
@ -86,6 +96,7 @@ main(int argc, char* argv[])
g_test_add_func("/config-db/props", test_props);
g_test_add_func("/config-db/basic", test_basic);
g_test_add_func("/config-db/read-only", test_read_only);
return g_test_run();
}

View File

@ -32,10 +32,13 @@
#include <chrono>
using namespace std::chrono_literals;
#include "mu-store.hh"
#include "mu-scanner.hh"
#include "utils/mu-async-queue.hh"
#include "utils/mu-error.hh"
#include "../mu-store.hh"
#include "utils/mu-utils-file.hh"
using namespace Mu;
@ -544,12 +547,106 @@ test_index_basic()
}
}
static void
test_index_lazy()
{
allow_warnings();
TempDir tdir;
auto store = Store::make_new(tdir.path(), MU_TESTMAILDIR2);
assert_valid_result(store);
g_assert_true(store->empty());
Indexer& idx{store->indexer()};
Indexer::Config conf{};
conf.lazy_check = true;
conf.ignore_noupdate = false;
const auto start{time({})};
g_assert_true(idx.start(conf));
while (idx.is_running())
g_usleep(10000);
g_assert_false(idx.is_running());
g_assert_true(idx.stop());
g_assert_cmpuint(idx.completed() - start, <, 3);
const auto& prog{idx.progress()};
g_assert_false(prog.running);
g_assert_cmpuint(prog.checked,==, 6);
g_assert_cmpuint(prog.updated,==, 6);
g_assert_cmpuint(prog.removed,==, 0);
g_assert_cmpuint(store->size(),==, 6);
}
static void
test_index_cleanup()
{
allow_warnings();
TempDir tdir;
auto mdir = join_paths(tdir.path(), "Test");
{
auto res = run_command({"cp", "-r", MU_TESTMAILDIR2, mdir});
assert_valid_result(res);
g_assert_cmpuint(res->exit_code,==, 0);
}
auto store = Store::make_new(tdir.path(), mdir);
assert_valid_result(store);
g_assert_true(store->empty());
Indexer& idx{store->indexer()};
Indexer::Config conf{};
conf.ignore_noupdate = true;
g_assert_true(idx.start(conf));
while (idx.is_running())
g_usleep(10000);
g_assert_false(idx.is_running());
g_assert_true(idx.stop());
g_assert_cmpuint(store->size(),==, 14);
// remove a message
{
auto mpath = join_paths(mdir, "bar", "cur", "mail6");
auto res = run_command({"rm", mpath});
assert_valid_result(res);
g_assert_cmpuint(res->exit_code,==, 0);
}
// no cleanup, # stays the same
conf.cleanup = false;
g_assert_true(idx.start(conf));
while (idx.is_running())
g_usleep(10000);
g_assert_false(idx.is_running());
g_assert_true(idx.stop());
g_assert_cmpuint(store->size(),==, 14);
// cleanup, message is gone from store.
conf.cleanup = true;
g_assert_true(idx.start(conf));
while (idx.is_running())
g_usleep(10000);
g_assert_false(idx.is_running());
g_assert_true(idx.stop());
g_assert_cmpuint(store->size(),==, 13);
}
int
main(int argc, char* argv[])
{
mu_test_init(&argc, &argv);
g_test_add_func("/index/basic", test_index_basic);
g_test_add_func("/index/lazy", test_index_lazy);
g_test_add_func("/index/cleanup", test_index_cleanup);
return g_test_run();

View File

@ -205,23 +205,20 @@ clear_links(const std::string& path, DIR* dir)
switch(d_type) {
case DT_LNK:
if (::unlink(fullpath.c_str()) != 0) {
mu_warning("error unlinking {}: {}",
fullpath, g_strerror(errno));
mu_warning("error unlinking {}: {}", fullpath, g_strerror(errno));
res = false;
}
break;
case DT_DIR: {
DIR* subdir{::opendir(fullpath.c_str())};
if (!subdir) {
mu_warning("failed to open dir {}: {}", fullpath,
g_strerror(errno));
mu_warning("error opening dir {}: {}", fullpath, g_strerror(errno));
res = false;
}
if (!clear_links(fullpath, subdir))
res = false;
::closedir(subdir);
}
break;
} break;
default:
break;
}
@ -289,7 +286,7 @@ msg_move_g_file(const std::string& src, const std::string& dst)
else
return Err(Error::Code::File, &err, "error moving {} -> {}", src, dst);
}
/* LCOV_EXCL_STOPT*/
/* LCOV_EXCL_STOP*/
/* use mv to move files; this is slower than rename() so only use this when
* needed: when moving across filesystems */
@ -323,10 +320,11 @@ msg_move(const std::string& src, const std::string& dst, bool assume_remote)
if (::rename(src.c_str(), dst.c_str()) == 0) /* seems it worked; double-check */
return msg_move_verify(src, dst);
/* LCOV_EXCL_START*/
if (errno != EXDEV) /* some unrecoverable error occurred */
return Err(Error{Error::Code::File, "error moving {} -> {}: {}",
src, dst, strerror(errno)});
/* LCOV_EXCL_STOP*/
}
/* the EXDEV / assume-remote case -- source and target live on different
@ -461,12 +459,7 @@ Mu::maildir_determine_target(const std::string& old_path,
const auto dst_file{determine_dst_filename(src_file, newflags, new_name)};
/* and the complete path name. */
const auto subdir = std::invoke([&]()->std::string {
if (none_of(newflags & Flags::New))
return "cur";
else
return "new";
});
const std::string subdir{(none_of(newflags & Flags::New)) ? "cur" : "new"};
return join_paths(dst_mdir, subdir,dst_file);
}

View File

@ -264,14 +264,12 @@ parse_basic(const Field &field, Sexp &&vals, Mu::ParserFlags flags)
if (auto&& finfo{flag_info(val)}; finfo)
return Xapian::Query{field.xapian_term(finfo->shortcut_lower())};
else
return Err(Error::Code::InvalidArgument,
"invalid flag '{}'", val);
return Err(Error::Code::InvalidArgument, "invalid flag '{}'", val);
case Field::Id::Priority:
if (auto&& prio{priority_from_name(val)}; prio)
return Xapian::Query{field.xapian_term(to_char(*prio))};
else
return Err(Error::Code::InvalidArgument,
"invalid priority '{}'", val);
return Err(Error::Code::InvalidArgument, "invalid priority '{}'", val);
default: {
auto q{Xapian::Query{field.xapian_term(val)}};
if (ngrams) { // special case: cjk; see if we can create an expanded query.
@ -328,8 +326,7 @@ parse(const Store& store, Sexp&& s, Mu::ParserFlags flags)
return parse_field_matcher(store, *field,
*match_sym, std::move(*args));
}
return Err(Error::Code::InvalidArgument,
"unexpected sexp {}", s.to_string());
return Err(Error::Code::InvalidArgument, "unexpected sexp {}", s.to_string());
}
/* LCOV_EXCL_START */

View File

@ -250,14 +250,12 @@ Scanner::Private::start()
{
const auto mode{F_OK | R_OK};
if (G_UNLIKELY(::access(root_dir_.c_str(), mode) != 0))
return Err(Error::Code::File,
"'{}' is not readable: {}", root_dir_,
return Err(Error::Code::File, "'{}' is not readable: {}", root_dir_,
g_strerror(errno));
struct stat statbuf {};
if (G_UNLIKELY(::stat(root_dir_.c_str(), &statbuf) != 0))
return Err(Error::Code::File,
"'{}' is not stat'able: {}",
return Err(Error::Code::File, "'{}' is not stat'able: {}",
root_dir_, g_strerror(errno));
if (G_UNLIKELY(!S_ISDIR(statbuf.st_mode)))
@ -322,6 +320,7 @@ Scanner::is_running() const
#if BUILD_TESTS
/* LCOV_EXCL_START*/
#include "mu-test-utils.hh"
static void
@ -334,11 +333,15 @@ test_scan_maildirs()
MU_TESTMAILDIR,
[&](const std::string& fullpath, const struct stat* statbuf, auto&& htype) -> bool {
++count;
g_usleep(10000);
return true;
}};
g_assert_true(scanner.start());
assert_valid_result(scanner.start());
scanner.stop();
count = 0;
assert_valid_result(scanner.start());
while (scanner.is_running()) { g_usleep(1000); }
while (scanner.is_running()) { g_usleep(100000); }
// very rudimentary test...
g_assert_cmpuint(count,==,23);
@ -356,7 +359,7 @@ test_count_maildirs()
dirs.emplace_back(basename(fullpath));
return true;
}, Scanner::Mode::MaildirsOnly};
g_assert_true(scanner.start());
assert_valid_result(scanner.start());
while (scanner.is_running()) { g_usleep(1000); }
@ -366,14 +369,27 @@ test_count_maildirs()
g_assert_true(seq_find_if(dirs, [](auto& p){return p == "wom_bat";}) != dirs.end());
}
static void
test_fail_nonexistent()
{
allow_warnings();
Scanner scanner{"/foo/bar/non-existent",
[&](auto&& a1, auto&& a2, auto&& a3){ return false; }};
g_assert_false(scanner.is_running());
g_assert_false(!!scanner.start());
g_assert_false(scanner.is_running());
}
int
main(int argc, char* argv[])
{
mu_test_init(&argc, &argv);
g_test_add_func("/index/scan-maildirs", test_scan_maildirs);
g_test_add_func("/index/count-maildirs", test_count_maildirs);
g_test_add_func("/scanner/scan-maildirs", test_scan_maildirs);
g_test_add_func("/scanner/count-maildirs", test_count_maildirs);
g_test_add_func("/scanner/fail-nonexistent", test_fail_nonexistent);
return g_test_run();
}
@ -402,4 +418,5 @@ main (int argc, char *argv[])
return 0;
}
/* LCOV_EXCL_STOP*/
#endif /*BUILD_LIST_MAILDIRS*/

View File

@ -285,7 +285,6 @@ public:
DupFlags = 1 << 1, /**< Update flags for duplicate messages too*/
};
/**
* Move a message both in the filesystem and in the store. After a
* successful move, the message is updated.

View File

@ -111,6 +111,13 @@ struct MetadataIface {
/// In-memory db
struct MemDb: public MetadataIface {
/**
* Create a new memdb
*
* @param readonly read-only? (for testing)
*/
MemDb(bool readonly=false):read_only_{readonly} {}
/**
* Set some metadata
*
@ -141,7 +148,7 @@ struct MemDb: public MetadataIface {
*
* @return true or false
*/
bool read_only() const override { return false; }
bool read_only() const override { return read_only_; }
/**
@ -157,6 +164,7 @@ struct MemDb: public MetadataIface {
private:
std::unordered_map<std::string, std::string> map_;
const bool read_only_;
};
/**
@ -171,8 +179,8 @@ public:
*
*/
enum struct Flavor {
ReadOnly, /**< Read-only database */
Open, /**< Open existing read-write */
ReadOnly, /**< Read-only database */
Open, /**< Open existing read-write */
CreateOverwrite, /**< Create new or overwrite existing */
};

View File

@ -363,8 +363,9 @@ test_maildir_get_new_path_02(void)
}
}
static void
test_maildir_get_new_path_custom(void)
test_maildir_get_new_path_custom_real(bool change_name)
{
struct {
std::string oldpath;
@ -393,12 +394,30 @@ test_maildir_get_new_path_custom(void)
paths[1].root_maildir,
paths[i].targetdir,
paths[i].flags,
false)};
change_name)};
assert_valid_result(newpath);
assert_equal(*newpath, paths[i].newpath);
if (change_name)
g_assert_true(*newpath != paths[i].newpath); // weak test
else
assert_equal(*newpath, paths[i].newpath);
}
}
static void
test_maildir_get_new_path_custom(void)
{
return test_maildir_get_new_path_custom_real(false);
}
static void
test_maildir_get_new_path_custom_change_name(void)
{
return test_maildir_get_new_path_custom_real(true);
}
static void
test_maildir_from_path(void)
{
@ -506,32 +525,30 @@ test_maildir_move_gio()
int
main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
mu_test_init(&argc, &argv);
/* mu_util_maildir_mkmdir */
g_test_add_func("/mu-maildir/mu-maildir-mkdir-01", test_maildir_mkdir_01);
g_test_add_func("/mu-maildir/mu-maildir-mkdir-02", test_maildir_mkdir_02);
g_test_add_func("/mu-maildir/mu-maildir-mkdir-03", test_maildir_mkdir_03);
g_test_add_func("/mu-maildir/mu-maildir-mkdir-04", test_maildir_mkdir_04);
g_test_add_func("/mu-maildir/mu-maildir-mkdir-05", test_maildir_mkdir_05);
g_test_add_func("/maildir/mkdir-01", test_maildir_mkdir_01);
g_test_add_func("/maildir/mkdir-02", test_maildir_mkdir_02);
g_test_add_func("/maildir/mkdir-03", test_maildir_mkdir_03);
g_test_add_func("/maildir/mkdir-04", test_maildir_mkdir_04);
g_test_add_func("/maildir/mkdir-05", test_maildir_mkdir_05);
g_test_add_func("/mu-maildir/mu-maildir-determine-target-ok",
test_determine_target_ok);
g_test_add_func("/mu-maildir/mu-maildir-determine-target-fail",
test_determine_target_fail);
g_test_add_func("/maildir/determine-target-ok", test_determine_target_ok);
g_test_add_func("/maildir/determine-target-fail", test_determine_target_fail);
// /* get/set flags */
g_test_add_func("/mu-maildir/mu-maildir-get-new-path-01", test_maildir_get_new_path_01);
g_test_add_func("/mu-maildir/mu-maildir-get-new-path-02", test_maildir_get_new_path_02);
g_test_add_func("/mu-maildir/mu-maildir-get-new-path-custom",
test_maildir_get_new_path_custom);
g_test_add_func("/mu-maildir/mu-maildir-from-path",
test_maildir_from_path);
g_test_add_func("/maildir/get-new-path-01", test_maildir_get_new_path_01);
g_test_add_func("/maildir/get-new-path-02", test_maildir_get_new_path_02);
g_test_add_func("/maildir/get-new-path-custom", test_maildir_get_new_path_custom);
g_test_add_func("/maildir/get-new-path-custom-change-name",
test_maildir_get_new_path_custom_change_name);
g_test_add_func("/mu-maildir/mu-maildir-link", test_maildir_link);
g_test_add_func("/maildir/from-path", test_maildir_from_path);
g_test_add_func("/mu-maildir/mu-maildir-move-vanilla", test_maildir_move_vanilla);
g_test_add_func("/mu-maildir/mu-maildir-move-gio", test_maildir_move_gio);
g_test_add_func("/maildir/link", test_maildir_link);
g_test_add_func("/maildir/move-vanilla", test_maildir_move_vanilla);
g_test_add_func("/maildir/aildir-move-gio", test_maildir_move_gio);
return g_test_run();
}

View File

@ -505,6 +505,26 @@ test_store_circular_symlink(void)
remove_directory(testhome);
}
static void
test_store_maildirs()
{
allow_warnings();
TempDir tdir;
auto store = Store::make_new(tdir.path(), MU_TESTMAILDIR2);
assert_valid_result(store);
g_assert_true(store->empty());
const auto mdirs = store->maildirs();
g_assert_cmpuint(mdirs.size(), ==, 3);
g_assert(seq_some(mdirs, [](auto&& m){return m == "/Foo";}));
g_assert(seq_some(mdirs, [](auto&& m){return m == "/bar";}));
g_assert(seq_some(mdirs, [](auto&& m){return m == "/wom_bat";}));
}
static void
test_store_fail()
@ -521,6 +541,7 @@ test_store_fail()
}
}
int
main(int argc, char* argv[])
{
@ -534,8 +555,12 @@ main(int argc, char* argv[])
g_test_add_func("/store/message/attachments",
test_message_attachments);
g_test_add_func("/store/move-dups", test_store_move_dups);
g_test_add_func("/store/maildirs", test_store_maildirs);
g_test_add_func("/store/index/index-move", test_index_move);
g_test_add_func("/store/index/circular-symlink", test_store_circular_symlink);
g_test_add_func("/store/index/fail", test_store_fail);
return g_test_run();

View File

@ -87,6 +87,12 @@ test('test-logger',
cpp_args: ['-DBUILD_TESTS'],
dependencies: [glib_dep, lib_mu_utils_dep, thread_dep ]))
test('test-option',
executable('test-option', 'mu-option.cc',
install: false,
cpp_args: ['-DBUILD_TESTS'],
dependencies: [glib_dep, lib_mu_utils_dep ]))
test('test-lang-detector',
executable('test-lang-detector', 'mu-lang-detector.cc',
install: false,

View File

@ -30,3 +30,77 @@ Mu::to_string_opt_gchar(gchar*&& str)
return res;
}
#if BUILD_TESTS
#include "mu-test-utils.hh"
static Option<int>
get_opt_int(bool b)
{
if (b)
return Some(123);
else
return Nothing;
}
static void
test_option()
{
{
const auto oi{get_opt_int(true)};
g_assert_true(!!oi);
g_assert_cmpint(oi.value(), ==, 123);
}
{
const auto oi{get_opt_int(false)};
g_assert_false(!!oi);
g_assert_false(oi.has_value());
g_assert_cmpint(oi.value_or(456), ==, 456);
}
}
static void
test_unwrap()
{
{
auto&& oi{get_opt_int(true)};
g_assert_cmpint(unwrap(std::move(oi)), ==, 123);
}
auto ex{0};
try {
auto&& oi{get_opt_int(false)};
unwrap(std::move(oi));
} catch(...) {
ex = 1;
}
g_assert_cmpuint(ex, ==, 1);
}
static void
test_opt_gchar()
{
auto o1{to_string_opt_gchar(g_strdup("boo!"))};
auto o2{to_string_opt_gchar(nullptr)};
g_assert_false(!!o2);
g_assert_true(o1.value() == "boo!");
}
int
main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/option/option", test_option);
g_test_add_func("/option/unwrap", test_unwrap);
g_test_add_func("/option/opt-gchar", test_opt_gchar);
return g_test_run();
}
#endif /*BUILD_TESTS*/

View File

@ -22,8 +22,6 @@
using namespace Mu;
// LCOV_EXCL_STOP
#if BUILD_TESTS
#include "mu-test-utils.hh"
@ -75,17 +73,14 @@ test_regex_replace()
int
main(int argc, char* argv[])
try {
{
mu_test_init(&argc, &argv);
g_test_add_func("/regex/match", test_regex_match);
g_test_add_func("/regex/match2", test_regex_match2);
g_test_add_func("/regex/replace", test_regex_replace);
return g_test_run();
} catch (const std::runtime_error& re) {
mu_printerrln("{}", re.what());
return 1;
return g_test_run();
}
#endif /*BUILD_TESTS*/

View File

@ -1,4 +1,4 @@
## Copyright (C) 2021 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
## Copyright (C) 2021-2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
##
## 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
@ -17,12 +17,7 @@
################################################################################
# tests
#
test('test-option',
executable('test-option',
'test-option.cc',
install: false,
dependencies: [glib_dep, lib_mu_utils_dep]))
test('test-mu-utils',
executable('test-mu-utils',
'test-utils.cc',

View File

@ -1,59 +0,0 @@
/*
** Copyright (C) 2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** This library is free software; you can redistribute it and/or
** modify it under the terms of the GNU Lesser General Public License
** as published by the Free Software Foundation; either version 2.1
** of the License, or (at your option) any later version.
**
** This library 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
** Lesser General Public License for more details.
**
** You should have received a copy of the GNU Lesser General Public
** License along with this library; if not, write to the Free
** Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
** 02110-1301, USA.
*/
#include "mu-utils.hh"
#include "mu-option.hh"
using namespace Mu;
static Option<int>
get_opt_int(bool b)
{
if (b)
return Some(123);
else
return Nothing;
}
static void
test_option()
{
{
const auto oi{get_opt_int(true)};
g_assert_true(!!oi);
g_assert_cmpint(oi.value(), ==, 123);
}
{
const auto oi{get_opt_int(false)};
g_assert_false(!!oi);
g_assert_false(oi.has_value());
g_assert_cmpint(oi.value_or(456), ==, 456);
}
}
int
main(int argc, char* argv[])
{
g_test_init(&argc, &argv, NULL);
g_test_add_func("/option/option", test_option);
return g_test_run();
}

View File

@ -1,18 +0,0 @@
/*
** Copyright (C) 2023 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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, 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.
**
*/