fix(util,registry): tolerate broken Info.plist (#1951)

Fixes #1949.
This commit is contained in:
Oleg Shparber
2026-08-13 22:24:25 +03:00
committed by GitHub
parent 25ad56c62e
commit ae4086f7a0
4 changed files with 124 additions and 2 deletions
-1
View File
@@ -104,7 +104,6 @@ Docset::Docset(QString path)
if (plist.hasError()) { if (plist.hasError()) {
qCWarning(log, "Failed to parse 'Info.plist' for docset at '%s'.", qPrintable(m_path)); qCWarning(log, "Failed to parse 'Info.plist' for docset at '%s'.", qPrintable(m_path));
return;
} }
if (m_name.isEmpty()) { if (m_name.isEmpty()) {
+2 -1
View File
@@ -22,7 +22,8 @@ bool Plist::read(const QString &fileName)
return false; return false;
} }
QXmlStreamReader xml(&file); // Some docsets ship an Info.plist with whitespace before the XML declaration.
QXmlStreamReader xml(file.readAll().trimmed());
while (!xml.atEnd()) { while (!xml.atEnd()) {
const QXmlStreamReader::TokenType token = xml.readNext(); const QXmlStreamReader::TokenType token = xml.readNext();
+6
View File
@@ -6,6 +6,12 @@ target_link_libraries(fuzzy_test PRIVATE Util Qt6::Test)
zeal_add_test(fuzzy_test) zeal_add_test(fuzzy_test)
# Property list tests
add_executable(plist_test plist_test.cpp)
target_link_libraries(plist_test PRIVATE Util Qt6::Test)
zeal_add_test(plist_test)
# SQLite Statement tests # SQLite Statement tests
add_executable(statement_test statement_test.cpp) add_executable(statement_test statement_test.cpp)
target_link_libraries(statement_test PRIVATE Util Qt6::Test) target_link_libraries(statement_test PRIVATE Util Qt6::Test)
+116
View File
@@ -0,0 +1,116 @@
// Copyright (C) Oleg Shparber, et al. <https://zealdocs.org>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "../plist.h"
#include <QtTest>
using namespace Zeal::Util;
class PlistTest : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void testReadsKeys();
void testLeadingNewline();
void testLeadingIndentation();
void testByteOrderMark();
void testMalformedContent();
void testMissingFile();
private:
QString writePlist(const QByteArray &data);
QTemporaryDir m_dir;
int m_counter = 0;
};
namespace {
const QByteArray Body = QByteArrayLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\""
" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
"<plist version=\"1.0\">\n"
" <dict>\n"
" <key>CFBundleName</key>\n"
" <string>Android KTX</string>\n"
" <key>isDashDocset</key>\n"
" <true/>\n"
" <key>isJavaScriptEnabled</key>\n"
" <false/>\n"
" </dict>\n"
"</plist>\n");
} // namespace
QString PlistTest::writePlist(const QByteArray &data)
{
const QString path = m_dir.filePath(QStringLiteral("Info%1.plist").arg(++m_counter));
QFile file(path);
if (!file.open(QIODevice::WriteOnly) || file.write(data) != data.size()) {
return {};
}
return path;
}
void PlistTest::initTestCase()
{
QVERIFY(m_dir.isValid());
}
void PlistTest::testReadsKeys()
{
Plist plist;
QVERIFY(plist.read(writePlist(Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist.value(QStringLiteral("CFBundleName")), QVariant(QStringLiteral("Android KTX")));
QCOMPARE(plist.value(QStringLiteral("isDashDocset")), QVariant(true));
QCOMPARE(plist.value(QStringLiteral("isJavaScriptEnabled")), QVariant(false));
}
// See: https://github.com/zealdocs/zeal/issues/1949
void PlistTest::testLeadingNewline()
{
Plist plist;
QVERIFY(plist.read(writePlist(QByteArrayLiteral("\n") + Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist[QStringLiteral("CFBundleName")].toString(), QStringLiteral("Android KTX"));
}
void PlistTest::testLeadingIndentation()
{
Plist plist;
QVERIFY(plist.read(writePlist(QByteArrayLiteral("\n ") + Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist[QStringLiteral("CFBundleName")].toString(), QStringLiteral("Android KTX"));
}
void PlistTest::testByteOrderMark()
{
Plist plist;
QVERIFY(plist.read(writePlist(QByteArrayLiteral("\xEF\xBB\xBF") + Body)));
QVERIFY(!plist.hasError());
QCOMPARE(plist[QStringLiteral("CFBundleName")].toString(), QStringLiteral("Android KTX"));
}
void PlistTest::testMalformedContent()
{
const QString path = writePlist(QByteArrayLiteral("<plist><dict><key>CFBundleName</key>"));
QVERIFY(!path.isEmpty());
Plist plist;
QVERIFY(!plist.read(path));
QVERIFY(plist.hasError());
}
void PlistTest::testMissingFile()
{
Plist plist;
QVERIFY(!plist.read(m_dir.filePath(QStringLiteral("Missing.plist"))));
QVERIFY(plist.hasError());
}
QTEST_GUILESS_MAIN(PlistTest)
#include "plist_test.moc"