/*
  SPDX-FileCopyrightText: 2010 Thomas McGuire <thomas.mcguire@kdab.com>
  SPDX-FileCopyrightText: 2019 Sandro Knauß <sknauss@kde.org>

  SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "basicobjecttreeparsertest.h"
using namespace Qt::Literals::StringLiterals;

#include "setupenv.h"
#include "util.h"

#include <MimeTreeParser/MessagePart>
#include <MimeTreeParser/NodeHelper>
#include <MimeTreeParser/ObjectTreeParser>
#include <MimeTreeParser/SimpleObjectTreeSource>

#include <QProcess>
#include <QTest>

using namespace MimeTreeParser;

QTEST_MAIN(ObjectTreeParserTest)

void ObjectTreeParserTest::initTestCase()
{
    Test::setupEnv();
    qputenv("TZ", "GMT");
}

static QString stringifyMessagePartTree(const MimeTreeParser::MessagePart::Ptr &messagePart, QString indent)
{
    const QString line = u"%1 * %3\n"_s.arg(indent, QString::fromUtf8(messagePart->metaObject()->className()));
    QString ret = line;

    indent += u' ';
    const auto subParts = messagePart->subParts();
    for (const auto &subPart : subParts) {
        ret += stringifyMessagePartTree(subPart, indent);
    }
    return ret;
}

static void testMessagePartTree(const MessagePart::Ptr &messagePart, const QString &mailFileName)
{
    QString renderedTree = stringifyMessagePartTree(messagePart, QString());

    const QString treeFileName = QLatin1StringView(MAIL_DATA_DIR) + u'/' + mailFileName + u".tree"_s;
    const QString outTreeFileName = mailFileName + u".tree"_s;

    {
        QFile f(outTreeFileName);
        f.open(QIODevice::WriteOnly);
        f.write(renderedTree.toUtf8());
        f.close();
    }
    // compare to reference file
    const QStringList args = QStringList() << u"-u"_s << treeFileName << outTreeFileName;
    QProcess proc;
    proc.setProcessChannelMode(QProcess::ForwardedChannels);
    proc.start(u"diff"_s, args);
    QVERIFY(proc.waitForFinished());

    QCOMPARE(proc.exitCode(), 0);
}

void ObjectTreeParserTest::testMailWithoutEncryption()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"encapsulated-with-attachment.mbox"_s);
    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    otp.parseObjectTree(originalMessage.get());
    QVERIFY(!nodeHelper.unencryptedMessage(originalMessage));
}

void ObjectTreeParserTest::testBinaryAttachmentNotPGP()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"binary-attachment-not-pgp.mbox"_s);
    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(originalMessage->contentType()->mimeType().data(), "multipart/mixed");
    QCOMPARE(originalMessage->contents().size(), 2);
    QCOMPARE(originalMessage->contents().at(0)->contentType()->mimeType().data(), "text/plain");
    QCOMPARE(originalMessage->contents().at(1)->contentType()->mimeType().data(), "application/msword");

    QCOMPARE(nodeHelper.encryptionState(originalMessage->contents().at(0)), KMMsgNotEncrypted);
    QCOMPARE(nodeHelper.encryptionState(originalMessage->contents().at(1)), KMMsgNotEncrypted);
}

void ObjectTreeParserTest::testSignedForwardedOpenPGPSignedEncrypted()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"signed-forward-openpgp-signed-encrypted.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "bla bla bla"); // The textual content doesn't include the encrypted encapsulated message by design
    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgPartiallyEncrypted);
    QCOMPARE(nodeHelper.overallSignatureState(originalMessage.get()), KMMsgFullySigned);

    std::shared_ptr<KMime::Message> unencryptedMessage = nodeHelper.unencryptedMessage(originalMessage);
    QVERIFY(!unencryptedMessage); // We must not invalidate the outer signature
}

void ObjectTreeParserTest::testForwardedOpenPGPSignedEncrypted()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"forward-openpgp-signed-encrypted.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    testSource.setDecryptMessage(true);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "bla bla bla"); // The textual content doesn't include the encrypted encapsulated message by design
    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgPartiallyEncrypted);

    QCOMPARE(nodeHelper.overallSignatureState(originalMessage.get()), KMMsgPartiallySigned);

    // Now, test that the unencrypted message is generated correctly
    std::shared_ptr<KMime::Message> unencryptedMessage = nodeHelper.unencryptedMessage(originalMessage);
    QVERIFY(unencryptedMessage.get());
    QCOMPARE(unencryptedMessage->contentType()->mimeType().data(), "multipart/mixed");
    QCOMPARE(unencryptedMessage->contents().size(), 2);
    QCOMPARE(unencryptedMessage->contents().first()->contentType()->mimeType().data(), "text/plain");
    QCOMPARE(unencryptedMessage->contents().first()->decodedBody().data(), "bla bla bla");
    QCOMPARE(unencryptedMessage->contents().at(1)->contentType()->mimeType().data(), "message/rfc822");
    std::shared_ptr<KMime::Message> encapsulated = unencryptedMessage->contents().at(1)->bodyAsMessage();
    QCOMPARE(encapsulated->contentType()->mimeType().data(), "multipart/signed");
    QCOMPARE(encapsulated->contents().size(), 2);
    QCOMPARE(encapsulated->contents().first()->contentType()->mimeType().data(), "text/plain");
    QCOMPARE(encapsulated->contents().at(1)->contentType()->mimeType().data(), "application/pgp-signature");
    QCOMPARE(encapsulated->contents().first()->decodedBody().data(), "encrypted message text");

    // TODO: Check that the signature is valid
}

void ObjectTreeParserTest::testSMIMESignedEncrypted()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"smime-signed-encrypted.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    testSource.setDecryptMessage(true);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "encrypted message text");
    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgFullyEncrypted);

    QCOMPARE(nodeHelper.overallSignatureState(originalMessage.get()), KMMsgFullySigned);

    // Now, test that the unencrypted message is generated correctly
    std::shared_ptr<KMime::Message> unencryptedMessage = nodeHelper.unencryptedMessage(originalMessage);
    QCOMPARE(unencryptedMessage->contentType()->mimeType().data(), "multipart/signed");
    QCOMPARE(unencryptedMessage->contents().size(), 2);
    QCOMPARE(unencryptedMessage->contents().first()->contentType()->mimeType().data(), "text/plain");
    QCOMPARE(unencryptedMessage->contents().at(1)->contentType()->mimeType().data(), "application/pkcs7-signature");
    QCOMPARE(unencryptedMessage->contents().first()->decodedBody().data(), "encrypted message text");

    // TODO: Check that the signature is valid
}

void ObjectTreeParserTest::testOpenPGPSignedEncrypted()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"openpgp-signed-encrypted.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    testSource.setDecryptMessage(true);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "encrypted message text");
    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgFullyEncrypted);

    QCOMPARE(nodeHelper.overallSignatureState(originalMessage.get()), KMMsgFullySigned);

    // Now, test that the unencrypted message is generated correctly
    std::shared_ptr<KMime::Message> unencryptedMessage = nodeHelper.unencryptedMessage(originalMessage);
    QCOMPARE(unencryptedMessage->contentType()->mimeType().data(), "multipart/signed");
    QCOMPARE(unencryptedMessage->contents().size(), 2);
    QCOMPARE(unencryptedMessage->contents().first()->contentType()->mimeType().data(), "text/plain");
    QCOMPARE(unencryptedMessage->contents().at(1)->contentType()->mimeType().data(), "application/pgp-signature");
    QCOMPARE(unencryptedMessage->contents().first()->decodedBody().data(), "encrypted message text");

    // TODO: Check that the signature is valid
}

void ObjectTreeParserTest::testOpenPGPEncryptedAndSigned()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"openpgp-encrypted+signed.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    testSource.setDecryptMessage(true);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "encrypted message text");
    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgFullyEncrypted);

    QCOMPARE(nodeHelper.overallSignatureState(originalMessage.get()), KMMsgFullySigned);

    // Now, test that the unencrypted message is generated correctly
    std::shared_ptr<KMime::Message> unencryptedMessage = nodeHelper.unencryptedMessage(originalMessage);
    QCOMPARE(unencryptedMessage->contentType()->mimeType().data(), "text/plain");
    QCOMPARE(unencryptedMessage->contents().size(), 0);
    QCOMPARE(unencryptedMessage->decodedBody().data(), "encrypted message text");

    // TODO: Check that the signature is valid
}

void ObjectTreeParserTest::testOpenPGPEncrypted()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"openpgp-encrypted.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    testSource.setDecryptMessage(true);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "encrypted message text");
    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgFullyEncrypted);

    // Now, test that the unencrypted message is generated correctly
    std::shared_ptr<KMime::Message> unencryptedMessage = nodeHelper.unencryptedMessage(originalMessage);
    QCOMPARE(unencryptedMessage->contentType()->mimeType().data(), "text/plain");
    QCOMPARE(unencryptedMessage->decodedBody().data(), "encrypted message text");
    QCOMPARE(unencryptedMessage->contents().size(), 0);
}

void ObjectTreeParserTest::testOpenPGPEncryptedNotDecrypted()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"openpgp-encrypted.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgFullyEncrypted);
    QCOMPARE(otp.plainTextContent().toLatin1().data(), "");

    std::shared_ptr<KMime::Message> unencryptedMessage = nodeHelper.unencryptedMessage(originalMessage);
    QVERIFY(!unencryptedMessage);
}

void ObjectTreeParserTest::testOpenPGPEncryptedOverrideEncoding()
{
    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(u"openpgp-encrypted-attachment-iso-8859-15-encoded.mbox"_s);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    testSource.setDecryptMessage(true);
    const QString data = QStringLiteral(
        "german umlauts ISO-8859-15:\n\u00c3\u20ac - a with two dots - utf-8: U+00C3 U20AC\n\u00c3\u009f - eszett or \"sharp s\" - utf-8: U+00C3 U+009F\n");
    ObjectTreeParser otp(&testSource, &nodeHelper);
    otp.parseObjectTree(originalMessage.get());

    QCOMPARE(nodeHelper.overallEncryptionState(originalMessage.get()), KMMsgFullyEncrypted);

    QVERIFY(otp.plainTextContent() != data); // it is not utf-8

    testSource.setOverrideCodecName("iso-8859-15");
    otp.parseObjectTree(originalMessage.get());
    QCOMPARE(otp.plainTextContent(), data);
}

void ObjectTreeParserTest::testAsync_data()
{
    QTest::addColumn<QString>("mailFileName");
    QTest::addColumn<QString>("output");

    QTest::newRow("openpgp-encrypt") << u"openpgp-encrypted.mbox"_s << u"encrypted message text"_s;
    QTest::newRow("smime-opaque-sign") << u"smime-opaque-sign.mbox"_s << u"A simple signed only test."_s;
    QTest::newRow("smime-encrypt") << u"smime-encrypted.mbox"_s << u"The quick brown fox jumped over the lazy dog."_s;
    QTest::newRow("openpgp-inline-encrypt") << u"openpgp-inline-charset-encrypted.mbox"_s << u"asdasd asd asd asdf sadf sdaf sadf \u00F6\u00E4\u00FC"_s;
}

void ObjectTreeParserTest::testAsync()
{
    QFETCH(QString, mailFileName);
    QFETCH(QString, output);

    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(mailFileName);
    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    testSource.setDecryptMessage(true);
    {
        QEventLoop loop;
        ObjectTreeParser otp(&testSource, &nodeHelper);

        connect(&nodeHelper, &NodeHelper::update, &loop, &QEventLoop::quit);
        otp.setAllowAsync(true);
        otp.parseObjectTree(originalMessage.get());
        loop.exec();
    }
    // Job ended
    {
        ObjectTreeParser otp(&testSource, &nodeHelper);
        otp.setAllowAsync(true);
        otp.parseObjectTree(originalMessage.get());
        QCOMPARE(otp.plainTextContent(), output);
    }
}

void ObjectTreeParserTest::testHtmlContent_data()
{
    QTest::addColumn<QString>("mailFileName");
    QTest::addColumn<QString>("output");

    QTest::newRow("html-attachments1") << u"html-attachment1.mbox"_s
                                       << u"<html><head></head><body><p><span style=\"font-family:Arial;\">A Body Text</span></p></body></html>"_s;
    QTest::newRow("html-attachments2") << u"html-attachment2.mbox"_s << u"<html><head></head><body>HTML Text</body></html>"_s;
}

void ObjectTreeParserTest::testHtmlContent()
{
    QFETCH(QString, mailFileName);
    QFETCH(QString, output);

    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(mailFileName);
    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    testSource.setDecryptMessage(true);
    otp.parseObjectTree(originalMessage.get());

    QVERIFY(otp.plainTextContent().isEmpty());
    QCOMPARE(otp.htmlContent(), output);
}

void ObjectTreeParserTest::testRenderedTree_data()
{
    QTest::addColumn<QString>("mailFileName");

    QDir dir(QStringLiteral(MAIL_DATA_DIR));
    const auto l = dir.entryList(QStringList(u"*.mbox"_s), QDir::Files | QDir::Readable | QDir::NoSymLinks);
    for (const QString &file : l) {
        if (!QFile::exists(dir.path() + u'/' + file + u".tree"_s)) {
            continue;
        }
        QTest::newRow(file.toLatin1().constData()) << file;
    }
}

void ObjectTreeParserTest::testRenderedTree()
{
    QFETCH(QString, mailFileName);

    std::shared_ptr<KMime::Message> originalMessage = readAndParseMail(mailFileName);
    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    testSource.setDecryptMessage(true);
    otp.parseObjectTree(originalMessage.get());

    testMessagePartTree(otp.parsedPart(), mailFileName);
}

void ObjectTreeParserTest::testParsePlainMessage()
{
    std::shared_ptr<KMime::Message> msg(new KMime::Message());
    QByteArray content(
        "From: Thomas McGuire <dontspamme@gmx.net>\n"
        "Subject: Plain Message Test\n"
        "Date: Wed, 5 Aug 2009 10:58:27 +0200\n"
        "MIME-Version: 1.0\n"
        "Content-Type: text/plain;\n"
        "  charset=\"iso-8859-15\"\n"
        "\n"
        "This is the message text.\n");
    msg->setContent(content);
    msg->parse();

    QCOMPARE(msg->subject()->as7BitString(), "Plain Message Test");
    QCOMPARE(msg->contents().size(), 0);

    // Parse the message
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource);
    otp.parseObjectTree(msg.get());

    // Check that the textual content and the charset have the expected values
    QCOMPARE(otp.plainTextContent(), u"This is the message text.\n"_s);
    QVERIFY(otp.htmlContent().isEmpty());

    // Check that the message was not modified in any way
    QCOMPARE(msg->encodedContent().constData(), content.constData());

    // Test that the charset of messages without an explicit charset declaration
    // is correct
    content =
        "From: Thomas McGuire <dontspamme@gmx.net>\n"
        "Subject: Plain Message Test\n"
        "Date: Wed, 5 Aug 2009 10:58:27 +0200\n"
        "MIME-Version: 1.0\n"
        "Content-Type: text/plain;\n"
        "\n"
        "This is the message text.\n";
    msg->setContent(content);
    msg->parse();
    ObjectTreeParser otp2(&testSource);
    otp2.parseObjectTree(msg.get());
}

void ObjectTreeParserTest::testParseEncapsulatedMessage()
{
    std::shared_ptr<KMime::Message> msg = readAndParseMail(u"encapsulated-with-attachment.mbox"_s);
    QCOMPARE(msg->subject()->as7BitString(), "Fwd: Test with attachment");
    QCOMPARE(msg->contents().size(), 2);

    // Parse the message
    SimpleObjectTreeSource testSource;
    NodeHelper nodeHelper;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    otp.parseObjectTree(msg.get());

    // Check that the OTP didn't modify the message in weird ways
    QCOMPARE(msg->contents().size(), 2);
    QCOMPARE(msg->contents().at(0)->contents().size(), 0);
    QCOMPARE(msg->contents().at(1)->contents().size(), 1);
    QCOMPARE(msg->contents().at(1)->contents().first()->contents().size(), 2);
    QCOMPARE(msg->contents().at(1)->contents().first()->contents().at(0)->contents().size(), 0);
    QCOMPARE(msg->contents().at(1)->contents().first()->contents().at(1)->contents().size(), 0);

    // Check that the textual content and the charset have the expected values
    QCOMPARE(otp.plainTextContent(), u"This is the first encapsulating message.\n"_s);
    QVERIFY(otp.htmlContent().isEmpty());

    // Check that the objecttreeparser did process the encapsulated message
    std::shared_ptr<KMime::Message> encapsulated = msg->contents().at(1)->bodyAsMessage();
    QVERIFY(encapsulated.get());
    QVERIFY(nodeHelper.nodeProcessed(encapsulated.get()));
    QVERIFY(nodeHelper.nodeProcessed(encapsulated->contents().at(0)));
    QVERIFY(nodeHelper.nodeProcessed(encapsulated->contents().at(1)));
    QVERIFY(nodeHelper.partMetaData(msg->contents().at(1)).isEncapsulatedRfc822Message);
}

void ObjectTreeParserTest::testMissingContentTypeHeader()
{
    std::shared_ptr<KMime::Message> msg = readAndParseMail(u"no-content-type.mbox"_s);
    QCOMPARE(msg->subject()->as7BitString(), "Simple Mail Without Content-Type Header");
    QCOMPARE(msg->contents().size(), 0);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);
    otp.parseObjectTree(msg.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "asdfasdf");
    QVERIFY(otp.htmlContent().isEmpty());
}

void ObjectTreeParserTest::testInlinePGPDecryption()
{
    std::shared_ptr<KMime::Message> msg = readAndParseMail(u"inlinepgpencrypted.mbox"_s);

    QCOMPARE(msg->subject()->as7BitString(), "inlinepgpencrypted");
    QCOMPARE(msg->contents().size(), 0);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);

    testSource.setDecryptMessage(true);
    otp.parseObjectTree(msg.get());

    QCOMPARE(otp.plainTextContent().toLatin1().data(), "some random text");

    // This test is only a workaround, till we can set the memento to the proper node of the mail.
    auto content = new KMime::Content;
    QVERIFY(nodeHelper.bodyPartMemento(content, "decryptverify-attachment:?place=0"));

    QVERIFY(otp.htmlContent().isEmpty());
}

void ObjectTreeParserTest::testInlinePGPSigned()
{
    std::shared_ptr<KMime::Message> msg = readAndParseMail(u"openpgp-inline-signed.mbox"_s);

    QCOMPARE(msg->subject()->as7BitString(), "test");
    QCOMPARE(msg->contents().size(), 0);

    NodeHelper nodeHelper;
    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource, &nodeHelper);

    testSource.setDecryptMessage(true);
    otp.parseObjectTree(msg.get());

    // This test is only a workaround, till we can set the memento to the proper node of the mail.
    QVERIFY(nodeHelper.bodyPartMemento(nullptr, "verification-attachment:?place=0"));
}

void ObjectTreeParserTest::testHTML()
{
    std::shared_ptr<KMime::Message> msg = readAndParseMail(u"html.mbox"_s);

    QCOMPARE(msg->subject()->as7BitString(), "HTML test");
    QCOMPARE(msg->contents().size(), 2);

    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource);

    otp.parseObjectTree(msg.get());

    QCOMPARE(otp.plainTextContent().toLatin1(), "Some HTML text");
    QVERIFY(otp.htmlContent().contains(QLatin1StringView("Some <span style=\" font-weight:600;\">HTML</span> text")));
}

void ObjectTreeParserTest::testHTMLasText()
{
    std::shared_ptr<KMime::Message> msg = readAndParseMail(u"html.mbox"_s);

    QCOMPARE(msg->subject()->as7BitString(), "HTML test");
    QCOMPARE(msg->contents().size(), 2);

    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource);

    testSource.setPreferredMode(MimeTreeParser::Util::MultipartPlain);
    otp.parseObjectTree(msg.get());

    QCOMPARE(otp.htmlContent().toLatin1(), "");
    QCOMPARE(otp.plainTextContent().toLatin1(), "Some HTML text");
}

void ObjectTreeParserTest::testHTMLOnly()
{
    std::shared_ptr<KMime::Message> msg = readAndParseMail(u"htmlonly.mbox"_s);

    QCOMPARE(msg->subject()->as7BitString(), "HTML test");
    QCOMPARE(msg->contents().size(), 0);

    SimpleObjectTreeSource testSource;
    ObjectTreeParser otp(&testSource);

    otp.parseObjectTree(msg.get());

    QVERIFY(otp.plainTextContent().isEmpty());
    QVERIFY(otp.htmlContent().contains(QLatin1StringView("<b>SOME</b> HTML text.")));
}

#include "moc_basicobjecttreeparsertest.cpp"
