From 3ba2c4ea08b5f9631556718c1f9b59d5213ea15b Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Mon, 15 Aug 2022 23:09:35 +0300 Subject: [PATCH 1/5] mu4e-headers: do not nillify mu4e~view-message In mu4e~headers-clear, do not set mu4e~view~message to nil; it may still be in use. --- mu4e/mu4e-headers.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mu4e/mu4e-headers.el b/mu4e/mu4e-headers.el index cc7acd92..1ae0cd7d 100644 --- a/mu4e/mu4e-headers.el +++ b/mu4e/mu4e-headers.el @@ -352,8 +352,7 @@ This is mostly useful for profiling.") "Clear the headers buffer and related data structures. Optionally, show TEXT." (when (buffer-live-p (mu4e-get-headers-buffer)) - (setq mu4e~headers-render-start (float-time) - mu4e~view-message nil) + (setq mu4e~headers-render-start (float-time)) (let ((inhibit-read-only t)) (with-current-buffer (mu4e-get-headers-buffer) (mu4e--mark-clear) From 11389247c532bf3564ea89ea3264f707d1340dd2 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Wed, 10 Aug 2022 08:20:58 +0300 Subject: [PATCH 2/5] tests: update test helpers and users Move test-mu-common to mu-test-utils. Use mu_test_init as a wrapper for g_test_init. Update users. --- guile/tests/meson.build | 2 +- guile/tests/test-mu-guile.cc | 10 +- lib/meson.build | 16 +-- lib/message/mu-contact.cc | 5 +- lib/message/mu-document.cc | 2 + lib/message/mu-fields.cc | 4 +- lib/message/mu-message-file.cc | 4 +- lib/message/test-mu-message.cc | 4 +- lib/mu-contacts-cache.cc | 10 +- lib/tests/bench-indexer.cc | 2 +- lib/tests/meson.build | 14 +-- lib/tests/test-mu-common.cc | 89 --------------- lib/tests/test-mu-common.hh | 58 ---------- lib/tests/test-mu-container.cc | 2 +- lib/tests/test-mu-maildir.cc | 2 +- lib/tests/test-mu-msg-fields.cc | 2 +- lib/tests/test-mu-msg.cc | 26 ++--- lib/tests/test-mu-store-query.cc | 14 +-- lib/tests/test-mu-store.cc | 13 +-- lib/tests/test-parser.cc | 2 +- lib/tests/test-query.cc | 14 +-- lib/utils/meson.build | 3 +- lib/utils/mu-test-utils.cc | 150 +++++++++++++++++++++++++ lib/utils/mu-test-utils.hh | 129 +++++++++++++++++++++ lib/utils/mu-utils.cc | 45 -------- lib/utils/mu-utils.hh | 68 ----------- lib/utils/tests/test-command-parser.cc | 27 ++--- lib/utils/tests/test-sexp.cc | 3 +- lib/utils/tests/test-utils.cc | 3 +- mu/tests/meson.build | 6 +- mu/tests/test-mu-cmd-cfind.cc | 10 +- mu/tests/test-mu-cmd.cc | 11 +- mu/tests/test-mu-query.cc | 11 +- 33 files changed, 355 insertions(+), 406 deletions(-) delete mode 100644 lib/tests/test-mu-common.cc delete mode 100644 lib/tests/test-mu-common.hh create mode 100644 lib/utils/mu-test-utils.cc create mode 100644 lib/utils/mu-test-utils.hh diff --git a/guile/tests/meson.build b/guile/tests/meson.build index 7cb61f5a..dc890510 100644 --- a/guile/tests/meson.build +++ b/guile/tests/meson.build @@ -28,4 +28,4 @@ test('test-mu-guile', '-DGUILE_LOAD_PATH="' + guile_load_path + '"', '-DGUILE_EXTENSIONS_PATH="' + guile_load_path + '"' ], - dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, lib_mu_dep])) diff --git a/guile/tests/test-mu-guile.cc b/guile/tests/test-mu-guile.cc index b7172bf4..a630d16d 100644 --- a/guile/tests/test-mu-guile.cc +++ b/guile/tests/test-mu-guile.cc @@ -27,7 +27,7 @@ #include #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include #include @@ -116,7 +116,7 @@ main(int argc, char* argv[]) TempDir tempdir; test_dir = tempdir.path(); - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); if (!set_en_us_utf8_locale()) return 0; /* don't error out... */ @@ -125,12 +125,6 @@ main(int argc, char* argv[]) g_test_add_func("/guile/message", test_mu_guile_messages); g_test_add_func("/guile/stats", test_mu_guile_stats); - g_log_set_handler(NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING | - G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); - rv = g_test_run(); return rv; diff --git a/lib/meson.build b/lib/meson.build index ccbf091a..eb04859d 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -1,4 +1,4 @@ -## Copyright (C) 2021 Dirk-Jan C. Binnema +## Copyright (C) 2021-2022 Dirk-Jan C. Binnema ## ## 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 @@ -63,16 +63,6 @@ tokenize = executable( dependencies: [ lib_mu_utils_dep, glib_dep ], install: false) -# test helpers -lib_test_mu_common=static_library('mu-test-common', [ - 'tests/test-mu-common.cc', - 'tests/test-mu-common.hh'], - dependencies: [ glib_dep, thread_dep, - config_h_dep ]) -lib_test_mu_common_dep=declare_dependency( - link_with: lib_test_mu_common, - include_directories: include_directories(['tests'])) - # actual tests test('test-threads', @@ -80,12 +70,12 @@ test('test-threads', 'mu-query-threads.cc', install: false, cpp_args: ['-DBUILD_TESTS'], - dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, lib_mu_dep])) test('test-contacts-cache', executable('test-contacts-cache', 'mu-contacts-cache.cc', install: false, cpp_args: ['-DBUILD_TESTS'], - dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, lib_mu_dep])) subdir('tests') diff --git a/lib/message/mu-contact.cc b/lib/message/mu-contact.cc index db77df42..711f4d27 100644 --- a/lib/message/mu-contact.cc +++ b/lib/message/mu-contact.cc @@ -80,7 +80,7 @@ Mu::lowercase_hash(const std::string& s) * */ -#include "utils/mu-utils.hh" +#include "utils/mu-test-utils.hh" static void test_ctor_foo() @@ -189,14 +189,13 @@ static void test_misc() { g_assert_false(!!contact_type_from_field_id(Field::Id::Subject)); - } int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_mime_init(); g_test_add_func("/message/contact/ctor-foo", test_ctor_foo); diff --git a/lib/message/mu-document.cc b/lib/message/mu-document.cc index c0a207ed..f0c230c5 100644 --- a/lib/message/mu-document.cc +++ b/lib/message/mu-document.cc @@ -370,6 +370,8 @@ Document::remove(Field::Id field_id) #ifdef BUILD_TESTS +#include "utils/mu-test-utils.hh" + #define assert_same_contact(C1,C2) do { \ g_assert_cmpstr(C1.email.c_str(),==,C2.email.c_str()); \ g_assert_cmpstr(C2.name.c_str(),==,C2.name.c_str()); \ diff --git a/lib/message/mu-fields.cc b/lib/message/mu-fields.cc index d114a1c8..aec8c7ab 100644 --- a/lib/message/mu-fields.cc +++ b/lib/message/mu-fields.cc @@ -20,6 +20,8 @@ #include "mu-fields.hh" #include "mu-flags.hh" +#include "utils/mu-test-utils.hh" + using namespace Mu; std::string @@ -184,7 +186,7 @@ test_xapian_term() int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_test_add_func("/message/fields/ids", test_ids); g_test_add_func("/message/fields/shortcuts", test_shortcuts); diff --git a/lib/message/mu-message-file.cc b/lib/message/mu-message-file.cc index 6067f622..3c86c249 100644 --- a/lib/message/mu-message-file.cc +++ b/lib/message/mu-message-file.cc @@ -122,6 +122,8 @@ Mu::flags_from_path(const std::string& path) #ifdef BUILD_TESTS +#include "utils/mu-test-utils.hh" + static void test_maildir_from_path() { @@ -192,7 +194,7 @@ test_flags_from_path() int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_test_add_func("/message/file/maildir-from-path", test_maildir_from_path); diff --git a/lib/message/test-mu-message.cc b/lib/message/test-mu-message.cc index 5311e86d..8d85fae2 100644 --- a/lib/message/test-mu-message.cc +++ b/lib/message/test-mu-message.cc @@ -16,7 +16,7 @@ ** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ** */ - +#include "utils/mu-test-utils.hh" #include "mu-message.hh" #include "mu-mime-object.hh" #include @@ -834,7 +834,7 @@ test_message_sanitize_maildir() int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_test_add_func("/message/message/mailing-list", test_message_mailing_list); diff --git a/lib/mu-contacts-cache.cc b/lib/mu-contacts-cache.cc index 24556aba..c4c81468 100644 --- a/lib/mu-contacts-cache.cc +++ b/lib/mu-contacts-cache.cc @@ -342,7 +342,7 @@ ContactsCache::is_personal(const std::string& addr) const * */ -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" static void test_mu_contacts_cache_base() @@ -499,19 +499,13 @@ test_mu_contacts_cache_sort() int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_test_add_func("/lib/contacts-cache/base", test_mu_contacts_cache_base); g_test_add_func("/lib/contacts-cache/personal", test_mu_contacts_cache_personal); g_test_add_func("/lib/contacts-cache/for-each", test_mu_contacts_cache_foreach); g_test_add_func("/lib/contacts-cache/sort", test_mu_contacts_cache_sort); - g_log_set_handler( - NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); - return g_test_run(); } #endif /*BUILD_TESTS*/ diff --git a/lib/tests/bench-indexer.cc b/lib/tests/bench-indexer.cc index c857847d..b83bd909 100644 --- a/lib/tests/bench-indexer.cc +++ b/lib/tests/bench-indexer.cc @@ -28,7 +28,7 @@ #include #include "mu-maildir.hh" -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" using namespace Mu; diff --git a/lib/tests/meson.build b/lib/tests/meson.build index ab233f25..17a9b726 100644 --- a/lib/tests/meson.build +++ b/lib/tests/meson.build @@ -21,40 +21,40 @@ test('test-maildir', executable('test-maildir', 'test-mu-maildir.cc', install: false, - dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, lib_mu_dep])) test('test-msg', executable('test-msg', 'test-mu-msg.cc', install: false, - dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, lib_mu_dep])) test('test-store', executable('test-store', 'test-mu-store.cc', install: false, - dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, lib_mu_dep])) test('test-query', executable('test-query', 'test-query.cc', install: false, - dependencies: [glib_dep, gmime_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, gmime_dep, lib_mu_dep])) test('test-tokenizer', executable('test-tokenizer', 'test-tokenizer.cc', install: false, - dependencies: [glib_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, lib_mu_dep])) test('test-parser', executable('test-parser', 'test-parser.cc', install: false, - dependencies: [glib_dep, gmime_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, gmime_dep, lib_mu_dep])) test('test-store-query', executable('test-store-query', 'test-mu-store-query.cc', install: false, - dependencies: [glib_dep, gmime_dep, lib_mu_dep, lib_test_mu_common_dep])) + dependencies: [glib_dep, gmime_dep, lib_mu_dep])) # # benchmarks # diff --git a/lib/tests/test-mu-common.cc b/lib/tests/test-mu-common.cc deleted file mode 100644 index 92d706f5..00000000 --- a/lib/tests/test-mu-common.cc +++ /dev/null @@ -1,89 +0,0 @@ -/* -** Copyright (C) 2008-2020 Dirk-Jan C. Binnema -** -** 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. -** -*/ - -#if HAVE_CONFIG_H -#include "config.h" -#endif /*HAVE_CONFIG_H*/ - -#include -#include - -#include -#include -#include - -#include -#include - -#include "test-mu-common.hh" - -char* -test_mu_common_get_random_tmpdir(void) -{ - char* dir; - int res; - - dir = g_strdup_printf("%s%cmu-test-%d%ctest-%x", - g_get_tmp_dir(), - G_DIR_SEPARATOR, - getuid(), - G_DIR_SEPARATOR, - (int)random() * getpid() * (int)time(NULL)); - - res = g_mkdir_with_parents(dir, 0700); - g_assert(res != -1); - - return dir; -} - -const char* -set_tz(const char* tz) -{ - static const char* oldtz; - - oldtz = getenv("TZ"); - if (tz) - setenv("TZ", tz, 1); - else - unsetenv("TZ"); - - tzset(); - return oldtz; -} - -gboolean -set_en_us_utf8_locale(void) -{ - setenv("LC_ALL", "en_US.UTF-8", 1); - setlocale(LC_ALL, "en_US.UTF-8"); - - if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0) { - g_print("Note: Unit tests require the en_US.utf8 locale. " - "Ignoring test cases.\n"); - return FALSE; - } - - return TRUE; -} - -void -black_hole(void) -{ - return; /* do nothing */ -} diff --git a/lib/tests/test-mu-common.hh b/lib/tests/test-mu-common.hh deleted file mode 100644 index cfb427f9..00000000 --- a/lib/tests/test-mu-common.hh +++ /dev/null @@ -1,58 +0,0 @@ -/* -** Copyright (C) 2008-2013 Dirk-Jan C. Binnema -** -** 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. -** -*/ - -#ifndef __TEST_MU_COMMON_H__ -#define __TEST_MU_COMMON_H__ - -#include - -G_BEGIN_DECLS - -/** - * get a dir name for a random temporary directory to do tests - * - * @return a random dir name, g_free when it's no longer needed - */ -char* test_mu_common_get_random_tmpdir(void); - -/** - * set the output to /dev/null - * - */ -void black_hole(void); - -/** - * set the timezone - * - * @param tz timezone - * - * @return the old timezone - */ -const char* set_tz(const char* tz); - -/** - * switch the locale to en_US.utf8, return TRUE if it succeeds - * - * @return TRUE if the switch succeeds, FALSE otherwise - */ -gboolean set_en_us_utf8_locale(void); - -G_END_DECLS - -#endif /*__TEST_MU_COMMON_H__*/ diff --git a/lib/tests/test-mu-container.cc b/lib/tests/test-mu-container.cc index 925de1a0..4fb1939b 100644 --- a/lib/tests/test-mu-container.cc +++ b/lib/tests/test-mu-container.cc @@ -20,7 +20,7 @@ #include "config.h" #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-container.hh" static gboolean diff --git a/lib/tests/test-mu-maildir.cc b/lib/tests/test-mu-maildir.cc index 23b36b34..d3713f64 100644 --- a/lib/tests/test-mu-maildir.cc +++ b/lib/tests/test-mu-maildir.cc @@ -26,7 +26,7 @@ #include #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-maildir.hh" #include "utils/mu-result.hh" #include "utils/mu-util.h" diff --git a/lib/tests/test-mu-msg-fields.cc b/lib/tests/test-mu-msg-fields.cc index 770689db..5f5df162 100644 --- a/lib/tests/test-mu-msg-fields.cc +++ b/lib/tests/test-mu-msg-fields.cc @@ -28,7 +28,7 @@ #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-message-fields.hh" static void diff --git a/lib/tests/test-mu-msg.cc b/lib/tests/test-mu-msg.cc index cd3ec40e..9056e079 100644 --- a/lib/tests/test-mu-msg.cc +++ b/lib/tests/test-mu-msg.cc @@ -28,7 +28,7 @@ #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "utils/mu-result.hh" #include "utils/mu-utils.hh" @@ -207,12 +207,8 @@ test_mu_msg_references(void) "non-exist-04@msg.id" }; - g_assert_cmpuint(msg.references().size(), == , expected_refs.size()); - size_t n{}; - for (auto&& ref: msg.references()) { - assert_equal(ref, expected_refs.at(n)); - ++n; - }; + assert_equal_seq_str(msg.references(), expected_refs); + assert_equal(msg.thread_id(), expected_refs[0]); } static void @@ -230,12 +226,8 @@ test_mu_msg_references_dups(void) "20051211184308.GB13513@gauss.org" }; - g_assert_cmpuint(msg.references().size(), == , expected_refs.size()); - size_t n{}; - for (auto&& ref: msg.references()) { - assert_equal(ref, expected_refs.at(n)); - ++n; - }; + assert_equal_seq_str(msg.references(), expected_refs); + assert_equal(msg.thread_id(), expected_refs[0]); } static void @@ -258,12 +250,8 @@ test_mu_msg_references_many(void) "8ioh48-8mu.ln1@leafnode-msgid.gclare.org.uk" }; - g_assert_cmpuint(msg.references().size(), == , expected_refs.size()); - size_t n{}; - for (auto&& ref: msg.references()) { - assert_equal(ref, expected_refs.at(n)); - ++n; - }; + assert_equal_seq_str(msg.references(), expected_refs); + assert_equal(msg.thread_id(), expected_refs[0]); } static void diff --git a/lib/tests/test-mu-store-query.cc b/lib/tests/test-mu-store-query.cc index 0331271c..e24b7c28 100644 --- a/lib/tests/test-mu-store-query.cc +++ b/lib/tests/test-mu-store-query.cc @@ -17,7 +17,7 @@ ** */ -#include "test-mu-common.hh" + #include #include #include @@ -27,6 +27,7 @@ #include #include +#include #include using namespace Mu; @@ -338,22 +339,15 @@ Child int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_test_bug_base("https://github.com/djcb/mu/issues/"); - g_test_add_func("/store/query/simple", test_simple); + g_test_add_func("/store/query/simple", test_simple); g_test_add_func("/store/query/spam-address-components", test_spam_address_components); g_test_add_func("/store/query/dups-related", test_dups_related); - if (!g_test_verbose()) - g_log_set_handler( - NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | - G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, NULL); - return g_test_run(); } diff --git a/lib/tests/test-mu-store.cc b/lib/tests/test-mu-store.cc index 3e281d92..1a140e30 100644 --- a/lib/tests/test-mu-store.cc +++ b/lib/tests/test-mu-store.cc @@ -28,7 +28,7 @@ #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-store.hh" #include "utils/mu-result.hh" #include @@ -343,7 +343,6 @@ test_store_fail() "/../../root/non-existent-path/54321", {}, {}); g_assert_false(!!store); - } } @@ -351,7 +350,7 @@ test_store_fail() int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_test_add_func("/store/ctor-dtor", test_store_ctor_dtor); g_test_add_func("/store/add-count-remove", test_store_add_count_remove); @@ -362,13 +361,5 @@ main(int argc, char* argv[]) g_test_add_func("/store/index/move", test_index_move); g_test_add_func("/store/index/fail", test_store_fail); - if (!g_test_verbose()) - g_log_set_handler( - NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | - G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); - return g_test_run(); } diff --git a/lib/tests/test-parser.cc b/lib/tests/test-parser.cc index 4429040f..74b5522c 100644 --- a/lib/tests/test-parser.cc +++ b/lib/tests/test-parser.cc @@ -23,7 +23,7 @@ #include #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-parser.hh" #include "utils/mu-result.hh" diff --git a/lib/tests/test-query.cc b/lib/tests/test-query.cc index 2ebcdd1e..d1ca0bb0 100644 --- a/lib/tests/test-query.cc +++ b/lib/tests/test-query.cc @@ -30,7 +30,7 @@ #include "index/mu-indexer.hh" #include "utils/mu-result.hh" #include "utils/mu-utils.hh" -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" using namespace Mu; @@ -87,20 +87,10 @@ test_query() int main(int argc, char* argv[]) try { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); g_test_add_func("/query", test_query); - - if (!g_test_verbose()) - g_log_set_handler( - NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | - G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); - - return g_test_run(); } catch (const std::runtime_error& re) { diff --git a/lib/utils/meson.build b/lib/utils/meson.build index ad466b2a..62773962 100644 --- a/lib/utils/meson.build +++ b/lib/utils/meson.build @@ -1,4 +1,4 @@ -## Copyright (C) 2021 Dirk-Jan C. Binnema +## Copyright (C) 2022 Dirk-Jan C. Binnema ## ## 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 @@ -21,6 +21,7 @@ lib_mu_utils=static_library('mu-utils', [ 'mu-option.cc', 'mu-readline.cc', 'mu-sexp.cc', + 'mu-test-utils.cc', 'mu-util.c', 'mu-util.h', 'mu-utils.cc'], diff --git a/lib/utils/mu-test-utils.cc b/lib/utils/mu-test-utils.cc new file mode 100644 index 00000000..8e049b24 --- /dev/null +++ b/lib/utils/mu-test-utils.cc @@ -0,0 +1,150 @@ +/* +** Copyright (C) 2008-2022 Dirk-Jan C. Binnema +** +** 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. +** +*/ + +#include "config.h" + +#include +#include + +#include +#include +#include + +#include +#include + +#include "utils/mu-test-utils.hh" +#include "utils/mu-error.hh" + + +using namespace Mu; + +char* +Mu::test_mu_common_get_random_tmpdir() +{ + char* dir; + int res; + + dir = g_strdup_printf("%s%cmu-test-%d%ctest-%x", + g_get_tmp_dir(), + G_DIR_SEPARATOR, + getuid(), + G_DIR_SEPARATOR, + (int)random() * getpid() * (int)time(NULL)); + + res = g_mkdir_with_parents(dir, 0700); + g_assert(res != -1); + + return dir; +} + +const char* +Mu::set_tz(const char* tz) +{ + static const char* oldtz; + + oldtz = getenv("TZ"); + if (tz) + setenv("TZ", tz, 1); + else + unsetenv("TZ"); + + tzset(); + return oldtz; +} + +bool +Mu::set_en_us_utf8_locale() +{ + setenv("LC_ALL", "en_US.UTF-8", 1); + setlocale(LC_ALL, "en_US.UTF-8"); + + if (strcmp(nl_langinfo(CODESET), "UTF-8") != 0) { + g_print("Note: Unit tests require the en_US.utf8 locale. " + "Ignoring test cases.\n"); + return FALSE; + } + + return TRUE; +} + +static void +black_hole(void) +{ + return; /* do nothing */ +} + +void +Mu::mu_test_init(int *argc, char ***argv) +{ + g_test_init(argc, argv, NULL); + + if (!g_test_verbose()) + g_log_set_handler( + NULL, + (GLogLevelFlags)(G_LOG_LEVEL_MASK | + G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), + (GLogFunc)black_hole, NULL); +} + + + +void +Mu::allow_warnings() +{ + g_test_log_set_fatal_handler( + [](const char*, GLogLevelFlags, const char*, gpointer) { return FALSE; }, + {}); +} + + + +Mu::TempDir::TempDir(bool autodelete): autodelete_{autodelete} +{ + GError *err{}; + gchar *tmpdir = g_dir_make_tmp("mu-tmp-XXXXXX", &err); + if (!tmpdir) + throw Mu::Error(Error::Code::File, &err, + "failed to create temporary directory"); + + path_ = tmpdir; + g_free(tmpdir); + + g_debug("created '%s'", path_.c_str()); +} + +Mu::TempDir::~TempDir() +{ + if (::access(path_.c_str(), F_OK) != 0) + return; /* nothing to do */ + + if (!autodelete_) { + g_debug("_not_ deleting %s", path_.c_str()); + return; + } + + /* ugly */ + GError *err{}; + const auto cmd{format("/bin/rm -rf '%s'", path_.c_str())}; + if (!g_spawn_command_line_sync(cmd.c_str(), NULL, NULL, NULL, &err)) { + g_warning("error: %s\n", err ? err->message : "?"); + g_clear_error(&err); + } else + g_debug("removed '%s'", path_.c_str()); +} diff --git a/lib/utils/mu-test-utils.hh b/lib/utils/mu-test-utils.hh new file mode 100644 index 00000000..c59b9d5a --- /dev/null +++ b/lib/utils/mu-test-utils.hh @@ -0,0 +1,129 @@ +/* +** Copyright (C) 2008-2022 Dirk-Jan C. Binnema +** +** 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. +** +*/ + +#ifndef MU_TEST_UTILS_HH__ +#define MU_TEST_UTILS_HH__ + +#include + +namespace Mu { + +/** + * get a dir name for a random temporary directory to do tests + * + * @return a random dir name, g_free when it's no longer needed + */ +char* test_mu_common_get_random_tmpdir(); + +/** + * mu wrapper for g_test_init + * + * @param argc + * @param argv + */ +void mu_test_init(int *argc, char ***argv); + +/** + * set the timezone + * + * @param tz timezone + * + * @return the old timezone + */ +const char* set_tz(const char* tz); + +/** + * switch the locale to en_US.utf8, return TRUE if it succeeds + * + * @return true if the switch succeeds, false otherwise + */ +bool set_en_us_utf8_locale(); + +/** + * For unit tests, assert two std::string's are equal. + * + * @param s1 string1 + * @param s2 string2 + */ +#define assert_equal(s1__,s2__) do { \ + std::string s1s__(s1__), s2s__(s2__); \ + g_assert_cmpstr(s1s__.c_str(), ==, s2s__.c_str()); \ + } while(0) + + +#define assert_equal_seq(seq1__, seq2__) do { \ + g_assert_cmpuint(seq1__.size(), ==, seq2__.size()); \ + size_t n__{}; \ + for (auto&& item__: seq1__) { \ + g_assert_true(item__ == seq2__.at(n__)); \ + ++n__; \ + } \ + } while(0) + +#define assert_equal_seq_str(seq1__, seq2__) do { \ + g_assert_cmpuint(seq1__.size(), ==, seq2__.size()); \ + size_t n__{}; \ + for (auto&& item__: seq1__) { \ + assert_equal(item__, seq2__.at(n__)); \ + ++n__; \ + } \ + } while(0) + +/** + * For unit-tests, allow warnings in the current function. + * + */ +void allow_warnings(); + + +/** + * For unit-tests, a RAII tempdir. + * + */ +struct TempDir { + /** + * Construct a temporary directory + */ + TempDir(bool autodelete=true); + + /** + * DTOR; removes the temporary directory + * + * + * @return + */ + ~TempDir(); + + /** + * Path to the temporary directory + * + * @return the path. + * + * + */ + const std::string& path() {return path_; } +private: + std::string path_; + const bool autodelete_; +}; + +} // namepace Mu + + +#endif /* MU_TEST_UTILS_HH__ */ diff --git a/lib/utils/mu-utils.cc b/lib/utils/mu-utils.cc index 0f9e6e36..f32eed72 100644 --- a/lib/utils/mu-utils.cc +++ b/lib/utils/mu-utils.cc @@ -585,7 +585,6 @@ Mu::from_lexnum(const std::string& str) } - std::string Mu::canonicalize_filename(const std::string& path, const std::string& relative_to) { @@ -602,50 +601,6 @@ Mu::canonicalize_filename(const std::string& path, const std::string& relative_t } -void -Mu::allow_warnings() -{ - g_test_log_set_fatal_handler( - [](const char*, GLogLevelFlags, const char*, gpointer) { return FALSE; }, - {}); -} - - - -Mu::TempDir::TempDir(bool autodelete): autodelete_{autodelete} -{ - GError *err{}; - gchar *tmpdir = g_dir_make_tmp("mu-tmp-XXXXXX", &err); - if (!tmpdir) - throw Mu::Error(Error::Code::File, &err, - "failed to create temporary directory"); - - path_ = tmpdir; - g_free(tmpdir); - - g_debug("created '%s'", path_.c_str()); -} - -Mu::TempDir::~TempDir() -{ - if (::access(path_.c_str(), F_OK) != 0) - return; /* nothing to do */ - - if (!autodelete_) { - g_debug("_not_ deleting %s", path_.c_str()); - return; - } - - /* ugly */ - GError *err{}; - const auto cmd{format("/bin/rm -rf '%s'", path_.c_str())}; - if (!g_spawn_command_line_sync(cmd.c_str(), NULL, NULL, NULL, &err)) { - g_warning("error: %s\n", err ? err->message : "?"); - g_clear_error(&err); - } else - g_debug("removed '%s'", path_.c_str()); -} - bool Mu::locale_workaround() try { diff --git a/lib/utils/mu-utils.hh b/lib/utils/mu-utils.hh index bfe14905..dd38416c 100644 --- a/lib/utils/mu-utils.hh +++ b/lib/utils/mu-utils.hh @@ -454,74 +454,6 @@ private: constexpr ET& operator|=(ET& e1, ET e2) { return e1 = e1 | e2; } \ static_assert(1==1) // require a semicolon -/** - * For unit tests, assert two std::string's are equal. - * - * @param s1 string1 - * @param s2 string2 - */ -#define assert_equal(s1__,s2__) do { \ - std::string s1s__(s1__), s2s__(s2__); \ - g_assert_cmpstr(s1s__.c_str(), ==, s2s__.c_str()); \ - } while(0) - - -#define assert_equal_seq(seq1__, seq2__) do { \ - g_assert_cmpuint(seq1__.size(), ==, seq2__.size()); \ - size_t n__{}; \ - for (auto&& item__: seq1__) { \ - g_assert_true(item__ == seq2__.at(n__)); \ - ++n__; \ - } \ - } while(0) - -#define assert_equal_seq_str(seq1__, seq2__) do { \ - g_assert_cmpuint(seq1__.size(), ==, seq2__.size()); \ - size_t n__{}; \ - for (auto&& item__: seq1__) { \ - assert_equal(item__, seq2__.at(n__)); \ - ++n__; \ - } \ - } while(0) - -/** - * For unit-tests, allow warnings in the current function. - * - */ -void allow_warnings(); - - -/** - * For unit-tests, a RAII tempdir. - * - */ -struct TempDir { - /** - * Construct a temporary directory - */ - TempDir(bool autodelete=true); - - /** - * DTOR; removes the temporary directory - * - * - * @return - */ - ~TempDir(); - - /** - * Path to the temporary directory - * - * @return the path. - * - * - */ - const std::string& path() {return path_; } -private: - std::string path_; - const bool autodelete_; -}; - } // namespace Mu #endif /* __MU_UTILS_HH__ */ diff --git a/lib/utils/tests/test-command-parser.cc b/lib/utils/tests/test-command-parser.cc index 44c9a9cd..4156b03b 100644 --- a/lib/utils/tests/test-command-parser.cc +++ b/lib/utils/tests/test-command-parser.cc @@ -1,5 +1,5 @@ /* -** Copyright (C) 2020 Dirk-Jan C. Binnema +** Copyright (C) 2022 Dirk-Jan C. Binnema ** ** This library is free software; you can redistribute it and/or ** modify it under the terms of the GNU Lesser General Public License @@ -25,6 +25,7 @@ #include "mu-command-parser.hh" #include "mu-utils.hh" +#include "mu-test-utils.hh" using namespace Mu; @@ -68,9 +69,9 @@ test_command() cmap.emplace( "my-command", CommandInfo{ArgMap{{":param1", ArgInfo{Sexp::Type::String, true, "some string"}}, - {":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}}, - "My command,", - {}}); + {":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}}, + "My command,", + {}}); g_assert_true(call(cmap, "(my-command :param1 \"hello\")")); g_assert_true(call(cmap, "(my-command :param1 \"hello\" :param2 123)")); @@ -86,12 +87,12 @@ test_command2() CommandMap cmap; cmap.emplace("bla", - CommandInfo{ArgMap{ + CommandInfo{ArgMap{ {":foo", ArgInfo{Sexp::Type::Number, false, "foo"}}, {":bar", ArgInfo{Sexp::Type::String, false, "bar"}}, }, - "yeah", - [&](const auto& params) {}}); + "yeah", + [&](const auto& params) {}}); g_assert_true(call(cmap, "(bla :foo nil)")); g_assert_false(call(cmap, "(bla :foo nil :bla nil)")); @@ -109,9 +110,9 @@ test_command_fail() cmap.emplace( "my-command", CommandInfo{ArgMap{{":param1", ArgInfo{Sexp::Type::String, true, "some string"}}, - {":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}}, - "My command,", - {}}); + {":param2", ArgInfo{Sexp::Type::Number, false, "some integer"}}}, + "My command,", + {}}); g_assert_false(call(cmap, "(my-command)")); g_assert_false(call(cmap, "(my-command2)")); @@ -125,9 +126,9 @@ black_hole() } int -main(int argc, char* argv[]) -try { - g_test_init(&argc, &argv, NULL); +main(int argc, char* argv[]) try { + + mu_test_init(&argc, &argv); g_test_add_func("/utils/command-parser/param-getters", test_param_getters); g_test_add_func("/utils/command-parser/command", test_command); diff --git a/lib/utils/tests/test-sexp.cc b/lib/utils/tests/test-sexp.cc index 770a1570..3cb1c5a5 100644 --- a/lib/utils/tests/test-sexp.cc +++ b/lib/utils/tests/test-sexp.cc @@ -25,6 +25,7 @@ #include "mu-command-parser.hh" #include "mu-utils.hh" +#include "mu-test-utils.hh" using namespace Mu; @@ -168,7 +169,7 @@ test_prop_list_remove() int main(int argc, char* argv[]) try { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); if (argc == 2) { std::cout << Sexp::make_parse(argv[1]) << '\n'; diff --git a/lib/utils/tests/test-utils.cc b/lib/utils/tests/test-utils.cc index 62399450..b69c705f 100644 --- a/lib/utils/tests/test-utils.cc +++ b/lib/utils/tests/test-utils.cc @@ -26,6 +26,7 @@ #include #include "mu-utils.hh" +#include "mu-test-utils.hh" #include "mu-error.hh" using namespace Mu; @@ -301,7 +302,7 @@ test_error() int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, nullptr); + mu_test_init(&argc, &argv); g_test_add_func("/utils/date-basic", test_date_basic); g_test_add_func("/utils/date-ymwdhMs", test_date_ymwdhMs); diff --git a/mu/tests/meson.build b/mu/tests/meson.build index 0f63e794..8aeaec53 100644 --- a/mu/tests/meson.build +++ b/mu/tests/meson.build @@ -23,18 +23,18 @@ test('test-cmd', executable('test-cmd', 'test-mu-cmd.cc', install: false, - dependencies: [glib_dep, lib_test_mu_common_dep, config_h_dep, lib_mu_dep])) + dependencies: [glib_dep, config_h_dep, lib_mu_dep])) test('test-cmd-cfind', executable('test-cmd-cfind', 'test-mu-cmd-cfind.cc', install: false, - dependencies: [glib_dep, lib_test_mu_common_dep, config_h_dep, lib_mu_dep])) + dependencies: [glib_dep, config_h_dep, lib_mu_dep])) test('test-cmd-query', executable('test-cmd-query', 'test-mu-query.cc', install: false, - dependencies: [glib_dep, lib_test_mu_common_dep, config_h_dep, lib_mu_dep])) + dependencies: [glib_dep, config_h_dep, lib_mu_dep])) gmime_test = executable( 'gmime-test', [ diff --git a/mu/tests/test-mu-cmd-cfind.cc b/mu/tests/test-mu-cmd-cfind.cc index 19d7e5df..8aec0c2b 100644 --- a/mu/tests/test-mu-cmd-cfind.cc +++ b/mu/tests/test-mu-cmd-cfind.cc @@ -27,7 +27,7 @@ #include #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-store.hh" #include "mu-query.hh" #include "utils/mu-utils.hh" @@ -325,7 +325,7 @@ test_mu_cfind_csv(void) int main(int argc, char* argv[]) { - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); if (!set_en_us_utf8_locale()) return 0; /* don't error out... */ @@ -341,11 +341,5 @@ main(int argc, char* argv[]) g_test_add_func("/mu-cmd-cfind/test-mu-cfind-org-contact", test_mu_cfind_org_contact); g_test_add_func("/mu-cmd-cfind/test-mu-cfind-csv", test_mu_cfind_csv); - g_log_set_handler(NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING | - G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); - return g_test_run(); } diff --git a/mu/tests/test-mu-cmd.cc b/mu/tests/test-mu-cmd.cc index c6990f7b..4933c66b 100644 --- a/mu/tests/test-mu-cmd.cc +++ b/mu/tests/test-mu-cmd.cc @@ -28,7 +28,7 @@ #include #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-store.hh" #include "mu-query.hh" #include "utils/mu-result.hh" @@ -829,8 +829,7 @@ main(int argc, char* argv[]) if (!g_getenv("RUN_TEST_MU_CMD")) return 0; - - g_test_init(&argc, &argv, NULL); + mu_test_init(&argc, &argv); if (!set_en_us_utf8_locale()) return 0; /* don't error out... */ @@ -870,12 +869,6 @@ main(int argc, char* argv[]) g_test_add_func("/mu-cmd/test-mu-verify-good", test_mu_verify_good); g_test_add_func("/mu-cmd/test-mu-verify-bad", test_mu_verify_bad); - g_log_set_handler(NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_LEVEL_WARNING | - G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); - TempDir tempdir; DBPATH = tempdir.path(); fill_database(); diff --git a/mu/tests/test-mu-query.cc b/mu/tests/test-mu-query.cc index ca2b73a6..283f2e9a 100644 --- a/mu/tests/test-mu-query.cc +++ b/mu/tests/test-mu-query.cc @@ -30,7 +30,7 @@ #include #include -#include "test-mu-common.hh" +#include "utils/mu-test-utils.hh" #include "mu-query.hh" #include "utils/mu-result.hh" #include "utils/mu-utils.hh" @@ -625,7 +625,7 @@ main(int argc, char* argv[]) setlocale(LC_ALL, ""); - g_test_init(&argc, &argv, nullptr); + mu_test_init(&argc, &argv); DB_PATH1 = make_database(MU_TESTMAILDIR); g_assert_false(DB_PATH1.empty()); @@ -668,13 +668,6 @@ main(int argc, char* argv[]) g_test_add_func("/mu-query/test-mu-query-cjk", test_mu_query_cjk); - - if (!g_test_verbose()) - g_log_set_handler(NULL, - (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | - G_LOG_LEVEL_WARNING | G_LOG_FLAG_RECURSION), - (GLogFunc)black_hole, - NULL); rv = g_test_run(); return rv; From b1013d8f0f50c618f21fea7a85ad7af62c3f095b Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Thu, 11 Aug 2022 23:01:29 +0300 Subject: [PATCH 3/5] message: update references() implementation Clean up the implementation at bit, and filter out 'fake' message-ids, such as the ones from protonmail. Update documentation. Add Mu::Message::thread_id(). This fixes #2312. --- lib/message/mu-message.hh | 21 +++++++++++---- lib/message/mu-mime-object.cc | 32 ++++++++++++++--------- lib/message/mu-mime-object.hh | 4 ++- lib/message/test-mu-message.cc | 48 ++++++++++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 19 deletions(-) diff --git a/lib/message/mu-message.hh b/lib/message/mu-message.hh index 043ef424..3f2e001b 100644 --- a/lib/message/mu-message.hh +++ b/lib/message/mu-message.hh @@ -301,10 +301,11 @@ public: size_t size() const { return static_cast(document().integer_value(Field::Id::Size)); } /** - * get the list of references (consisting of both the References and - * In-Reply-To fields), with the oldest first and the direct parent as - * the last one. Note, any reference (message-id) will appear at most - * once, duplicates are filtered out. + * Get the (possibly empty) list of references (consisting of both the + * References and In-Reply-To fields), with the oldest first and the + * direct parent as the last one. Note, any reference (message-id) will + * appear at most once, duplicates and fake-message-id (see impls) are + * filtered out. * * @return a vec with the references for this msg. */ @@ -312,6 +313,17 @@ public: return document().string_vec_value(Field::Id::References); } + /** + * Get the thread-id for this message. This is the message-id of the + * oldest-known (grand) parent, or the message-id of this message if + * none. + * + * @return the thread id. + */ + std::string thread_id() const { + return document().string_value(Field::Id::ThreadId); + } + /** * get the list of tags (ie., X-Label) * @@ -409,7 +421,6 @@ public: using Part = MessagePart; const std::vector& parts() const; - /** * Get the path to a cche directory for this message, which * is useful for temporarily saving attachments diff --git a/lib/message/mu-mime-object.cc b/lib/message/mu-mime-object.cc index 1b0783b4..7e5734fd 100644 --- a/lib/message/mu-mime-object.cc +++ b/lib/message/mu-mime-object.cc @@ -397,22 +397,31 @@ MimeMessage::contacts(Contact::Type ctype) const noexcept return contacts; } - - +/* + * references() returns the concatenation of the References and In-Reply-To + * message-ids (in that order). Duplicates are removed. + * + * The _first_ one in the list determines the thread-id for the message. + */ std::vector MimeMessage::references() const noexcept { - constexpr std::array ref_headers = { - "References", "In-reply-to", - }; - - // is ref already in the list? + // is ref already in the list? O(n) but with small n. auto is_dup = [](auto&& seq, const std::string& ref) { return seq_some(seq, [&](auto&& str) { return ref == str; }); }; + auto is_fake = [](auto&& msgid) { + // this is bit ugly; protonmail injects fake References which + // can otherwise screw up threading. + if (g_str_has_suffix(msgid, "protonmail.internalid")) + return true; + /* ... */ + return false; + }; + std::vector refs; - for (auto&& ref_header: ref_headers) { + for (auto&& ref_header: { "References", "In-reply-to" }) { auto hdr{header(ref_header)}; if (!hdr) @@ -422,12 +431,9 @@ MimeMessage::references() const noexcept refs.reserve(refs.size() + g_mime_references_length(mime_refs)); for (auto i = 0; i != g_mime_references_length(mime_refs); ++i) { - const auto msgid{g_mime_references_get_message_id(mime_refs, i)}; - if (!msgid || is_dup(refs, msgid)) - continue; // invalid or skip dups - - refs.emplace_back(msgid); + if (msgid && !is_dup(refs, msgid) && !is_fake(msgid)) + refs.emplace_back(msgid); } g_mime_references_free(mime_refs); } diff --git a/lib/message/mu-mime-object.hh b/lib/message/mu-mime-object.hh index 1eee7567..56a4b56c 100644 --- a/lib/message/mu-mime-object.hh +++ b/lib/message/mu-mime-object.hh @@ -973,7 +973,9 @@ public: /** * Get the references for this message (including in-reply-to), in the - * order of older..newer; in-reply-to would be the last one. + * order of older..newer; the first one would the oldest parent, and + * in-reply-to would be the last one (if any). These are de-duplicated, + * and known-fake references removed (see implementation) * * @return references. */ diff --git a/lib/message/test-mu-message.cc b/lib/message/test-mu-message.cc index 8d85fae2..6d19488e 100644 --- a/lib/message/test-mu-message.cc +++ b/lib/message/test-mu-message.cc @@ -808,6 +808,52 @@ RU5EOlZFVkVOVA0KRU5EOlZDQUxFTkRBUg0K } +static void +test_message_references() +{ + constexpr auto msgtext = +R"(Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=utf-8 +References: + <90a760c4-6e88-07b4-1f20-8b10414e49aa@arm.com> + +To: "Robin Murphy" +Reply-To: "Dan Carpenter" +From: "Dan Carpenter" +Subject: Re: [PATCH] iommu/omap: fix buffer overflow in debugfs +List-Id: +Date: Fri, 5 Aug 2022 09:37:02 +0300 +In-Reply-To: <90a760c4-6e88-07b4-1f20-8b10414e49aa@arm.com> +Precedence: bulk +Message-Id: <20220805063702.GH3438@kadam> + +On Thu, Aug 04, 2022 at 05:31:39PM +0100, Robin Murphy wrote: +> On 04/08/2022 3:32 pm, Dan Carpenter wrote: +> > There are two issues here: +)"; + auto message{Message::make_from_text( + msgtext, + "/home/test/Maildir/inbox/cur/162342449279256.88888_1.evergrey:2,S")}; + g_assert_true(!!message); + assert_equal(message->subject(), + "Re: [PATCH] iommu/omap: fix buffer overflow in debugfs"); + g_assert_true(message->priority() == Priority::Low); + + /* + * "90a760c4-6e88-07b4-1f20-8b10414e49aa@arm.com" is seen both in + * references and in-reply-to; in the de-duplication, the first one wins. + */ + std::vector expected_refs = { + "YuvYh1JbE3v+abd5@kili", + "90a760c4-6e88-07b4-1f20-8b10414e49aa@arm.com", + /* protonmail.internalid is fake and removed */ + // "T4CDWjUrgtI5n4mh1JEdW6RLYzqbPE9-yDrhEVwDM22WX-198fBwcnLd-4_" + // "xR1gvsVSHQps9fp_pZevTF0ZmaA==@protonmail.internalid" + }; + + assert_equal_seq_str(expected_refs, message->references()); +} + static void test_message_fail () @@ -850,6 +896,8 @@ main(int argc, char* argv[]) test_message_detect_attachment); g_test_add_func("/message/message/calendar", test_message_calendar); + g_test_add_func("/message/message/references", + test_message_references); g_test_add_func("/message/message/fail", test_message_fail); g_test_add_func("/message/message/sanitize-maildir", From 52697fd1327f44f97d6c7c943fdb5d7e650f5194 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Thu, 11 Aug 2022 23:06:12 +0300 Subject: [PATCH 4/5] tests: add unit-tests for references with fake message-ids For #2312. --- lib/tests/test-mu-store-query.cc | 116 +++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/lib/tests/test-mu-store-query.cc b/lib/tests/test-mu-store-query.cc index e24b7c28..84bbd82d 100644 --- a/lib/tests/test-mu-store-query.cc +++ b/lib/tests/test-mu-store-query.cc @@ -336,6 +336,120 @@ Child } } + +static void +test_related_missing_root() +{ + const TestMap test_msgs = {{ +{ +"inbox/cur/msg1:2,S", +R"(Content-Type: text/plain; charset=utf-8 +References: +To: "Joerg Roedel" , "Suman Anna" +Reply-To: "Dan Carpenter" +From: "Dan Carpenter" +Subject: [PATCH] iommu/omap: fix buffer overflow in debugfs +Date: Thu, 4 Aug 2022 17:32:39 +0300 +Message-Id: +List-Id: +Precedence: bulk + +There are two issues here: +)"}, +{ +"inbox/cur/msg2:2,S", +R"(Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=utf-8 +References: + <9pEUi_xoxa7NskF7EK_qfrlgjXzGsyw9K7cMfYbo-KI6fnyVMKTpc8E2Fu94V8xedd7cMpn0LlBrr9klBMflpw==@protonmail.internalid> +Reply-To: "Laurent Pinchart" +From: "Laurent Pinchart" +Subject: Re: [PATCH] iommu/omap: fix buffer overflow in debugfs +List-Id: +Message-Id: +Precedence: bulk +In-Reply-To: + +Hi Dan, + +Thank you for the patch. +)"}, +{ +"inbox/cur/msg3:2,S", +R"(Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=utf-8 +References: + +To: "Dan Carpenter" , "Joerg Roedel" + , "Suman Anna" +Reply-To: "Robin Murphy" +From: "Robin Murphy" +Subject: Re: [PATCH] iommu/omap: fix buffer overflow in debugfs +List-Id: +Message-Id: <90a760c4-6e88-07b4-1f20-8b10414e49aa@arm.com> +Precedence: bulk +In-Reply-To: +Date: Thu, 4 Aug 2022 17:31:39 +0100 + +On 04/08/2022 3:32 pm, Dan Carpenter wrote: +> There are two issues here: +)"}, +{ +"inbox/new/msg4", +R"(Content-Transfer-Encoding: quoted-printable +Content-Type: text/plain; charset=utf-8 +References: + <90a760c4-6e88-07b4-1f20-8b10414e49aa@arm.com> + +To: "Robin Murphy" +Reply-To: "Dan Carpenter" +From: "Dan Carpenter" +Subject: Re: [PATCH] iommu/omap: fix buffer overflow in debugfs +List-Id: +Date: Fri, 5 Aug 2022 09:37:02 +0300 +In-Reply-To: <90a760c4-6e88-07b4-1f20-8b10414e49aa@arm.com> +Precedence: bulk +Message-Id: <20220805063702.GH3438@kadam> + +On Thu, Aug 04, 2022 at 05:31:39PM +0100, Robin Murphy wrote: +> On 04/08/2022 3:32 pm, Dan Carpenter wrote: +> > There are two issues here: +)"}, +}}; + TempDir tdir; + auto store{make_test_store(tdir.path(), test_msgs, {})}; + { + auto qr = store.run_query("fix buffer overflow in debugfs", + Field::Id::Date, QueryFlags::IncludeRelated); + g_assert_true(!!qr); + g_assert_cmpuint(qr->size(), ==, 4); + } + + { + auto qr = store.run_query("fix buffer overflow in debugfs and flag:unread", + Field::Id::Date, QueryFlags::None); + g_assert_true(!!qr); + g_assert_cmpuint(qr->size(), ==, 1); + assert_equal(qr->begin().message_id().value_or(""), "20220805063702.GH3438@kadam"); + assert_equal(qr->begin().thread_id().value_or(""), "YuvYh1JbE3v+abd5@kili"); + } + + { + /* this one failed earlier, because the 'protonmail' id is the + * first reference, which means it does _not_ have the same + * thread-id as the rest; however, we filter these + * fake-message-ids now.*/ + g_test_bug("2312"); + + auto qr = store.run_query("fix buffer overflow in debugfs and flag:unread", + Field::Id::Date, QueryFlags::IncludeRelated); + g_assert_true(!!qr); + g_assert_cmpuint(qr->size(), ==, 4); + } +} + + + int main(int argc, char* argv[]) { @@ -348,6 +462,8 @@ main(int argc, char* argv[]) test_spam_address_components); g_test_add_func("/store/query/dups-related", test_dups_related); + g_test_add_func("/store/query/related-missing-root", + test_related_missing_root); return g_test_run(); } From cf4201c1ee73350e151d752dab1b56a229345633 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Mon, 15 Aug 2022 23:11:39 +0300 Subject: [PATCH 5/5] build: bump version to 1.8.9 --- configure.ac | 2 +- meson.build | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 05e747b1..b2de0b49 100644 --- a/configure.ac +++ b/configure.ac @@ -15,7 +15,7 @@ ## Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. AC_PREREQ([2.68]) -AC_INIT([mu],[1.8.8],[https://github.com/djcb/mu/issues],[mu]) +AC_INIT([mu],[1.8.9],[https://github.com/djcb/mu/issues],[mu]) AC_COPYRIGHT([Copyright (C) 2008-2022 Dirk-Jan C. Binnema]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_SRCDIR([mu/mu.cc]) diff --git a/meson.build b/meson.build index d9cd8f9f..2d49c95b 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ # project setup # project('mu', ['c', 'cpp'], - version: '1.8.8', + version: '1.8.9', meson_version: '>= 0.52.0', # debian 10 license: 'GPL-3.0-or-later', default_options : [