mirror of https://github.com/djcb/mu.git
store: update to use Message; big cleanup
Remove much of the message processing from the store
This commit is contained in:
parent
459f6db476
commit
525fef479a
|
@ -216,6 +216,26 @@ Indexer::Private::maybe_start_worker()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
add_message(Store& store, const std::string& path)
|
||||||
|
{
|
||||||
|
auto msg{Message::make_from_path(path)};
|
||||||
|
if (!msg) {
|
||||||
|
g_warning("failed to create message from %s: %s",
|
||||||
|
path.c_str(), msg.error().what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto res = store.add_message(msg.value(), true /*use-transaction*/);
|
||||||
|
if (!res) {
|
||||||
|
g_warning("failed to add message @ %s: %s",
|
||||||
|
path.c_str(), res.error().what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
Indexer::Private::item_worker()
|
Indexer::Private::item_worker()
|
||||||
{
|
{
|
||||||
|
@ -230,8 +250,7 @@ Indexer::Private::item_worker()
|
||||||
std::lock_guard lock{w_lock_};
|
std::lock_guard lock{w_lock_};
|
||||||
switch (item.type) {
|
switch (item.type) {
|
||||||
case WorkItem::Type::File:
|
case WorkItem::Type::File:
|
||||||
store_.add_message(item.full_path,
|
if (add_message(store_, item.full_path))
|
||||||
true /*use-transaction*/);
|
|
||||||
++progress_.updated;
|
++progress_.updated;
|
||||||
break;
|
break;
|
||||||
case WorkItem::Type::Dir:
|
case WorkItem::Type::Dir:
|
||||||
|
@ -328,10 +347,9 @@ Indexer::Private::start(const Indexer::Config& conf)
|
||||||
|
|
||||||
conf_ = conf;
|
conf_ = conf;
|
||||||
if (conf_.max_threads == 0) {
|
if (conf_.max_threads == 0) {
|
||||||
/* we're blocked mostly by a) filesystem and b) database;
|
/* note, most time is spent in the (single) db thread
|
||||||
* so it's not very useful to start many threads */
|
* but creating messages in parallel still helps a bit */
|
||||||
max_workers_ = std::max(
|
max_workers_ = std::thread::hardware_concurrency()/2;
|
||||||
4U, std::thread::hardware_concurrency());
|
|
||||||
} else
|
} else
|
||||||
max_workers_ = conf.max_threads;
|
max_workers_ = conf.max_threads;
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,8 @@ struct Store::Private {
|
||||||
: read_only_{readonly}, db_{make_xapian_db(path,
|
: read_only_{readonly}, db_{make_xapian_db(path,
|
||||||
read_only_ ? XapianOpts::ReadOnly
|
read_only_ ? XapianOpts::ReadOnly
|
||||||
: XapianOpts::Open)},
|
: XapianOpts::Open)},
|
||||||
properties_{make_properties(path)}, contacts_cache_{db().get_metadata(ContactsKey),
|
properties_{make_properties(path)},
|
||||||
|
contacts_cache_{db().get_metadata(ContactsKey),
|
||||||
properties_.personal_addresses}
|
properties_.personal_addresses}
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
@ -218,7 +219,7 @@ struct Store::Private {
|
||||||
: DefaultMaxMessageSize;
|
: DefaultMaxMessageSize;
|
||||||
writable_db().set_metadata(MaxMessageSizeKey, Mu::format("%zu", max_msg_size));
|
writable_db().set_metadata(MaxMessageSizeKey, Mu::format("%zu", max_msg_size));
|
||||||
|
|
||||||
writable_db().set_metadata(RootMaildirKey, root_maildir);
|
writable_db().set_metadata(RootMaildirKey, canonicalize_filename(root_maildir, {}));
|
||||||
|
|
||||||
std::string addrs;
|
std::string addrs;
|
||||||
for (const auto& addr : personal_addresses) { // _very_ minimal check.
|
for (const auto& addr : personal_addresses) { // _very_ minimal check.
|
||||||
|
@ -318,60 +319,54 @@ Store::empty() const
|
||||||
return size() == 0;
|
return size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string
|
|
||||||
maildir_from_path(const std::string& root, const std::string& path)
|
|
||||||
{
|
|
||||||
if (G_UNLIKELY(root.empty()) || root.length() >= path.length() || path.find(root) != 0)
|
|
||||||
throw Mu::Error{Error::Code::InvalidArgument,
|
|
||||||
"root '%s' is not a proper suffix of path '%s'",
|
|
||||||
root.c_str(),
|
|
||||||
path.c_str()};
|
|
||||||
|
|
||||||
auto mdir{path.substr(root.length())};
|
|
||||||
auto slash{mdir.rfind('/')};
|
|
||||||
|
|
||||||
if (G_UNLIKELY(slash == std::string::npos) || slash < 4)
|
|
||||||
throw Mu::Error{Error::Code::InvalidArgument, "invalid path: %s", path.c_str()};
|
|
||||||
mdir.erase(slash);
|
|
||||||
auto subdir = mdir.data() + slash - 4;
|
|
||||||
if (G_UNLIKELY(strncmp(subdir, "/cur", 4) != 0 && strncmp(subdir, "/new", 4)))
|
|
||||||
throw Mu::Error{Error::Code::InvalidArgument,
|
|
||||||
"cannot find '/new' or '/cur' - invalid path: %s",
|
|
||||||
path.c_str()};
|
|
||||||
if (mdir.length() == 4)
|
|
||||||
return "/";
|
|
||||||
|
|
||||||
mdir.erase(mdir.length() - 4);
|
|
||||||
return mdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<Store::Id>
|
Result<Store::Id>
|
||||||
Store::add_message(const std::string& path, bool use_transaction)
|
Store::add_message(const std::string& path, bool use_transaction)
|
||||||
{
|
{
|
||||||
const auto maildir{maildir_from_path(properties().root_maildir, path)};
|
if (auto msg{Message::make_from_path(path)}; !msg)
|
||||||
auto msg{Message::make_from_path(Message::Options::None, path, maildir)};
|
|
||||||
if (G_UNLIKELY(!msg))
|
|
||||||
return Err(msg.error());
|
return Err(msg.error());
|
||||||
|
else
|
||||||
|
return add_message(msg.value(), use_transaction);
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Store::Id>
|
||||||
|
Store::add_message(Message& msg, bool use_transaction)
|
||||||
|
{
|
||||||
|
const auto mdir{mu_maildir_from_path(msg.path(),
|
||||||
|
properties().root_maildir)};
|
||||||
|
if (!mdir)
|
||||||
|
return Err(mdir.error());
|
||||||
|
|
||||||
|
if (auto&& res = msg.set_maildir(mdir.value()); !res)
|
||||||
|
return Err(res.error());
|
||||||
|
|
||||||
std::lock_guard guard{priv_->lock_};
|
std::lock_guard guard{priv_->lock_};
|
||||||
|
|
||||||
|
priv_->contacts_cache_.add(msg.all_contacts());
|
||||||
|
|
||||||
|
const auto docid = xapian_try([&]{
|
||||||
|
|
||||||
if (use_transaction)
|
if (use_transaction)
|
||||||
priv_->transaction_inc();
|
priv_->transaction_inc();
|
||||||
|
|
||||||
const auto docid = priv_->writable_db().add_document(
|
const auto docid = priv_->writable_db().add_document(
|
||||||
msg->document().xapian_document());
|
msg.document().xapian_document());
|
||||||
|
|
||||||
if (use_transaction) /* commit if batch is full */
|
if (use_transaction) /* commit if batch is full */
|
||||||
priv_->transaction_maybe_commit();
|
priv_->transaction_maybe_commit();
|
||||||
|
|
||||||
|
return docid;
|
||||||
|
}, InvalidId);
|
||||||
|
|
||||||
if (G_UNLIKELY(docid == InvalidId))
|
if (G_UNLIKELY(docid == InvalidId))
|
||||||
return Err(Error::Code::Message, "failed to add message");
|
return Err(Error::Code::Message, "failed to add message");
|
||||||
|
|
||||||
g_debug("added message @ %s; docid = %u", path.c_str(), docid);
|
g_debug("added message @ %s; docid = %u", msg.path().c_str(), docid);
|
||||||
|
g_debug("%s", msg.document().xapian_document().get_description().c_str());
|
||||||
|
|
||||||
return Ok(static_cast<Store::Id>(docid));
|
return Ok(static_cast<Store::Id>(docid));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Store::update_message(const Message& msg, unsigned docid)
|
Store::update_message(const Message& msg, unsigned docid)
|
||||||
{
|
{
|
||||||
|
@ -379,8 +374,8 @@ Store::update_message(const Message& msg, unsigned docid)
|
||||||
[&]{
|
[&]{
|
||||||
priv_->writable_db().replace_document(
|
priv_->writable_db().replace_document(
|
||||||
docid, msg.document().xapian_document());
|
docid, msg.document().xapian_document());
|
||||||
|
g_debug("updated message @ %s; docid = %u",
|
||||||
g_debug("updated message %u @ %s", docid, msg.path().c_str());
|
msg.path().c_str(), docid);
|
||||||
return true;
|
return true;
|
||||||
}, false);
|
}, false);
|
||||||
}
|
}
|
||||||
|
@ -462,9 +457,6 @@ Store::move_message(Store::Id id,
|
||||||
return Ok(std::move(msg.value()));
|
return Ok(std::move(msg.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
Store::metadata(const std::string& key) const
|
Store::metadata(const std::string& key) const
|
||||||
{
|
{
|
||||||
|
@ -598,14 +590,12 @@ Store::lock() const
|
||||||
return priv_->lock_;
|
return priv_->lock_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Option<QueryResults>
|
Result<QueryResults>
|
||||||
Store::run_query(const std::string& expr,
|
Store::run_query(const std::string& expr,
|
||||||
Option<Field::Id> sortfield_id,
|
Field::Id sortfield_id,
|
||||||
QueryFlags flags, size_t maxnum) const
|
QueryFlags flags, size_t maxnum) const
|
||||||
{
|
{
|
||||||
return xapian_try([&] {
|
return Query{*this}.run(expr, sortfield_id, flags, maxnum);
|
||||||
Query q{*this};
|
|
||||||
return q.run(expr, sortfield_id, flags, maxnum);}, Nothing);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
|
|
|
@ -143,11 +143,11 @@ public:
|
||||||
* @param flags query flags
|
* @param flags query flags
|
||||||
* @param maxnum maximum number of results to return. 0 for 'no limit'
|
* @param maxnum maximum number of results to return. 0 for 'no limit'
|
||||||
*
|
*
|
||||||
* @return the query-results, or Nothing in case of error.
|
* @return the query-results or an error.
|
||||||
*/
|
*/
|
||||||
std::mutex& lock() const;
|
std::mutex& lock() const;
|
||||||
Option<QueryResults> run_query(const std::string& expr = "",
|
Result<QueryResults> run_query(const std::string& expr,
|
||||||
Option<Field::Id> sortfield_id = {},
|
Field::Id sortfield_id = Field::Id::Date,
|
||||||
QueryFlags flags = QueryFlags::None,
|
QueryFlags flags = QueryFlags::None,
|
||||||
size_t maxnum = 0) const;
|
size_t maxnum = 0) const;
|
||||||
|
|
||||||
|
@ -186,6 +186,19 @@ public:
|
||||||
*/
|
*/
|
||||||
Result<Id> add_message(const std::string& path, bool use_transaction = false);
|
Result<Id> add_message(const std::string& path, bool use_transaction = false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a message to the store. When planning to write many messages,
|
||||||
|
* it's much faster to do so in a transaction. If so, set
|
||||||
|
* @in_transaction to true. When done with adding messages, call
|
||||||
|
* commit().
|
||||||
|
*
|
||||||
|
* @param msg a message
|
||||||
|
* @param whether to bundle up to batch_size changes in a transaction
|
||||||
|
*
|
||||||
|
* @return the doc id of the added message or an error.
|
||||||
|
*/
|
||||||
|
Result<Id> add_message(Message& msg, bool use_transaction = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update a message in the store.
|
* Update a message in the store.
|
||||||
*
|
*
|
||||||
|
|
|
@ -52,31 +52,178 @@ test_store_ctor_dtor()
|
||||||
static void
|
static void
|
||||||
test_store_add_count_remove()
|
test_store_add_count_remove()
|
||||||
{
|
{
|
||||||
TempDir tempdir;
|
TempDir tempdir{false};
|
||||||
Mu::Store store{tempdir.path(), MuTestMaildir, {}, {}};
|
|
||||||
|
|
||||||
const auto id1 = store.add_message(MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,");
|
Mu::Store store{tempdir.path() + "/xapian", MuTestMaildir, {}, {}};
|
||||||
|
|
||||||
|
const auto msgpath{MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"};
|
||||||
|
const auto id1 = store.add_message(msgpath);
|
||||||
assert_valid_result(id1);
|
assert_valid_result(id1);
|
||||||
|
store.commit();
|
||||||
|
|
||||||
g_assert_cmpuint(store.size(), ==, 1);
|
g_assert_cmpuint(store.size(), ==, 1);
|
||||||
g_assert_true(store.contains_message(MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"));
|
g_assert_true(store.contains_message(msgpath));
|
||||||
|
|
||||||
|
g_assert_true(store.contains_message(msgpath));
|
||||||
|
|
||||||
const auto id2 = store.add_message(MuTestMaildir2 + "/bar/cur/mail3");
|
const auto id2 = store.add_message(MuTestMaildir2 + "/bar/cur/mail3");
|
||||||
assert_valid_result(id2);
|
g_assert_false(!!id2); // wrong maildir.
|
||||||
|
store.commit();
|
||||||
|
|
||||||
|
const auto msg3path{MuTestMaildir + "/cur/1252168370_3.14675.cthulhu!2,S"};
|
||||||
|
const auto id3 = store.add_message(msg3path);
|
||||||
|
assert_valid_result(id3);
|
||||||
|
|
||||||
g_assert_cmpuint(store.size(), ==, 2);
|
g_assert_cmpuint(store.size(), ==, 2);
|
||||||
g_assert_true(store.contains_message(MuTestMaildir2 + "/bar/cur/mail3"));
|
g_assert_true(store.contains_message(msg3path));
|
||||||
|
|
||||||
store.remove_message(id1.value());
|
store.remove_message(id1.value());
|
||||||
g_assert_cmpuint(store.size(), ==, 1);
|
g_assert_cmpuint(store.size(), ==, 1);
|
||||||
g_assert_false(
|
g_assert_false(
|
||||||
store.contains_message(MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"));
|
store.contains_message(MuTestMaildir + "/cur/1283599333.1840_11.cthulhu!2,"));
|
||||||
|
|
||||||
store.remove_message(MuTestMaildir2 + "/bar/cur/mail3");
|
store.remove_message(msg3path);
|
||||||
g_assert_true(store.empty());
|
g_assert_true(store.empty());
|
||||||
g_assert_false(store.contains_message(MuTestMaildir2 + "/bar/cur/mail3"));
|
g_assert_false(store.contains_message(msg3path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_message_mailing_list()
|
||||||
|
{
|
||||||
|
constexpr const char *test_message_1 =
|
||||||
|
R"(Return-Path: <sqlite-dev-bounces@sqlite.org>
|
||||||
|
X-Original-To: xxxx@localhost
|
||||||
|
Delivered-To: xxxx@localhost
|
||||||
|
Received: from mindcrime (localhost [127.0.0.1])
|
||||||
|
by mail.xxxxsoftware.nl (Postfix) with ESMTP id 32F276963F
|
||||||
|
for <xxxx@localhost>; Mon, 4 Aug 2008 21:49:34 +0300 (EEST)
|
||||||
|
Message-Id: <83B5AF40-DBFA-4578-A043-04C80276E195@sqlabs.net>
|
||||||
|
From: anon@example.com
|
||||||
|
To: sqlite-dev@sqlite.org
|
||||||
|
Mime-Version: 1.0 (Apple Message framework v926)
|
||||||
|
Date: Mon, 4 Aug 2008 11:40:49 +0200
|
||||||
|
X-Mailer: Apple Mail (2.926)
|
||||||
|
Subject: Capybaras United
|
||||||
|
Precedence: list
|
||||||
|
Reply-To: sqlite-dev@sqlite.org
|
||||||
|
List-Id: <sqlite-dev.sqlite.org>
|
||||||
|
Content-Type: text/plain; charset="us-ascii"
|
||||||
|
Content-Transfer-Encoding: 7bit
|
||||||
|
Sender: sqlite-dev-bounces@sqlite.org
|
||||||
|
Content-Length: 639
|
||||||
|
|
||||||
|
Inside sqlite3VdbeExec there is a very big switch statement.
|
||||||
|
In order to increase performance with few modifications to the
|
||||||
|
original code, why not use this technique ?
|
||||||
|
http://docs.freebsd.org/info/gcc/gcc.info.Labels_as_Values.html
|
||||||
|
|
||||||
|
With a properly defined "instructions" array, instead of the switch
|
||||||
|
statement you can use something like:
|
||||||
|
goto * instructions[pOp->opcode];
|
||||||
|
)";
|
||||||
|
TempDir tempdir;
|
||||||
|
Mu::Store store{tempdir.path(), "/home/test/Maildir", {}, {}};
|
||||||
|
|
||||||
|
const auto msgpath{"/home/test/Maildir/inbox/cur/1649279256.107710_1.evergrey:2,S"};
|
||||||
|
auto message{Message::make_from_text(test_message_1, msgpath)};
|
||||||
|
assert_valid_result(message);
|
||||||
|
|
||||||
|
const auto docid = store.add_message(*message);
|
||||||
|
assert_valid_result(docid);
|
||||||
|
g_assert_cmpuint(store.size(),==, 1);
|
||||||
|
|
||||||
|
auto msg2{store.find_message(*docid)};
|
||||||
|
g_assert_true(!!msg2);
|
||||||
|
assert_equal(message->path(), msg2->path());
|
||||||
|
|
||||||
|
g_assert_true(store.contains_message(message->path()));
|
||||||
|
|
||||||
|
const auto qr = store.run_query("to:sqlite-dev@sqlite.org");
|
||||||
|
g_assert_true(!!qr);
|
||||||
|
g_assert_cmpuint(qr->size(), ==, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_message_attachments(void)
|
||||||
|
{
|
||||||
|
constexpr const char* msg_text =
|
||||||
|
R"(Return-Path: <foo@example.com>
|
||||||
|
Received: from pop.gmail.com [256.85.129.309]
|
||||||
|
by evergrey with POP3 (fetchmail-6.4.29)
|
||||||
|
for <djcb@localhost> (single-drop); Thu, 24 Mar 2022 20:12:40 +0200 (EET)
|
||||||
|
Sender: "Foo, Example" <foo@example.com>
|
||||||
|
User-agent: mu4e 1.7.11; emacs 29.0.50
|
||||||
|
From: "Foo Example" <foo@example.com>
|
||||||
|
To: bar@example.com
|
||||||
|
Subject: =?utf-8?B?w6R0dMOkY2htZcOxdHM=?=
|
||||||
|
Date: Thu, 24 Mar 2022 20:04:39 +0200
|
||||||
|
Organization: ACME Inc.
|
||||||
|
Message-Id: <3144HPOJ0VC77.3H1XTAG2AMTLH@"@WILSONB.COM>
|
||||||
|
MIME-Version: 1.0
|
||||||
|
X-label: @NextActions operation:mindcrime Queensrÿche
|
||||||
|
Content-Type: multipart/mixed; boundary="=-=-="
|
||||||
|
|
||||||
|
--=-=-=
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
Hello,
|
||||||
|
--=-=-=
|
||||||
|
Content-Type: image/jpeg
|
||||||
|
Content-Disposition: attachment; filename=file-01.bin
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
|
AAECAw==
|
||||||
|
--=-=-=
|
||||||
|
Content-Type: audio/ogg
|
||||||
|
Content-Disposition: inline; filename=/tmp/file-02.bin
|
||||||
|
Content-Transfer-Encoding: base64
|
||||||
|
|
||||||
|
BAUGBw==
|
||||||
|
--=-=-=
|
||||||
|
Content-Type: message/rfc822
|
||||||
|
Content-Disposition: attachment;
|
||||||
|
filename="message.eml"
|
||||||
|
|
||||||
|
From: "Fnorb" <fnorb@example.com>
|
||||||
|
To: Bob <bob@example.com>
|
||||||
|
Subject: news for you
|
||||||
|
Date: Mon, 28 Mar 2022 22:53:26 +0300
|
||||||
|
|
||||||
|
Attached message!
|
||||||
|
|
||||||
|
--=-=-=
|
||||||
|
Content-Type: text/plain
|
||||||
|
|
||||||
|
World!
|
||||||
|
--=-=-=--
|
||||||
|
)";
|
||||||
|
|
||||||
|
TempDir tempdir;
|
||||||
|
Mu::Store store{tempdir.path(), "/home/test/Maildir", {}, {}};
|
||||||
|
|
||||||
|
auto message{Message::make_from_text(
|
||||||
|
msg_text,
|
||||||
|
"/home/test/Maildir/inbox/cur/1649279256.abcde_1.evergrey:2,S")};
|
||||||
|
assert_valid_result(message);
|
||||||
|
|
||||||
|
const auto docid = store.add_message(*message);
|
||||||
|
assert_valid_result(docid);
|
||||||
|
store.commit();
|
||||||
|
|
||||||
|
auto msg2{store.find_message(*docid)};
|
||||||
|
g_assert_true(!!msg2);
|
||||||
|
assert_equal(message->path(), msg2->path());
|
||||||
|
|
||||||
|
g_assert_true(store.contains_message(message->path()));
|
||||||
|
|
||||||
|
// for (auto&& term = msg2->document().xapian_document().termlist_begin();
|
||||||
|
// term != msg2->document().xapian_document().termlist_end(); ++term)
|
||||||
|
// g_message(">>> %s", (*term).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char* argv[])
|
main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
|
@ -85,11 +232,10 @@ main(int argc, char* argv[])
|
||||||
/* mu_runtime_init/uninit */
|
/* mu_runtime_init/uninit */
|
||||||
g_test_add_func("/store/ctor-dtor", test_store_ctor_dtor);
|
g_test_add_func("/store/ctor-dtor", test_store_ctor_dtor);
|
||||||
g_test_add_func("/store/add-count-remove", test_store_add_count_remove);
|
g_test_add_func("/store/add-count-remove", test_store_add_count_remove);
|
||||||
|
g_test_add_func("/store/message/mailing-list",
|
||||||
// if (!g_test_verbose())
|
test_message_mailing_list);
|
||||||
// g_log_set_handler (NULL,
|
g_test_add_func("/store/message/attachments",
|
||||||
// G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL| G_LOG_FLAG_RECURSION,
|
test_message_attachments);
|
||||||
// (GLogFunc)black_hole, NULL);
|
|
||||||
|
|
||||||
return g_test_run();
|
return g_test_run();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue