File indexing completed on 2024-05-19 05:05:53
0001 /*************************************************************************** 0002 * SPDX-License-Identifier: GPL-2.0-or-later 0003 * * 0004 * SPDX-FileCopyrightText: 2004-2023 Thomas Fischer <fischer@unix-ag.uni-kl.de> 0005 * * 0006 * This program is free software; you can redistribute it and/or modify * 0007 * it under the terms of the GNU General Public License as published by * 0008 * the Free Software Foundation; either version 2 of the License, or * 0009 * (at your option) any later version. * 0010 * * 0011 * This program is distributed in the hope that it will be useful, * 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0014 * GNU General Public License for more details. * 0015 * * 0016 * You should have received a copy of the GNU General Public License * 0017 * along with this program; if not, see <https://www.gnu.org/licenses/>. * 0018 ***************************************************************************/ 0019 0020 #include <QtTest> 0021 0022 #include <QStandardPaths> 0023 0024 #include <KBibTeX> 0025 #include <Preferences> 0026 #include <Value> 0027 #include <Entry> 0028 #include <Comment> 0029 #include <File> 0030 #include <FileInfo> 0031 #include <EncoderXML> 0032 #include <EncoderLaTeX> 0033 #include <BibUtils> 0034 #include <FileImporter> 0035 #include <FileImporterBibTeX> 0036 #include <FileImporterRIS> 0037 #include <FileImporterBibUtils> 0038 #include <FileExporterBibTeX> 0039 #include <FileExporterRIS> 0040 #include <FileExporterBibUtils> 0041 #include <FileExporterXML> 0042 0043 #include "logging_test.h" 0044 // Provides definition of KDESRCDIR 0045 #include "test-config.h" 0046 0047 typedef QHash<FileExporterXML::OutputStyle, QString> MapFileExporterXMLOutputStyleToQString; 0048 0049 Q_DECLARE_METATYPE(MapFileExporterXMLOutputStyleToQString) 0050 Q_DECLARE_METATYPE(QMimeType) 0051 Q_DECLARE_METATYPE(QSharedPointer<Element>) 0052 0053 class KBibTeXIOTest : public QObject 0054 { 0055 Q_OBJECT 0056 0057 private: 0058 File *mobyDickBibliography(); 0059 File *latinUmlautBibliography(); 0060 File *onlyComments(int numPercentSigns, int numLines); 0061 File *koreanBibliography(); 0062 File *russianBibliography(); 0063 QVector<QPair<const char *, File *> > fileImporterExporterTestCases(); 0064 0065 private Q_SLOTS: 0066 void initTestCase(); 0067 void encoderConvertToPlainAscii_data(); 0068 void encoderConvertToPlainAscii(); 0069 void encoderXMLdecode_data(); 0070 void encoderXMLdecode(); 0071 void encoderXMLencode_data(); 0072 void encoderXMLencode(); 0073 void encoderLaTeXdecode_data(); 0074 void encoderLaTeXdecode(); 0075 void encoderLaTeXencode_data(); 0076 void encoderLaTeXencode(); 0077 void encoderLaTeXencodeHash(); 0078 void fileImporterSplitName_data(); 0079 void fileImporterSplitName(); 0080 void fileInfoMimeTypeForUrl_data(); 0081 void fileInfoMimeTypeForUrl(); 0082 void fileInfoUrlsInText_data(); 0083 void fileInfoUrlsInText(); 0084 void fileExporterXMLsave_data(); 0085 void fileExporterXMLsave(); 0086 void fileExporterRISsave_data(); 0087 void fileExporterRISsave(); 0088 void fileExporterBibTeXsave_data(); 0089 void fileExporterBibTeXsave(); 0090 void fileImporterRISload_data(); 0091 void fileImporterRISload(); 0092 void fileImporterBibTeXload_data(); 0093 void fileImporterBibTeXload(); 0094 void fileExporterBibTeXEncoding_data(); 0095 void fileExporterBibTeXEncoding(); 0096 void fileImportExportBibTeXroundtrip_data(); 0097 void fileImportExportBibTeXroundtrip(); 0098 void protectiveCasingEntryGeneratedOnTheFly(); 0099 void protectiveCasingEntryFromData(); 0100 void partialBibTeXInput_data(); 0101 void partialBibTeXInput(); 0102 void partialRISInput_data(); 0103 void partialRISInput(); 0104 void jabRefFieldFile_data(); 0105 void jabRefFieldFile(); 0106 0107 private: 0108 }; 0109 0110 void KBibTeXIOTest::encoderConvertToPlainAscii_data() 0111 { 0112 QTest::addColumn<QString>("unicodestring"); 0113 /// Depending on the chosen implementation for Encoder::instance().convertToPlainAscii(), 0114 /// the ASCII variant may slightly differ (both alternatives are considered valid). 0115 /// If both implementations produce the same ASCII output, 'asciialternative2' is 0116 /// to be set to be empty. 0117 QTest::addColumn<QString>("asciialternative1"); 0118 QTest::addColumn<QString>("asciialternative2"); 0119 0120 QTest::newRow("Just 'A'") << QString(QChar(0x00c0)) + QChar(0x00c2) + QChar(0x00c5) << QStringLiteral("AAA") << QString(); 0121 QTest::newRow("Just ASCII letters and numbers") << QStringLiteral("qwertyuiopASDFGHJKLzxcvbnm1234567890") << QStringLiteral("qwertyuiopASDFGHJKLzxcvbnm1234567890") << QString(); 0122 QTest::newRow("Latin text") << QStringLiteral("Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur.") << QStringLiteral("Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur.") << QString(); 0123 QTest::newRow("ASCII low and high bytes") << QStringLiteral("\x00\x01\x09\x0a\x10\x11\x19\x1a\x1f\x20\x7e\x7f") << QStringLiteral(" ~") << QString(); 0124 QTest::newRow("European Scripts/Latin-1 Supplement") << QString::fromUtf8("\xc3\x80\xc3\x82\xc3\x84\xc3\x92\xc3\x94\xc3\x96\xc3\xac\xc3\xad\xc3\xae\xc3\xaf") << QStringLiteral("AAAOOOiiii") << QStringLiteral("AAAEOOOEiiii"); 0125 QTest::newRow("European Scripts/Latin Extended-A") << QString::fromUtf8("\xc4\x8a\xc4\x8b\xc4\xae\xc4\xaf\xc5\x9c\xc5\x9d\xc5\xbb\xc5\xbc") << QStringLiteral("CcIiSsZz") << QString(); 0126 QTest::newRow("European Scripts/Latin Extended-B") << QString::fromUtf8("\xc7\x8a\xc7\x8b\xc7\x8c") << QStringLiteral("NJNjnj") << QString(); 0127 QTest::newRow("European Scripts/Latin Extended Additional") << QString::fromUtf8("\xe1\xb8\xbe\xe1\xb8\xbf\xe1\xb9\xa4\xe1\xb9\xa5\xe1\xbb\xae\xe1\xbb\xaf") << QStringLiteral("MmSsUu") << QString(); 0128 QTest::newRow("European Scripts/Cyrillic") << QString::fromUtf8("\xd0\x90\xd0\x9e\xd0\x9f") << QStringLiteral("AOP") << QString(); 0129 QTest::newRow("European Scripts/Greek and Coptic") << QString::fromUtf8("\xce\xba\xce\xb1\xce\xa4\xcf\xba\xce\x9d") << QStringLiteral("kaTSN") << QStringLiteral("kappaalphaTauSanNu"); 0130 QTest::newRow("East Asian Scripts/Katakana") << QString::fromUtf8("\xe3\x82\xb7\xe3\x83\x84") << QStringLiteral("shitsu") << QStringLiteral("situ"); 0131 QTest::newRow("East Asian Scripts/Hangul Syllables") << QString::fromUtf8("\xea\xb9\x80\xec\xa0\x95\xec\x9d\x80") << QStringLiteral("gimjeongeun") << QStringLiteral("gimjeong-eun"); 0132 QTest::newRow("Non-BMP characters (stay unchanged)") << QString::fromUtf8(/* U+10437 */ "\xf0\x90\x90\xb7" /* U+10E6D */ "\xf0\x90\xb9\xad" /* U+1D11E */ "\xf0\x9d\x84\x9e" /* U+10FFFF */ "") << QString::fromUtf8("\xf0\x90\x90\xb7\xf0\x90\xb9\xad\xf0\x9d\x84\x9e") << QString(); 0133 QTest::newRow("Base symbols followed by combining symbols") << QString::fromUtf8("123" /* COMBINING GRAVE ACCENT */ "A\xcc\x80" /* COMBINING DIAERESIS */ "A\xcc\x88" /* COMBINING LOW LINE */ "A\xcc\xb2" "123") << QStringLiteral("123AAA123") << QString(); 0134 } 0135 0136 void KBibTeXIOTest::encoderConvertToPlainAscii() 0137 { 0138 QFETCH(QString, unicodestring); 0139 QFETCH(QString, asciialternative1); 0140 QFETCH(QString, asciialternative2); 0141 0142 const QString converted = Encoder::instance().convertToPlainAscii(unicodestring); 0143 /// Depending on the chosen implementation for Encoder::instance().convertToPlainAscii(), 0144 /// the ASCII variant may slightly differ (both alternatives are considered valid). 0145 if (converted != asciialternative1 && converted != asciialternative2) 0146 qCWarning(LOG_KBIBTEX_TEST) << "converted=" << converted << " asciialternative1=" << asciialternative1 << " asciialternative2=" << asciialternative2; 0147 QVERIFY(converted == asciialternative1 || converted == asciialternative2); 0148 } 0149 0150 void KBibTeXIOTest::encoderXMLdecode_data() 0151 { 0152 QTest::addColumn<QString>("xml"); 0153 QTest::addColumn<QString>("unicode"); 0154 0155 QTest::newRow("Just ASCII") << QStringLiteral("Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur.") << QStringLiteral("Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur."); 0156 QTest::newRow("Quotation marks") << QStringLiteral("Caesar said: "Veni, vidi, vici"") << QStringLiteral("Caesar said: \"Veni, vidi, vici\""); 0157 QTest::newRow("Characters from EncoderXMLCharMapping") << QStringLiteral(""&<>") << QStringLiteral("\"\\&<>"); 0158 QTest::newRow("Characters from backslashSymbols") << QStringLiteral("&%_") << QStringLiteral("\\&\\%\\_"); 0159 0160 for (int start = 0; start < 16; ++start) { 0161 QString xmlString, unicodeString; 0162 for (int offset = 1561; offset < 6791; offset += 621) { 0163 const ushort unicode = static_cast<ushort>((start * 3671 + offset) & 0x7fff); 0164 xmlString += QStringLiteral("&#") + QString::number(unicode) + QStringLiteral(";"); 0165 unicodeString += QChar(unicode); 0166 } 0167 QTest::newRow(QString(QStringLiteral("Some arbitrary Unicode characters (%1): %2")).arg(start).arg(xmlString).toLatin1().constData()) << xmlString << unicodeString; 0168 } 0169 } 0170 0171 void KBibTeXIOTest::encoderXMLdecode() 0172 { 0173 QFETCH(QString, xml); 0174 QFETCH(QString, unicode); 0175 0176 QCOMPARE(EncoderXML::instance().decode(xml), unicode); 0177 } 0178 0179 void KBibTeXIOTest::encoderXMLencode_data() 0180 { 0181 encoderXMLdecode_data(); 0182 } 0183 0184 void KBibTeXIOTest::encoderXMLencode() 0185 { 0186 QFETCH(QString, xml); 0187 QFETCH(QString, unicode); 0188 0189 QCOMPARE(EncoderXML::instance().encode(unicode, Encoder::TargetEncoding::ASCII), xml); 0190 } 0191 0192 void KBibTeXIOTest::encoderLaTeXdecode_data() 0193 { 0194 QTest::addColumn<QString>("latex"); 0195 QTest::addColumn<QString>("unicode"); 0196 QTest::addColumn<QString>("alternativelatex"); 0197 0198 QTest::newRow("Just ASCII") << QStringLiteral("Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur.") << QStringLiteral("Gallia est omnis divisa in partes tres, quarum unam incolunt Belgae, aliam Aquitani, tertiam qui ipsorum lingua Celtae, nostra Galli appellantur.") << QString(); 0199 QTest::newRow("Dotless i and j characters") << QStringLiteral("{\\`\\i}{\\`{\\i}}{\\'\\i}{\\^\\i}{\\\"\\i}{\\~\\i}{\\=\\i}{\\u\\i}{\\k\\i}{\\^\\j}{\\m\\i}{\\v\\i}{\\v\\j}\\m\\i") << QString(QChar(0x00EC)) + QChar(0x00EC) + QChar(0x00ED) + QChar(0x00EE) + QChar(0x00EF) + QChar(0x0129) + QChar(0x012B) + QChar(0x012D) + QChar(0x012F) + QChar(0x0135) + QStringLiteral("{\\m\\i}") + QChar(0x01D0) + QChar(0x01F0) + QStringLiteral("\\m\\i") << QStringLiteral("{\\`\\i}{\\`\\i}{\\'\\i}{\\^\\i}{\\\"\\i}{\\~\\i}{\\=\\i}{\\u\\i}{\\k\\i}{\\^\\j}{\\m\\i}{\\v\\i}{\\v\\j}\\m\\i"); 0200 QTest::newRow("\\l and \\ldots") << QStringLiteral("\\l\\ldots\\l\\ldots") << QString(QChar(0x0142)) + QChar(0x2026) + QChar(0x0142) + QChar(0x2026) << QStringLiteral("{\\l}{\\ldots}{\\l}{\\ldots}"); 0201 QTest::newRow("Various two-letter commands (1)") << QStringLiteral("\\AA\\textmu") << QString(QChar(0x00c5)) + QChar(0x03bc) << QStringLiteral("{\\AA}{\\textmu}"); 0202 QTest::newRow("Various two-letter commands (2)") << QStringLiteral("{\\AA}{\\textmu}") << QString(QChar(0x00c5)) + QChar(0x03bc) << QStringLiteral("{\\AA}{\\textmu}"); 0203 QTest::newRow("Various two-letter commands (3)") << QStringLiteral("\\AA \\textmu") << QString(QChar(0x00c5)) + QChar(0x03bc) << QStringLiteral("{\\AA}{\\textmu}"); 0204 QTest::newRow("Inside curly brackets: modifier plus letter") << QStringLiteral("aa{\\\"A}bb{\\\"T}") << QStringLiteral("aa") + QChar(0x00c4) + QStringLiteral("bb{\\\"T}") << QString(); 0205 QTest::newRow("Inside curly brackets: modifier plus, inside curly brackets, letter") << QStringLiteral("aa{\\\"{A}}bb{\\\"{T}}") << QStringLiteral("aa") + QChar(0x00c4) + QStringLiteral("bb{\\\"{T}}") << QStringLiteral("aa{\\\"A}bb{\\\"{T}}"); 0206 QTest::newRow("Modifier plus letter") << QStringLiteral("\\\"A aa\\\"Abb\\\"T") << QChar(0x00c4) + QStringLiteral(" aa") + QChar(0x00c4) + QStringLiteral("bb\\\"T") << QStringLiteral("{\\\"A} aa{\\\"A}bb\\\"T"); 0207 QTest::newRow("Modifier plus, inside curly brackets, letter") << QStringLiteral("\\\"{A} aa\\\"{A}bb\\\"{T}") << QChar(0x00c4) + QStringLiteral(" aa") + QChar(0x00c4) + QStringLiteral("bb\\\"{T}") << QStringLiteral("{\\\"A} aa{\\\"A}bb\\\"{T}"); 0208 QTest::newRow("Single-letter commands") << QStringLiteral("\\,\\&\\_\\#\\%") << QChar(0x2009) + QStringLiteral("&_#%") << QString(); 0209 QTest::newRow("\\noopsort{\\noopsort}") << QStringLiteral("\\noopsort{\\noopsort}") << QStringLiteral("\\noopsort{\\noopsort}") << QString(); 0210 QTest::newRow("\\ensuremath") << QStringLiteral("\\ensuremath{${\\alpha}$}${\\ensuremath{\\delta}}$-spot \\ensuremath{26^{\\mathrm{th}}} {\\ensuremath{-}}") << QStringLiteral("\\ensuremath{$") + QChar(0x03b1) + QStringLiteral("$}${\\ensuremath{") + QChar(0x03b4) + QStringLiteral("}}$-spot \\ensuremath{26^{\\mathrm{th}}} {\\ensuremath{-}}") << QStringLiteral("\\ensuremath{$\\alpha$}${\\ensuremath{\\delta}}$-spot \\ensuremath{26^{\\mathrm{th}}} {\\ensuremath{-}}"); 0211 QTest::newRow("Greek mu with 'Dollar' math") << QString(QStringLiteral("%1\\mu\\textmu$%1\\mu$")).arg(QChar(0x03bc)) << QString(QStringLiteral("%1%1%1$%1%1$")).arg(QChar(0x03bc)) << QStringLiteral("{\\textmu}{\\textmu}{\\textmu}$\\mu{}\\mu$"); 0212 QTest::newRow("Greek mu with '\\ensuremath'") << QString(QStringLiteral("%1\\mu\\textmu\\ensuremath{%1\\mu}")).arg(QChar(0x03bc)) << QString(QStringLiteral("%1%1%1\\ensuremath{%1%1}")).arg(QChar(0x03bc)) << QString(QStringLiteral("{\\textmu}{\\textmu}{\\textmu}\\ensuremath{\\mu{}\\mu}")); 0213 QTest::newRow("Micro sign with 'Dollar' math") << QString(QStringLiteral("%1$%1$")).arg(QChar(0x00b5)) << QString(QStringLiteral("%1$%1$")).arg(QChar(0x00b5)) << QStringLiteral("{\\textmu}$\\mu$"); 0214 QTest::newRow("Micro sign with '\\ensuremath'") << QString(QStringLiteral("%1\\ensuremath{%1}")).arg(QChar(0x00b5)) << QString(QStringLiteral("%1\\ensuremath{%1}")).arg(QChar(0x00b5)) << QString(QStringLiteral("{\\textmu}\\ensuremath{\\mu}")); 0215 QTest::newRow("\\uu") << QStringLiteral("u\\uu\\u uu\\u u u u{\\uu}{\\u u}u{\\u u} u") << QString(QStringLiteral("u\\uu%1u%1 u u{\\uu}%1u%1 u")).arg(QChar(0x016d)) << QStringLiteral("u\\uu{\\u u}u{\\u u} u u{\\uu}{\\u u}u{\\u u} u"); 0216 QTest::newRow("mhchem") << QStringLiteral("\\ce{H2O}") << QStringLiteral("\\ce{H2O}") << QString(); 0217 QTest::newRow("Latin small letter e with ogonek versus mhchem") << QStringLiteral("{\\c e} vs \\ce{H2O}") << QString(QStringLiteral("%1 vs \\ce{H2O}")).arg(QChar(0x0229)) << QStringLiteral("\\c{e} vs \\ce{H2O}"); 0218 } 0219 0220 void KBibTeXIOTest::encoderLaTeXdecode() 0221 { 0222 QFETCH(QString, latex); 0223 QFETCH(QString, unicode); 0224 0225 QCOMPARE(EncoderLaTeX::instance().decode(latex), unicode); 0226 } 0227 0228 void KBibTeXIOTest::encoderLaTeXencode_data() 0229 { 0230 encoderLaTeXdecode_data(); 0231 } 0232 0233 void KBibTeXIOTest::encoderLaTeXencode() 0234 { 0235 QFETCH(QString, latex); 0236 QFETCH(QString, unicode); 0237 QFETCH(QString, alternativelatex); 0238 0239 const QString generatedLatex = EncoderLaTeX::instance().encode(unicode, Encoder::TargetEncoding::ASCII); 0240 if (generatedLatex != latex && !alternativelatex.isEmpty()) 0241 QCOMPARE(generatedLatex, alternativelatex); 0242 else 0243 QCOMPARE(generatedLatex, latex); 0244 } 0245 0246 void KBibTeXIOTest::encoderLaTeXencodeHash() 0247 { 0248 /// File with single entry, inspired by 'Moby Dick' 0249 QScopedPointer<File> file(new File()); 0250 QSharedPointer<Entry> entry(new Entry(Entry::etBook, QStringLiteral("latex-encoder-test"))); 0251 file->append(entry); 0252 static const QString titleText(QStringLiteral("{This is a Title with a \\#}")); 0253 entry->insert(Entry::ftTitle, Value() << QSharedPointer<PlainText>(new PlainText(titleText))); 0254 static const QString authorLastNameText(QStringLiteral("{Flash the \\#}")); 0255 entry->insert(Entry::ftAuthor, Value() << QSharedPointer<Person>(new Person(QString(), authorLastNameText))); 0256 static const QString urlText(QStringLiteral("https://127.0.0.1/#hashtag")); 0257 entry->insert(Entry::ftUrl, Value() << QSharedPointer<VerbatimText>(new VerbatimText(urlText))); 0258 file->setProperty(File::ProtectCasing, Qt::Checked); 0259 file->setProperty(File::Encoding, QStringLiteral("latex")); 0260 0261 QBuffer fileBuffer; 0262 fileBuffer.open(QBuffer::WriteOnly); 0263 FileExporterBibTeX exporter(this); 0264 exporter.save(&fileBuffer, file.data()); 0265 fileBuffer.close(); 0266 0267 fileBuffer.open(QBuffer::ReadOnly); 0268 FileImporterBibTeX importer(this); 0269 QScopedPointer<File> importedFile(importer.load(&fileBuffer)); 0270 fileBuffer.close(); 0271 0272 QVERIFY(!importedFile.isNull()); 0273 QVERIFY(importedFile->count() == 1); 0274 QSharedPointer<Entry> importedEntry = importedFile->first().dynamicCast<Entry>(); 0275 QVERIFY(!importedEntry.isNull()); 0276 QVERIFY(importedEntry->count() == 3); 0277 QVERIFY(importedEntry->contains(Entry::ftTitle)); 0278 QVERIFY(importedEntry->value(Entry::ftTitle).count() == 1); 0279 const QSharedPointer<PlainText> titlePlainText = importedEntry->value(Entry::ftTitle).first().dynamicCast<PlainText>(); 0280 QVERIFY(!titlePlainText.isNull()); 0281 const QString importedTitleText = titlePlainText->text(); 0282 QVERIFY(!importedTitleText.isEmpty()); 0283 QVERIFY(importedEntry->contains(Entry::ftAuthor)); 0284 QVERIFY(importedEntry->value(Entry::ftAuthor).count() == 1); 0285 const QSharedPointer<Person> authorPerson = importedEntry->value(Entry::ftAuthor).first().dynamicCast<Person>(); 0286 QVERIFY(!authorPerson.isNull()); 0287 const QString importedAuthorLastNameText = authorPerson->lastName(); 0288 QVERIFY(!importedAuthorLastNameText.isEmpty()); 0289 QVERIFY(importedEntry->contains(Entry::ftUrl)); 0290 QVERIFY(importedEntry->value(Entry::ftUrl).count() == 1); 0291 const QSharedPointer<VerbatimText> urlVerbatimText = importedEntry->value(Entry::ftUrl).first().dynamicCast<VerbatimText>(); 0292 QVERIFY(!urlVerbatimText.isNull()); 0293 const QString importedUrlText = urlVerbatimText->text(); 0294 QVERIFY(!importedUrlText.isEmpty()); 0295 0296 QVERIFY(importedTitleText == titleText); 0297 QVERIFY(importedAuthorLastNameText == authorLastNameText); 0298 QVERIFY(importedUrlText == urlText); 0299 } 0300 0301 void KBibTeXIOTest::fileImporterSplitName_data() 0302 { 0303 QTest::addColumn<QString>("name"); 0304 QTest::addColumn<Person *>("person"); 0305 0306 QTest::newRow("Empty name") << QString() << new Person(QString(), QString(), QString()); 0307 QTest::newRow("PubMed style") << QStringLiteral("Jones A B C") << new Person(QStringLiteral("A B C"), QStringLiteral("Jones"), QString()); 0308 QTest::newRow("Just last name") << QStringLiteral("Dido") << new Person(QString(), QStringLiteral("Dido"), QString()); 0309 QTest::newRow("Name with 'von'") << QStringLiteral("Theodor von Sickel") << new Person(QStringLiteral("Theodor"), QStringLiteral("von Sickel"), QString()); 0310 QTest::newRow("Name with 'von', reversed") << QStringLiteral("von Sickel, Theodor") << new Person(QStringLiteral("Theodor"), QStringLiteral("von Sickel"), QString()); 0311 QTest::newRow("Name with 'van der'") << QStringLiteral("Adriaen van der Werff") << new Person(QStringLiteral("Adriaen"), QStringLiteral("van der Werff"), QString()); 0312 QTest::newRow("Name with 'van der', reversed") << QStringLiteral("van der Werff, Adriaen") << new Person(QStringLiteral("Adriaen"), QStringLiteral("van der Werff"), QString()); 0313 QTest::newRow("Name with suffix") << QStringLiteral("Anna Eleanor Roosevelt Jr.") << new Person(QStringLiteral("Anna Eleanor"), QStringLiteral("Roosevelt"), QStringLiteral("Jr.")); 0314 } 0315 0316 void KBibTeXIOTest::fileImporterSplitName() 0317 { 0318 QFETCH(QString, name); 0319 QFETCH(Person *, person); 0320 0321 Person *computedPerson = FileImporter::splitName(name); 0322 QCOMPARE(*computedPerson, *person); 0323 0324 delete person; 0325 delete computedPerson; 0326 } 0327 0328 void KBibTeXIOTest::fileInfoMimeTypeForUrl_data() 0329 { 0330 QTest::addColumn<QUrl>("url"); 0331 QTest::addColumn<QMimeType>("mimetype"); 0332 0333 static const QMimeDatabase db; 0334 QTest::newRow("Invalid URL") << QUrl() << QMimeType(); 0335 QTest::newRow("Generic URL") << QUrl(QStringLiteral("https://www.example.com")) << db.mimeTypeForName(QStringLiteral("text/html")); 0336 QTest::newRow("Generic local file") << QUrl(QStringLiteral("/usr/bin/who")) << db.mimeTypeForName(QStringLiteral("application/octet-stream")); 0337 QTest::newRow("Generic Samba URL") << QUrl(QStringLiteral("smb://fileserver.local/file")) << db.mimeTypeForName(QStringLiteral("application/octet-stream")); 0338 QTest::newRow("URL to .bib file") << QUrl(QStringLiteral("https://www.example.com/references.bib")) << db.mimeTypeForName(QStringLiteral("text/x-bibtex")); 0339 QTest::newRow("Local .bib file") << QUrl(QStringLiteral("/home/user/references.bib")) << db.mimeTypeForName(QStringLiteral("text/x-bibtex")); 0340 QTest::newRow("URL to .pdf file") << QUrl(QStringLiteral("https://www.example.com/references.pdf")) << db.mimeTypeForName(QStringLiteral("application/pdf")); 0341 QTest::newRow("Local .pdf file") << QUrl(QStringLiteral("/home/user/references.pdf")) << db.mimeTypeForName(QStringLiteral("application/pdf")); 0342 } 0343 0344 void KBibTeXIOTest::fileInfoMimeTypeForUrl() 0345 { 0346 QFETCH(QUrl, url); 0347 QFETCH(QMimeType, mimetype); 0348 0349 QCOMPARE(FileInfo::mimeTypeForUrl(url), mimetype); 0350 } 0351 0352 void KBibTeXIOTest::fileInfoUrlsInText_data() 0353 { 0354 QTest::addColumn<QString>("text"); 0355 QTest::addColumn<QSet<QUrl>>("expectedUrls"); 0356 0357 QTest::newRow("Empty text") << QString() << QSet<QUrl>(); 0358 QTest::newRow("Lore ipsum with DOI (without URL)") << QStringLiteral("Lore ipsum 10.1000/38-abc Lore ipsum") << QSet<QUrl> {QUrl(KBibTeX::doiUrlPrefix + QStringLiteral("10.1000/38-abc"))}; 0359 QTest::newRow("Lore ipsum with DOI (with HTTP URL)") << QStringLiteral("Lore ipsum http://doi.example.org/10.1000/38-abc Lore ipsum") << QSet<QUrl> {QUrl(KBibTeX::doiUrlPrefix + QStringLiteral("10.1000/38-abc"))}; 0360 QTest::newRow("Lore ipsum with DOI (with HTTPS URL)") << QStringLiteral("Lore ipsum https://doi.example.org/10.1000/42-XYZ Lore ipsum") << QSet<QUrl> {QUrl(KBibTeX::doiUrlPrefix + QStringLiteral("10.1000/42-XYZ"))}; 0361 QTest::newRow("URLs and DOI (without URL), all semicolon-separated") << QStringLiteral("http://www.example.com;10.1000/38-abc ;\nhttps://www.example.com") << QSet<QUrl> {QUrl(QStringLiteral("http://www.example.com")), QUrl(KBibTeX::doiUrlPrefix + QStringLiteral("10.1000/38-abc")), QUrl(QStringLiteral("https://www.example.com"))}; 0362 QTest::newRow("URLs and DOI (with URL), all semicolon-separated") << QStringLiteral("http://www.example.com\n; 10.1000/38-abc;https://www.example.com") << QSet<QUrl> {QUrl(QStringLiteral("http://www.example.com")), QUrl(KBibTeX::doiUrlPrefix + QStringLiteral("10.1000/38-abc")), QUrl(QStringLiteral("https://www.example.com"))}; 0363 QTest::newRow("URLs with various separators") << QStringLiteral("http://www.example.com/def.pdf https://www.example.com\nhttp://download.example.com/abc") << QSet<QUrl> {QUrl(QStringLiteral("http://www.example.com/def.pdf")), QUrl(QStringLiteral("https://www.example.com")), QUrl(QStringLiteral("http://download.example.com/abc"))}; 0364 QTest::newRow("URLs with query strings and anchors") << QStringLiteral("http://www.example.com/def.pdf?a=3&b=1 https://www.example.com#1581584\nhttp://download.example.com/abc,7352,A#abc?gh=352&ghi=1254") << QSet<QUrl> {QUrl(QStringLiteral("http://www.example.com/def.pdf?a=3&b=1")), QUrl(QStringLiteral("https://www.example.com#1581584")), QUrl(QStringLiteral("http://download.example.com/abc,7352,A#abc?gh=352&ghi=1254"))}; 0365 } 0366 0367 void KBibTeXIOTest::fileInfoUrlsInText() 0368 { 0369 QFETCH(QString, text); 0370 QFETCH(QSet<QUrl>, expectedUrls); 0371 0372 QSet<QUrl> extractedUrls; 0373 FileInfo::urlsInText(text, FileInfo::TestExistence::No, QString(), extractedUrls); 0374 0375 QCOMPARE(extractedUrls.count(), expectedUrls.count()); 0376 for (const QUrl &expectedUrl : const_cast<const QSet<QUrl> &>(expectedUrls)) 0377 QCOMPARE(extractedUrls.contains(expectedUrl), true); 0378 } 0379 0380 static const char *fileImporterExporterTestCases_Label_Empty_file = "Empty file"; 0381 static const char *fileImporterExporterTestCases_Label_Moby_Dick = "Moby Dick"; 0382 0383 File *KBibTeXIOTest::mobyDickBibliography() 0384 { 0385 static File *mobyDickFile = nullptr; 0386 if (mobyDickFile == nullptr) { 0387 /// File with single entry, inspired by 'Moby Dick' 0388 mobyDickFile = new File(); 0389 QSharedPointer<Entry> entry(new Entry(Entry::etBook, QStringLiteral("the-whale-1851"))); 0390 mobyDickFile->append(entry); 0391 entry->insert(Entry::ftTitle, Value() << QSharedPointer<PlainText>(new PlainText(QStringLiteral("{Call me Ishmael}")))); 0392 entry->insert(Entry::ftAuthor, Value() << QSharedPointer<Person>(new Person(QStringLiteral("Herman"), QStringLiteral("Melville"))) << QSharedPointer<Person>(new Person(QStringLiteral("Moby"), QStringLiteral("Dick")))); 0393 entry->insert(Entry::ftMonth, Value() << QSharedPointer<MacroKey>(new MacroKey(QStringLiteral("jun")))); 0394 entry->insert(Entry::ftYear, Value() << QSharedPointer<PlainText>(new PlainText(QStringLiteral("1851")))); 0395 mobyDickFile->setProperty(File::ProtectCasing, Qt::Checked); 0396 } 0397 return mobyDickFile; 0398 } 0399 0400 File *KBibTeXIOTest::latinUmlautBibliography() 0401 { 0402 static File *latinUmlautFile = nullptr; 0403 if (latinUmlautFile == nullptr) { 0404 latinUmlautFile = new File(); 0405 QSharedPointer<Entry> entry(new Entry(Entry::etArticle, QStringLiteral("einstein1907relativitaetsprinzip"))); 0406 latinUmlautFile->append(entry); 0407 entry->insert(Entry::ftTitle, Value() << QSharedPointer<PlainText>(new PlainText(QString(QStringLiteral("{%1ber das Relativit%2tsprinzip und die aus demselben gezogenen Folgerungen}")).arg(QChar(0x00DC)).arg(QChar(0x00E4))))); 0408 entry->insert(Entry::ftAuthor, Value() << QSharedPointer<Person>(new Person(QStringLiteral("Albert"), QStringLiteral("Einstein")))); 0409 entry->insert(Entry::ftYear, Value() << QSharedPointer<PlainText>(new PlainText(QStringLiteral("1907/08")))); 0410 entry->insert(Entry::ftPages, Value() << QSharedPointer<PlainText>(new PlainText(QStringLiteral("23") + QChar(0x2013) + QStringLiteral("42")))); 0411 latinUmlautFile->setProperty(File::ProtectCasing, Qt::Checked); 0412 } 0413 return latinUmlautFile; 0414 } 0415 0416 File *KBibTeXIOTest::onlyComments(int numPercentSigns, int numLines) 0417 { 0418 const int key = numPercentSigns * 1024 + numLines; 0419 static QHash<int, File *> commentFiles; 0420 if (!commentFiles.contains(key)) { 0421 File *commentFile = new File(); 0422 const QString percentSigns {numPercentSigns > 0 ? QString(numPercentSigns, QLatin1Char('%')) + QStringLiteral(" ") : QString()}; 0423 QString rawText {numPercentSigns < 0 ? QStringLiteral("Inside command") : (numPercentSigns > 0 ? QString(QStringLiteral("%1 percent symbol(s), %2 line(s)")).arg(QString::number(numPercentSigns), QString::number(numLines)) : QStringLiteral("Direct comment"))}; 0424 const QString moreLinesRawText {QStringLiteral("\nOne more line")}; 0425 for (int i = 1; i < numLines; ++i) 0426 rawText.append(moreLinesRawText); 0427 QSharedPointer<Comment> comment(new Comment(rawText, numPercentSigns < 0 ? Preferences::CommentContext::Command : (numPercentSigns > 0 ? Preferences::CommentContext::Prefix : Preferences::CommentContext::Verbatim), percentSigns)); 0428 commentFile->append(comment); 0429 commentFiles.insert(key, commentFile); 0430 } 0431 return commentFiles[key]; 0432 } 0433 0434 File *KBibTeXIOTest::koreanBibliography() 0435 { 0436 static File *koreanFile = nullptr; 0437 if (koreanFile == nullptr) { 0438 koreanFile = new File(); 0439 QSharedPointer<Entry> entry(new Entry(Entry::etMastersThesis, QStringLiteral("korean-text-copied-from-dailynk"))); 0440 koreanFile->append(entry); 0441 entry->insert(Entry::ftTitle, Value() << QSharedPointer<PlainText>(new PlainText(QString::fromUtf8("\xeb\xb3\xb4\xec\x9c\x84\xea\xb5\xad \xec\x88\x98\xec\x82\xac\xeb\xb6\x80\xc2\xb7\xec\x98\x81\xec\xb0\xbd\xea\xb4\x80\xeb\xa6\xac\xeb\xb6\x80 \xec\x8a\xb9\xea\xb2\xa9\xe2\x80\xa6\xea\xb9\x80\xec\xa0\x95\xec\x9d\x80, \xeb\xb6\x80\xec\xa0\x95\xeb\xb6\x80\xed\x8c\xa8\xec\x99\x80 \xec\xa0\x84\xec\x9f\x81 \xec\x98\x88\xea\xb3\xa0")))); 0442 entry->insert(Entry::ftAuthor, Value() << QSharedPointer<Person>(new Person(QString::fromUtf8("\xec\xa0\x95\xec\x9d\x80"), QString::fromUtf8("\xea\xb9\x80")))); 0443 entry->insert(Entry::ftYear, Value() << QSharedPointer<PlainText>(new PlainText(QStringLiteral("1921")))); 0444 koreanFile->setProperty(File::NameFormatting, Preferences::personNameFormatLastFirst); 0445 koreanFile->setProperty(File::ProtectCasing, Qt::Unchecked); 0446 } 0447 return koreanFile; 0448 } 0449 0450 File *KBibTeXIOTest::russianBibliography() 0451 { 0452 static File *russianFile = nullptr; 0453 if (russianFile == nullptr) { 0454 russianFile = new File(); 0455 QSharedPointer<Entry> entry(new Entry(Entry::etBook, QStringLiteral("war-and-peace"))); 0456 russianFile->append(entry); 0457 entry->insert(Entry::ftTitle, Value() << QSharedPointer<PlainText>(new PlainText(QString::fromUtf8("\xd0\x92\xd0\xbe\xd0\xb9\xd0\xbd\xd0\xb0 \xd0\xb8 \xd0\xbc\xd0\xb8\xd1\x80")))); 0458 entry->insert(Entry::ftAuthor, Value() << QSharedPointer<Person>(new Person(QString::fromUtf8("\xd0\x9b\xd0\xb5\xd0\xb2"), QString::fromUtf8("{\xd0\x9d\xd0\xb8\xd0\xba\xd0\xbe\xd0\xbb\xd0\xb0\xd0\xb5\xd0\xb2\xd0\xb8\xd1\x87 \xd0\xa2\xd0\xbe\xd0\xbb\xd1\x81\xd1\x82\xd0\xbe\xd0\xb9}")))); 0459 entry->insert(Entry::ftYear, Value() << QSharedPointer<PlainText>(new PlainText(QStringLiteral("1869")))); 0460 russianFile->setProperty(File::NameFormatting, Preferences::personNameFormatLastFirst); 0461 russianFile->setProperty(File::ProtectCasing, Qt::Unchecked); 0462 } 0463 return russianFile; 0464 } 0465 0466 QVector<QPair<const char *, File *> > KBibTeXIOTest::fileImporterExporterTestCases() 0467 { 0468 /// The vector 'result' is static so that if this function is invoked multiple 0469 /// times, the vector will be initialized and filled with File objects only upon 0470 /// the function's first invocation. 0471 static QVector<QPair<const char *, File *> > result; 0472 0473 if (result.isEmpty()) { 0474 /// Empty file without any entries 0475 result.append(QPair<const char *, File *>(fileImporterExporterTestCases_Label_Empty_file, new File())); 0476 0477 /// File with single entry, inspired by 'Moby Dick' 0478 result.append(QPair<const char *, File *>(fileImporterExporterTestCases_Label_Moby_Dick, mobyDickBibliography())); 0479 0480 // TODO add more file objects to result vector 0481 0482 /// Set various properties to guarantee reproducible results irrespective of local settings 0483 for (auto it = result.constBegin(); it != result.constEnd(); ++it) { 0484 File *file = it->second; 0485 file->setProperty(File::NameFormatting, Preferences::personNameFormatLastFirst); 0486 file->setProperty(File::ProtectCasing, static_cast<int>(Qt::Checked)); 0487 // TODO more file properties to set? 0488 } 0489 } 0490 0491 return result; 0492 } 0493 0494 void KBibTeXIOTest::fileExporterXMLsave_data() 0495 { 0496 QTest::addColumn<File *>("bibTeXfile"); 0497 QTest::addColumn<MapFileExporterXMLOutputStyleToQString>("expectedData"); 0498 0499 static const QHash<const char *, MapFileExporterXMLOutputStyleToQString> keyToXmlData { 0500 { fileImporterExporterTestCases_Label_Empty_file, { 0501 {FileExporterXML::OutputStyle::XML_KBibTeX, QStringLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?>|<!-- XML document written by KBibTeXIO as part of KBibTeX -->|<!-- https://userbase.kde.org/KBibTeX -->|<bibliography>|</bibliography>|")}, 0502 {FileExporterXML::OutputStyle::HTML_Standard, QStringLiteral("<!DOCTYPE html>|<!-- HTML document written by KBibTeXIO as part of KBibTeX -->|<!-- https://userbase.kde.org/KBibTeX -->|<html xmlns=\"http://www.w3.org/1999/xhtml\">|<head><title>Bibliography</title><meta charset=\"utf-8\"></head>|<body>|</body></html>||")}, 0503 {FileExporterXML::OutputStyle::HTML_AbstractOnly, QStringLiteral("<!DOCTYPE html>|<!-- HTML document written by KBibTeXIO as part of KBibTeX -->|<!-- https://userbase.kde.org/KBibTeX -->|<html xmlns=\"http://www.w3.org/1999/xhtml\">|<head><title>Bibliography</title><meta charset=\"utf-8\"></head>|<body>|</body></html>||")}, 0504 {FileExporterXML::OutputStyle::Plain_WikipediaCite, QString()} 0505 } 0506 }, 0507 { fileImporterExporterTestCases_Label_Moby_Dick, { 0508 {FileExporterXML::OutputStyle::XML_KBibTeX, QStringLiteral("<?xml version=\"1.0\" encoding=\"UTF-8\"?>|<!-- XML document written by KBibTeXIO as part of KBibTeX -->|<!-- https://userbase.kde.org/KBibTeX -->|<bibliography>| <entry id=\"the-whale-1851\" type=\"book\">| <authors>|<person><firstname>Herman</firstname><lastname>Melville</lastname></person><person><firstname>Moby</firstname><lastname>Dick</lastname></person>| </authors>| <month triple=\"jun\" number=\"6\"><text>June</text></month>| <title><text>Call me Ishmael</text></title>| <year number=\"1851\"><text>1851</text></year>| </entry>|</bibliography>|")}, 0509 {FileExporterXML::OutputStyle::HTML_Standard, QStringLiteral("<!DOCTYPE html>|<!-- HTML document written by KBibTeXIO as part of KBibTeX -->|<!-- https://userbase.kde.org/KBibTeX -->|<html xmlns=\"http://www.w3.org/1999/xhtml\">|<head><title>Bibliography</title><meta charset=\"utf-8\"></head>|<body>|<p>Melville<span style=\"opacity:0.75;\">, Herman</span>, Dick<span style=\"opacity:0.75;\">, Moby</span>: <strong>Call me Ishmael</strong>, June 1851</p>|</body></html>||")}, 0510 {FileExporterXML::OutputStyle::HTML_AbstractOnly, QString()}, 0511 {FileExporterXML::OutputStyle::Plain_WikipediaCite, QStringLiteral("{{Citation|| title = Call me Ishmael|| year = 1851||last1 = Melville||first1 = Herman||last2 = Dick||first2 = Moby|}}|")} 0512 } 0513 } 0514 }; 0515 static const QVector<QPair<const char *, File *> > keyFileTable = fileImporterExporterTestCases(); 0516 0517 for (auto it = keyFileTable.constBegin(); it != keyFileTable.constEnd(); ++it) 0518 if (keyToXmlData.contains(it->first)) 0519 QTest::newRow(it->first) << it->second << keyToXmlData.value(it->first); 0520 } 0521 0522 void KBibTeXIOTest::fileExporterXMLsave() 0523 { 0524 QFETCH(File *, bibTeXfile); 0525 QFETCH(MapFileExporterXMLOutputStyleToQString, expectedData); 0526 0527 FileExporterXML fileExporterXML(this); 0528 0529 for (auto it = expectedData.constBegin(); it != expectedData.constEnd(); ++it) { 0530 fileExporterXML.setOutputStyle(it.key()); 0531 const QString &expectedText = it.value(); 0532 const QString generatedText = fileExporterXML.toString(bibTeXfile).remove(QLatin1Char('\r')).replace(QLatin1Char('\n'), QLatin1Char('|')); 0533 QCOMPARE(generatedText, expectedText); 0534 } 0535 } 0536 0537 void KBibTeXIOTest::fileExporterRISsave_data() 0538 { 0539 QTest::addColumn<File *>("bibTeXfile"); 0540 QTest::addColumn<QString>("risData"); 0541 0542 static const QHash<const char *, QString> keyToRisData { 0543 {fileImporterExporterTestCases_Label_Empty_file, QString()}, 0544 {fileImporterExporterTestCases_Label_Moby_Dick, QStringLiteral("TY - BOOK|ID - the-whale-1851|AU - Melville, Herman|AU - Dick, Moby|TI - Call me Ishmael|PY - 1851/06//|ER - ||")} 0545 }; 0546 static const QVector<QPair<const char *, File *> > keyFileTable = fileImporterExporterTestCases(); 0547 0548 for (auto it = keyFileTable.constBegin(); it != keyFileTable.constEnd(); ++it) 0549 if (keyToRisData.contains(it->first)) 0550 QTest::newRow(it->first) << it->second << keyToRisData.value(it->first); 0551 } 0552 0553 void KBibTeXIOTest::fileExporterRISsave() 0554 { 0555 QFETCH(File *, bibTeXfile); 0556 QFETCH(QString, risData); 0557 0558 FileExporterRIS fileExporterRIS(this); 0559 const QString generatedData = fileExporterRIS.toString(bibTeXfile).remove(QLatin1Char('\r')).replace(QLatin1Char('\n'), QLatin1Char('|')); 0560 0561 QCOMPARE(generatedData, risData); 0562 } 0563 0564 void KBibTeXIOTest::fileExporterBibTeXsave_data() 0565 { 0566 QTest::addColumn<File *>("bibTeXfile"); 0567 QTest::addColumn<QString>("bibTeXdata"); 0568 0569 static const QHash<const char *, QString> keyToBibTeXData { 0570 {fileImporterExporterTestCases_Label_Empty_file, QString()}, 0571 {fileImporterExporterTestCases_Label_Moby_Dick, QStringLiteral("@book{the-whale-1851,|\tauthor = {Melville, Herman and Dick, Moby},|\tmonth = jun,|\ttitle = {{Call me Ishmael}},|\tyear = {1851}|}||")} 0572 }; 0573 static const QVector<QPair<const char *, File *> > keyFileTable = fileImporterExporterTestCases(); 0574 0575 for (auto it = keyFileTable.constBegin(); it != keyFileTable.constEnd(); ++it) 0576 if (keyToBibTeXData.contains(it->first)) 0577 QTest::newRow(it->first) << it->second << keyToBibTeXData.value(it->first); 0578 } 0579 0580 void KBibTeXIOTest::fileExporterBibTeXsave() 0581 { 0582 QFETCH(File *, bibTeXfile); 0583 QFETCH(QString, bibTeXdata); 0584 0585 FileExporterBibTeX fileExporterBibTeX(this); 0586 const QString generatedData = fileExporterBibTeX.toString(bibTeXfile).remove(QLatin1Char('\r')).replace(QLatin1Char('\n'), QLatin1Char('|')); 0587 0588 QCOMPARE(generatedData, bibTeXdata); 0589 } 0590 0591 void KBibTeXIOTest::fileImporterRISload_data() 0592 { 0593 QTest::addColumn<QByteArray>("risData"); 0594 QTest::addColumn<File *>("bibTeXfile"); 0595 0596 static const QHash<const char *, QString> keyToRisData { 0597 {fileImporterExporterTestCases_Label_Empty_file, QString()}, 0598 {fileImporterExporterTestCases_Label_Moby_Dick, QStringLiteral("TY - BOOK|ID - the-whale-1851|AU - Melville, Herman|AU - Dick, Moby|TI - Call me Ishmael|PY - 1851/06//|ER - ||")} 0599 }; 0600 static const QVector<QPair<const char *, File *> > keyFileTable = fileImporterExporterTestCases(); 0601 0602 for (auto it = keyFileTable.constBegin(); it != keyFileTable.constEnd(); ++it) 0603 if (keyToRisData.contains(it->first)) 0604 QTest::newRow(it->first) << keyToRisData.value(it->first).toUtf8().replace('|', '\n') << it->second; 0605 } 0606 0607 void KBibTeXIOTest::fileImporterRISload() 0608 { 0609 QFETCH(QByteArray, risData); 0610 QFETCH(File *, bibTeXfile); 0611 0612 FileImporterRIS fileImporterRIS(this); 0613 fileImporterRIS.setProtectCasing(true); 0614 QBuffer buffer(&risData); 0615 buffer.open(QBuffer::ReadOnly); 0616 QScopedPointer<File> generatedFile(fileImporterRIS.load(&buffer)); 0617 0618 QVERIFY(generatedFile->operator ==(*bibTeXfile)); 0619 } 0620 0621 void KBibTeXIOTest::fileImporterBibTeXload_data() 0622 { 0623 QTest::addColumn<QByteArray>("bibTeXdata"); 0624 QTest::addColumn<File *>("bibTeXfile"); 0625 0626 static const QHash<const char *, QString> keyToBibTeXData { 0627 {fileImporterExporterTestCases_Label_Empty_file, QString()}, 0628 {fileImporterExporterTestCases_Label_Moby_Dick, QStringLiteral("@book{the-whale-1851,|\tauthor = {Melville, Herman and Dick, Moby},|\tmonth = jun,|\ttitle = {{Call me Ishmael}},|\tyear = {1851}|}||")} 0629 }; 0630 static const QVector<QPair<const char *, File *> > keyFileTable = fileImporterExporterTestCases(); 0631 0632 for (auto it = keyFileTable.constBegin(); it != keyFileTable.constEnd(); ++it) 0633 if (keyToBibTeXData.contains(it->first)) 0634 QTest::newRow(it->first) << keyToBibTeXData.value(it->first).toUtf8().replace('|', '\n') << it->second ; 0635 } 0636 0637 void KBibTeXIOTest::fileImporterBibTeXload() 0638 { 0639 QFETCH(QByteArray, bibTeXdata); 0640 QFETCH(File *, bibTeXfile); 0641 0642 FileImporterBibTeX fileImporterBibTeX(this); 0643 QBuffer buffer(&bibTeXdata); 0644 buffer.open(QBuffer::ReadOnly); 0645 QScopedPointer<File> generatedFile(fileImporterBibTeX.load(&buffer)); 0646 0647 QVERIFY(generatedFile->operator ==(*bibTeXfile)); 0648 } 0649 0650 void KBibTeXIOTest::protectiveCasingEntryGeneratedOnTheFly() 0651 { 0652 static const QString titleText = QStringLiteral("Some Title for a Journal Article"); 0653 static const QString singleCurleyBracketTitle = QStringLiteral("{") + titleText + QStringLiteral("}"); 0654 static const QString doubleCurleyBracketTitle = QStringLiteral("{{") + titleText + QStringLiteral("}}"); 0655 0656 FileExporterBibTeX fileExporterBibTeX(this); 0657 0658 /// Create a simple File object with a title field 0659 File file; 0660 file.setProperty(File::StringDelimiter, QStringLiteral("{}")); 0661 QSharedPointer<Entry> entry {new Entry(Entry::etArticle, QStringLiteral("SomeId"))}; 0662 Value titleValue = Value() << QSharedPointer<PlainText>(new PlainText(titleText)); 0663 entry->insert(Entry::ftTitle, titleValue); 0664 file.append(entry); 0665 0666 file.setProperty(File::ProtectCasing, Qt::Checked); 0667 const QString textWithProtectiveCasing = fileExporterBibTeX.toString(&file); 0668 QVERIFY(textWithProtectiveCasing.contains(doubleCurleyBracketTitle)); 0669 0670 file.setProperty(File::ProtectCasing, Qt::Unchecked); 0671 const QString textWithoutProtectiveCasing = fileExporterBibTeX.toString(&file); 0672 QVERIFY(textWithoutProtectiveCasing.contains(singleCurleyBracketTitle) 0673 && !textWithoutProtectiveCasing.contains(doubleCurleyBracketTitle)); 0674 } 0675 0676 void KBibTeXIOTest::fileExporterBibTeXEncoding_data() 0677 { 0678 QTest::addColumn<File *>("bibTeXfile"); 0679 QTest::addColumn<QString>("encoding"); 0680 QTest::addColumn<QSet<QByteArray>>("expectedOutputOptionalBOMs"); 0681 QTest::addColumn<QByteArray>("expectedOutput"); 0682 0683 static const QSet<QByteArray> bomUTF8 {{"\xEF\xBB\xBF"}}; 0684 static const QByteArray mobyDickEntryOutput {"@book{the-whale-1851,\n\tauthor = {Melville, Herman and Dick, Moby},\n\tmonth = jun,\n\ttitle = {{Call me Ishmael}},\n\tyear = {1851}\n}\n\n"}; 0685 static const QStringList listOfASCIIcompatibleEncodings {QStringLiteral("LaTeX"), QStringLiteral("UTF-8"), QStringLiteral("ISO-8859-1"), QStringLiteral("Windows-1254"), QStringLiteral("ISO 2022-JP") /** technically not ASCII-compatible, but works for this case here */}; 0686 for (const QString &encoding : listOfASCIIcompatibleEncodings) { 0687 const QByteArray encodingOutput{QByteArray{"@comment{x-kbibtex-encoding="} +encoding.toLatin1() + QByteArray{"}\n\n"}}; 0688 const QByteArray mobyDickExpectedOutput{encoding == QStringLiteral("LaTeX") ? mobyDickEntryOutput : encodingOutput + mobyDickEntryOutput}; 0689 const QSet<QByteArray> bom {encoding == QStringLiteral("UTF-8") ? bomUTF8 : QSet<QByteArray>()}; 0690 QTest::newRow(QString(QStringLiteral("Moby Dick (ASCII only) encoded in '%1'")).arg(encoding).toLatin1().constData()) << mobyDickBibliography() << encoding << bom << mobyDickExpectedOutput; 0691 } 0692 static const QSet<QByteArray> bomUTF16 {{"\xFF\xFE"}, {"\xFE\xFF"}}; 0693 static const QByteArray mobyDickExpectedOutputUTF16 {"@\0""b\0o\0o\0k\0{\0t\0h\0""e\0-\0w\0h\0""a\0l\0""e\0-\0""1\0""8\0""5\0""1\0,\0\n\0\x09\0""a\0u\0t\0h\0o\0r\0 \0=\0 \0{\0M\0""e\0l\0v\0i\0l\0l\0""e\0,\0 \0H\0""e\0r\0m\0""a\0n\0 \0""a\0n\0""d\0 \0""D\0i\0""c\0k\0,\0 \0M\0o\0""b\0y\0}\0,\0\n\0\x09\0m\0o\0n\0t\0h\0 \0=\0 \0j\0u\0n\0,\0\n\0\x09\0t\0i\0t\0l\0""e\0 \0=\0 \0{\0{\0""C\0""a\0l\0l\0 \0m\0""e\0 \0I\0s\0h\0m\0""a\0""e\0l\0}\0}\0,\0\n\0\x09\0y\0""e\0""a\0r\0 \0=\0 \0{\0""1\0""8\0""5\0""1\0}\0\n\0}\0\n\0\n\0", 258}; 0694 QTest::newRow("Moby Dick (ASCII only) encoded in 'UTF-16'") << mobyDickBibliography() << QStringLiteral("UTF-16") << bomUTF16 << mobyDickExpectedOutputUTF16; 0695 static const QSet<QByteArray> bomUTF32 {QByteArray("\xFF\xFE\x00\x00", 4), {"\x00\x00\xFE\xFF"}}; 0696 static const QByteArray mobyDickExpectedOutputUTF32 {"@\0\0\0""b\0\0\0o\0\0\0o\0\0\0k\0\0\0{\0\0\0t\0\0\0h\0\0\0""e\0\0\0-\0\0\0w\0\0\0h\0\0\0""a\0\0\0l\0\0\0""e\0\0\0-\0\0\0""1\0\0\0""8\0\0\0""5\0\0\0""1\0\0\0,\0\0\0\n\0\0\0\x09\0\0\0""a\0\0\0u\0\0\0t\0\0\0h\0\0\0o\0\0\0r\0\0\0 \0\0\0=\0\0\0 \0\0\0{\0\0\0M\0\0\0""e\0\0\0l\0\0\0v\0\0\0i\0\0\0l\0\0\0l\0\0\0""e\0\0\0,\0\0\0 \0\0\0H\0\0\0""e\0\0\0r\0\0\0m\0\0\0""a\0\0\0n\0\0\0 \0\0\0""a\0\0\0n\0\0\0""d\0\0\0 \0\0\0""D\0\0\0i\0\0\0""c\0\0\0k\0\0\0,\0\0\0 \0\0\0M\0\0\0o\0\0\0""b\0\0\0y\0\0\0}\0\0\0,\0\0\0\n\0\0\0\x09\0\0\0m\0\0\0o\0\0\0n\0\0\0t\0\0\0h\0\0\0 \0\0\0=\0\0\0 \0\0\0j\0\0\0u\0\0\0n\0\0\0,\0\0\0\n\0\0\0\x09\0\0\0t\0\0\0i\0\0\0t\0\0\0l\0\0\0""e\0\0\0 \0\0\0=\0\0\0 \0\0\0{\0\0\0{\0\0\0""C\0\0\0""a\0\0\0l\0\0\0l\0\0\0 \0\0\0m\0\0\0""e\0\0\0 \0\0\0I\0\0\0s\0\0\0h\0\0\0m\0\0\0""a\0\0\0""e\0\0\0l\0\0\0}\0\0\0}\0\0\0,\0\0\0\n\0\0\0\x09\0\0\0y\0\0\0""e\0\0\0""a\0\0\0r\0\0\0 \0\0\0=\0\0\0 \0\0\0{\0\0\0""1\0\0\0""8\0\0\0""5\0\0\0""1\0\0\0}\0\0\0\n\0\0\0}\0\0\0\n\0\0\0\n\0\0\0", 516}; 0697 QTest::newRow("Moby Dick (ASCII only) encoded in 'UTF-32'") << mobyDickBibliography() << QStringLiteral("UTF-32") << bomUTF32 << mobyDickExpectedOutputUTF32; 0698 static const QByteArray latinUmlautExpectedOutputLaTeX {"@article{einstein1907relativitaetsprinzip,\n\tauthor = {Einstein, Albert},\n\tpages = {23--42},\n\ttitle = {{{\\\"U}ber das Relativit{\\\"a}tsprinzip und die aus demselben gezogenen Folgerungen}},\n\tyear = {1907/08}\n}\n\n"}; 0699 QTest::newRow("Albert Einstein (latin umlaut) data encoded in 'LaTeX'") << latinUmlautBibliography() << QStringLiteral("LaTeX") << bomUTF8 << latinUmlautExpectedOutputLaTeX; 0700 static const QByteArray latinUmlautExpectedOutputUTF8 {"@comment{x-kbibtex-encoding=UTF-8}\n\n@article{einstein1907relativitaetsprinzip,\n\tauthor = {Einstein, Albert},\n\tpages = {23\xE2\x80\x93""42},\n\ttitle = {{\xC3\x9C""ber das Relativit\xC3\xA4tsprinzip und die aus demselben gezogenen Folgerungen}},\n\tyear = {1907/08}\n}\n\n"}; 0701 QTest::newRow("Albert Einstein (latin umlaut) encoded in 'UTF-8'") << latinUmlautBibliography() << QStringLiteral("UTF-8") << bomUTF8 << latinUmlautExpectedOutputUTF8; 0702 } 0703 0704 void KBibTeXIOTest::fileExporterBibTeXEncoding() 0705 { 0706 QFETCH(File *, bibTeXfile); 0707 QFETCH(QString, encoding); 0708 QFETCH(QSet<QByteArray>, expectedOutputOptionalBOMs); 0709 QFETCH(QByteArray, expectedOutput); 0710 0711 FileExporterBibTeX exporter(this); 0712 exporter.setEncoding(encoding); 0713 QByteArray generatedOutput; 0714 generatedOutput.reserve(8192); 0715 QBuffer buffer(&generatedOutput); 0716 buffer.open(QBuffer::WriteOnly); 0717 QVERIFY(exporter.save(&buffer, bibTeXfile)); 0718 buffer.close(); 0719 0720 bool anyMatch = generatedOutput == expectedOutput; 0721 for (const QByteArray &bom : expectedOutputOptionalBOMs) { 0722 if (anyMatch) break; 0723 const QByteArray merged{bom + expectedOutput}; 0724 anyMatch |= merged == generatedOutput; 0725 } 0726 QVERIFY2(anyMatch, "generatedOutput does not match expectedOutput (even with BOM)"); 0727 } 0728 0729 void KBibTeXIOTest::fileImportExportBibTeXroundtrip_data() { 0730 struct TestCase { 0731 QString label; 0732 File *bibliography; 0733 QStringList encodingsToBeTested; 0734 FileImporterBibTeX::CommentHandling fileImporterCommentHandling; 0735 }; 0736 static const QVector<struct TestCase> testCases {/* 0737 {QStringLiteral("Moby Dick"), mobyDickBibliography(), Preferences::availableBibTeXEncodings, FileImporterBibTeX::CommentHandling::Ignore}, 0738 {QStringLiteral("Albert Einstein"), latinUmlautBibliography(), Preferences::availableBibTeXEncodings, FileImporterBibTeX::CommentHandling::Ignore}, 0739 {QStringLiteral("Kim Jong-un"), koreanBibliography(), {QStringLiteral("LaTeX"), QStringLiteral("UTF-8"), QStringLiteral("UTF-16"), QStringLiteral("UTF-16BE"), QStringLiteral("UTF-16LE"), QStringLiteral("UTF-32"), QStringLiteral("UTF-32BE"), QStringLiteral("UTF-32LE"), QStringLiteral("GB18030"), QStringLiteral("EUC-KR"), QStringLiteral("Windows-949")}, FileImporterBibTeX::CommentHandling::Ignore}, 0740 {QStringLiteral("L. Tolstoy"), russianBibliography(), {QStringLiteral("LaTeX"), QStringLiteral("ISO-8859-5"), QStringLiteral("UTF-8"), QStringLiteral("UTF-16"), QStringLiteral("UTF-16BE"), QStringLiteral("UTF-16LE"), QStringLiteral("UTF-32"), QStringLiteral("UTF-32BE"), QStringLiteral("UTF-32LE"), QStringLiteral("KOI8-R"), QStringLiteral("KOI8-U"), QStringLiteral("Big5-HKSCS"), QStringLiteral("GB18030"), QStringLiteral("EUC-JP"), QStringLiteral("EUC-KR"), QStringLiteral("ISO 2022-JP"), QStringLiteral("Shift-JIS"), QStringLiteral("Windows-949"), QStringLiteral("Windows-1251")}, FileImporterBibTeX::CommentHandling::Ignore},*/ 0741 {QStringLiteral("Only comments (Command, 1 line)"), onlyComments(-1, 1), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}/*, 0742 {QStringLiteral("Only comments (Direct, 1 line)"), onlyComments(0, 1), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0743 {QStringLiteral("Only comments ('%', 1 line)"), onlyComments(1, 1), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0744 {QStringLiteral("Only comments ('%%', 1 line)"), onlyComments(2, 1), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0745 {QStringLiteral("Only comments ('%%%', 1 line)"), onlyComments(3, 1), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0746 {QStringLiteral("Only comments (Command, 2 lines)"), onlyComments(-1, 2), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0747 {QStringLiteral("Only comments (Direct, 2 lines)"), onlyComments(0, 2), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0748 {QStringLiteral("Only comments ('%', 2 lines)"), onlyComments(1, 2), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0749 {QStringLiteral("Only comments ('%%', 2 lines)"), onlyComments(2, 2), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0750 {QStringLiteral("Only comments ('%%%', 2 lines)"), onlyComments(3, 2), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0751 {QStringLiteral("Only comments (Command, 3 lines)"), onlyComments(-1, 3), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0752 {QStringLiteral("Only comments (Direct, 3 lines)"), onlyComments(0, 3), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0753 {QStringLiteral("Only comments ('%', 3 lines)"), onlyComments(1, 3), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0754 {QStringLiteral("Only comments ('%%', 3 lines)"), onlyComments(2, 3), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}, 0755 {QStringLiteral("Only comments ('%%%', 3 lines)"), onlyComments(3, 3), {QStringLiteral("UTF-8")}, FileImporterBibTeX::CommentHandling::Keep}*/ 0756 }; 0757 0758 QTest::addColumn<File *>("bibliography"); 0759 QTest::addColumn<QString>("encoding"); 0760 QTest::addColumn<FileImporterBibTeX::CommentHandling>("fileImporterCommentHandling"); 0761 0762 for (const TestCase &testCase : testCases) { 0763 for (const QString &encoding : testCase.encodingsToBeTested) { 0764 const QString testLabel = QString(QStringLiteral("Round-trip of '%1' encoded in '%2'")).arg(testCase.label).arg(encoding); 0765 QTest::newRow(testLabel.toLatin1().constData()) << testCase.bibliography << encoding << testCase.fileImporterCommentHandling; 0766 } 0767 } 0768 } 0769 0770 void KBibTeXIOTest::fileImportExportBibTeXroundtrip() 0771 { 0772 QFETCH(File *, bibliography); 0773 QFETCH(QString, encoding); 0774 QFETCH(FileImporterBibTeX::CommentHandling, fileImporterCommentHandling); 0775 0776 FileImporterBibTeX importer(this); 0777 importer.setCommentHandling(fileImporterCommentHandling); 0778 0779 // The point with this test is to apply various encodings (e.g. 'UTF-8', 'ISO 2022-JP') 0780 // to example bibliography files when writing to a buffer (a in-memory representation of 0781 // a real .bib file). 0782 // The encoding can by set in two different ways: 0783 // 1. As a property of the File object 0784 // 2. Enforced upon a FileExporterBibTeX instance, ignoring the File's encoding property 0785 0786 // Fist, the forced-upon case will be executed 0787 0788 QByteArray ba(1 << 12, '\0'); 0789 QBuffer buffer(&ba); 0790 0791 buffer.open(QBuffer::WriteOnly); 0792 FileExporterBibTeX exporterWithForcedEncoding(this); 0793 exporterWithForcedEncoding.setEncoding(encoding); //< Force the encoding on the FileExporterBibTeX instance 0794 QVERIFY(exporterWithForcedEncoding.save(&buffer, bibliography)); 0795 const qint64 bytesWrittenWithForcedEncoding = buffer.pos(); 0796 QVERIFY(bytesWrittenWithForcedEncoding > 32); //< All bibliographies in test have a certain minimum size 0797 buffer.close(); 0798 0799 ba.resize(static_cast<int>(bytesWrittenWithForcedEncoding & 0x7fffffff)); 0800 0801 buffer.open(QBuffer::ReadOnly); 0802 File *loadedFile = importer.load(&buffer); 0803 buffer.close(); 0804 0805 QVERIFY(loadedFile != nullptr); 0806 QVERIFY(loadedFile->length() == 1); 0807 QVERIFY(bibliography->operator ==(*loadedFile)); 0808 delete loadedFile; 0809 0810 // Second, the File encoding property case will be executed 0811 0812 ba.fill('\0', 1 << 12); //< reset and clear buffer from above execution 0813 0814 buffer.open(QBuffer::WriteOnly); 0815 FileExporterBibTeX exporterWithFileEncoding(this); 0816 bibliography->setProperty(File::Encoding, encoding); //< set the File's encoding property 0817 QVERIFY(exporterWithFileEncoding.save(&buffer, bibliography)); 0818 const qint64 bytesWrittenWithFileEncoding = buffer.pos(); 0819 QVERIFY(bytesWrittenWithFileEncoding > 32); //< All bibliographies in test have a certain minimum size 0820 buffer.close(); 0821 0822 ba.resize(static_cast<int>(bytesWrittenWithFileEncoding & 0x7fffffff)); 0823 0824 buffer.open(QBuffer::ReadOnly); 0825 loadedFile = importer.load(&buffer); 0826 buffer.close(); 0827 0828 QVERIFY(loadedFile != nullptr); 0829 QVERIFY(loadedFile->length() == 1); 0830 QVERIFY(bibliography->operator ==(*loadedFile)); 0831 delete loadedFile; 0832 } 0833 0834 void KBibTeXIOTest::protectiveCasingEntryFromData() 0835 { 0836 static const QString titleText = QStringLiteral("Some Title for a Journal Article"); 0837 static const QString singleCurleyBracketTitle = QStringLiteral("{") + titleText + QStringLiteral("}"); 0838 static const QString doubleCurleyBracketTitle = QStringLiteral("{{") + titleText + QStringLiteral("}}"); 0839 static const QString bibTeXDataDoubleCurleyBracketTitle = QStringLiteral("@articl{doubleCurleyBracketTitle,\ntitle={{") + titleText + QStringLiteral("}}\n}\n"); 0840 static const QString bibTeXDataSingleCurleyBracketTitle = QStringLiteral("@articl{singleCurleyBracketTitle,\ntitle={") + titleText + QStringLiteral("}\n}\n"); 0841 0842 FileImporterBibTeX fileImporterBibTeX(this); 0843 FileExporterBibTeX fileExporterBibTeX(this); 0844 0845 QByteArray b1(bibTeXDataDoubleCurleyBracketTitle.toUtf8()); 0846 QBuffer bufferDoubleCurleyBracketTitle(&b1, this); 0847 QByteArray b2(bibTeXDataSingleCurleyBracketTitle.toUtf8()); 0848 QBuffer bufferSingleCurleyBracketTitle(&b2, this); 0849 0850 bufferDoubleCurleyBracketTitle.open(QBuffer::ReadOnly); 0851 QScopedPointer<File> fileDoubleCurleyBracketTitle(fileImporterBibTeX.load(&bufferDoubleCurleyBracketTitle)); 0852 bufferDoubleCurleyBracketTitle.close(); 0853 fileDoubleCurleyBracketTitle->setProperty(File::StringDelimiter, QStringLiteral("{}")); 0854 bufferSingleCurleyBracketTitle.open(QBuffer::ReadOnly); 0855 QScopedPointer<File> fileSingleCurleyBracketTitle(fileImporterBibTeX.load(&bufferSingleCurleyBracketTitle)); 0856 bufferSingleCurleyBracketTitle.close(); 0857 fileSingleCurleyBracketTitle->setProperty(File::StringDelimiter, QStringLiteral("{}")); 0858 0859 fileDoubleCurleyBracketTitle->setProperty(File::ProtectCasing, Qt::PartiallyChecked); 0860 const QString textDoubleCurleyBracketTitlePartialProtectiveCasing = fileExporterBibTeX.toString(fileDoubleCurleyBracketTitle.data()); 0861 QVERIFY(textDoubleCurleyBracketTitlePartialProtectiveCasing.contains(doubleCurleyBracketTitle)); 0862 0863 fileSingleCurleyBracketTitle->setProperty(File::ProtectCasing, Qt::PartiallyChecked); 0864 const QString textSingleCurleyBracketTitlePartialProtectiveCasing = fileExporterBibTeX.toString(fileSingleCurleyBracketTitle.data()); 0865 QVERIFY(textSingleCurleyBracketTitlePartialProtectiveCasing.contains(singleCurleyBracketTitle) 0866 && !textSingleCurleyBracketTitlePartialProtectiveCasing.contains(doubleCurleyBracketTitle)); 0867 0868 fileDoubleCurleyBracketTitle->setProperty(File::ProtectCasing, Qt::Checked); 0869 const QString textDoubleCurleyBracketTitleWithProtectiveCasing = fileExporterBibTeX.toString(fileDoubleCurleyBracketTitle.data()); 0870 QVERIFY(textDoubleCurleyBracketTitleWithProtectiveCasing.contains(doubleCurleyBracketTitle)); 0871 0872 fileSingleCurleyBracketTitle->setProperty(File::ProtectCasing, Qt::Checked); 0873 const QString textSingleCurleyBracketTitleWithProtectiveCasing = fileExporterBibTeX.toString(fileSingleCurleyBracketTitle.data()); 0874 QVERIFY(textSingleCurleyBracketTitleWithProtectiveCasing.contains(doubleCurleyBracketTitle)); 0875 0876 fileDoubleCurleyBracketTitle->setProperty(File::ProtectCasing, Qt::Unchecked); 0877 const QString textDoubleCurleyBracketTitleWithoutProtectiveCasing = fileExporterBibTeX.toString(fileDoubleCurleyBracketTitle.data()); 0878 QVERIFY(textDoubleCurleyBracketTitleWithoutProtectiveCasing.contains(singleCurleyBracketTitle) 0879 && !textDoubleCurleyBracketTitleWithoutProtectiveCasing.contains(doubleCurleyBracketTitle)); 0880 0881 fileSingleCurleyBracketTitle->setProperty(File::ProtectCasing, Qt::Unchecked); 0882 const QString textSingleCurleyBracketTitleWithoutProtectiveCasing = fileExporterBibTeX.toString(fileSingleCurleyBracketTitle.data()); 0883 QVERIFY(textSingleCurleyBracketTitleWithoutProtectiveCasing.contains(singleCurleyBracketTitle) 0884 && !textSingleCurleyBracketTitleWithoutProtectiveCasing.contains(doubleCurleyBracketTitle)); 0885 } 0886 0887 void KBibTeXIOTest::partialBibTeXInput_data() 0888 { 0889 QTest::addColumn<bool>("isValid"); 0890 QTest::addColumn<QString>("text"); 0891 0892 static const struct BibTeXDataTable { 0893 const char *label; 0894 const bool isValid; 0895 const QString text; 0896 } 0897 bibTeXDataTable[] = { 0898 {"Empty string", false, QString()}, 0899 {"Only 'at' sign", false, QStringLiteral("@")}, 0900 {"Only 'at' sign followed by element type", false, QStringLiteral("@entry")}, 0901 {"Only up to opening curly bracket", false, QStringLiteral("@entry{")}, 0902 {"Complete entry but without id", true, QStringLiteral("@entry{,\n title=\"{Abc Def}\",\n month = jan\n}")}, 0903 {"Entry without any data", true, QStringLiteral("@entry{}")}, 0904 {"Entry up to entry id, but no closing curly bracket", false, QStringLiteral("@entry{test")}, 0905 {"Entry up to entry id with opening curly bracket", false, QStringLiteral("@entry{test{")}, 0906 {"Entry up to entry id with closing curly bracket", true, QStringLiteral("@entry{test}")}, 0907 {"Entry up to comma after entry id", false, QStringLiteral("@entry{test,")}, 0908 {"Entry up to comma after entry id, followed by closing curly bracket", true, QStringLiteral("@entry{test,}")}, 0909 {"Entry up to first field's key, but nothing more, not even an assign char", false, QStringLiteral("@entry{test,title")}, 0910 {"Entry up to first field's key, but nothing more, just a closing curly bracket", false, QStringLiteral("@entry{test,title}")}, 0911 {"Entry up to first field's assign char, but nothing more", false, QStringLiteral("@entry{test,title=")}, 0912 {"Entry up to first field's assign char, but nothing more, just a closing curly bracket", false, QStringLiteral("@entry{test,title=}")}, 0913 {"Invalid combination of curly bracket in a field's value (1)", false, QStringLiteral("@entry{test,title={}")}, 0914 {"Invalid combination of curly bracket in a field's value (2)", false, QStringLiteral("@entry{test,title={{}}")}, 0915 {"Invalid combination of curly bracket in a field's value (3)", false, QStringLiteral("@entry{test,title={}{}")}, 0916 {"Invalid combination of curly bracket in a field's value (4)", false, QStringLiteral("@entry{test,title={}{}}")}, 0917 {"Complete entry with empty title (1)", true, QStringLiteral("@entry{test,\n title=\"{}\"\n}")}, 0918 {"Complete entry with empty title (2)", true, QStringLiteral("@entry{test,\n title=\"\"\n}")}, 0919 {"Complete entry with empty title (3)", true, QStringLiteral("@entry{test,\n title={{}}\n}")}, 0920 {"Complete entry with empty title (4)", true, QStringLiteral("@entry{test,\n title={}\n}")}, 0921 {"Entry abruptly ending at macro key as field value (1)", false, QStringLiteral("@entry{test,\n month = jan")}, 0922 {"Entry abruptly ending at macro key as field value (2)", false, QStringLiteral("@entry{test,\n month = jan\n")}, 0923 // TODO more tests 0924 {"Complete entry", true, QStringLiteral("@entry{test,\n title=\"{Abc Def}\",\n month = jan\n}")} 0925 }; 0926 0927 for (const auto &bibTeXDataRow : bibTeXDataTable) 0928 QTest::newRow(bibTeXDataRow.label) << bibTeXDataRow.isValid << bibTeXDataRow.text; 0929 } 0930 0931 void KBibTeXIOTest::partialBibTeXInput() 0932 { 0933 QFETCH(bool, isValid); 0934 QFETCH(QString, text); 0935 0936 bool gotErrors = false; 0937 FileImporterBibTeX importer(this); 0938 connect(&importer, &FileImporter::message, [&gotErrors](const FileImporter::MessageSeverity messageSeverity, const QString & messageText) { 0939 gotErrors |= messageSeverity >= FileImporter::MessageSeverity::Error; 0940 Q_UNUSED(messageText) 0941 //qCDebug(LOG_KBIBTEX_TEST)<<"FileImporterBibTeX issues message during 'partialBibTeXInput' test: "<<messageText; 0942 }); 0943 QScopedPointer<File> bibTeXfile(importer.fromString(text)); 0944 0945 QVERIFY(text.isEmpty() || isValid != gotErrors); 0946 QVERIFY(isValid ? (!bibTeXfile.isNull() && bibTeXfile->count() == 1) : (bibTeXfile.isNull() || bibTeXfile->count() == 0)); 0947 } 0948 0949 void KBibTeXIOTest::partialRISInput_data() 0950 { 0951 QTest::addColumn<bool>("isValid"); 0952 QTest::addColumn<QString>("text"); 0953 0954 static const struct RISDataTable { 0955 const char *label; 0956 const bool isValid; 0957 const QString text; 0958 } 0959 risDataTable[] = { 0960 //{"Empty string", false, QString()}, 0961 {"Incorrect year", true, QStringLiteral("TY - JOUR\nAU - Shannon, Claude E.\nPY - 5555/07//\nTI - A Mathematical Theory of Communication\nT2 - Bell System Technical Journal\nSP - 379\nEP - 423\nVL - 27\nER -")}, 0962 {"Incorrect month", true, QStringLiteral("TY - JOUR\nAU - Shannon, Claude E.\nPY - 1948/17//\nTI - A Mathematical Theory of Communication\nT2 - Bell System Technical Journal\nSP - 379\nEP - 423\nVL - 27\nER -")}, 0963 {"Entry does not end with 'ER'", true, QStringLiteral("TY - JOUR\nAU - Shannon, Claude E.\nPY - 1948/07//\nTI - A Mathematical Theory of Communication\nT2 - Bell System Technical Journal\nSP - 379\nEP - 423\nVL - 27")}, 0964 // TODO more tests 0965 //{"Complete entry", true, QStringLiteral("TY - JOUR\nAU - Shannon, Claude E.\nPY - 1948/07//\nTI - A Mathematical Theory of Communication\nT2 - Bell System Technical Journal\nSP - 379\nEP - 423\nVL - 27\nER -")} 0966 }; 0967 0968 for (const auto &risDataRow : risDataTable) 0969 QTest::newRow(risDataRow.label) << risDataRow.isValid << risDataRow.text; 0970 } 0971 0972 void KBibTeXIOTest::partialRISInput() 0973 { 0974 QFETCH(bool, isValid); 0975 QFETCH(QString, text); 0976 0977 bool gotErrors = false; 0978 FileImporterRIS importer(this); 0979 connect(&importer, &FileImporter::message, [&gotErrors](const FileImporter::MessageSeverity messageSeverity, const QString & messageText) { 0980 gotErrors |= messageSeverity >= FileImporter::MessageSeverity::Error; 0981 Q_UNUSED(messageText) 0982 //qCDebug(LOG_KBIBTEX_TEST)<<"FileImporterRIS issues message during 'partialBibTeXInput' test: "<<messageText; 0983 }); 0984 QScopedPointer<File> bibTeXfile(importer.fromString(text)); 0985 0986 QVERIFY(text.isEmpty() || isValid != gotErrors); 0987 QVERIFY(isValid ? (!bibTeXfile.isNull() && bibTeXfile->count() == 1) : (bibTeXfile.isNull() || bibTeXfile->count() == 0)); 0988 } 0989 0990 void KBibTeXIOTest::jabRefFieldFile_data() 0991 { 0992 QTest::addColumn<FileExporter *>("exporter"); 0993 QTest::addColumn<FileImporter *>("importer"); 0994 QTest::addColumn<File *>("inputBibliography"); 0995 0996 static const size_t buffersize = 1024; 0997 char buffer[buffersize], bibutilsbuffer[buffersize]; 0998 QVector<QPair<FileExporter *, FileImporter *>> listOfExImporter { 0999 qMakePair(new FileExporterBibTeX(this), new FileImporterBibTeX(this)) 1000 }; 1001 if (BibUtils::available()) { 1002 /// BibUtils does not (yet) support all variations like JabRef (and KBibTeX's own BibTeX exporter/importer) does 1003 static const QVector<BibUtils::Format> bibUtilFormats{/* BibUtils::Format::BibTeX */}; 1004 for (const auto format : bibUtilFormats) { 1005 FileExporterBibUtils *exporter = new FileExporterBibUtils(this); 1006 exporter->setFormat(format); 1007 FileImporterBibUtils *importer = new FileImporterBibUtils(this); 1008 importer->setFormat(format); 1009 listOfExImporter.append(qMakePair(exporter, importer)); 1010 } 1011 } 1012 for (auto &pair : listOfExImporter) { 1013 auto exporter = pair.first; 1014 auto importer = pair.second; 1015 const BibUtils::Format bibutilsformat = strncmp(exporter->metaObject()->className() + 12, "BibUtils", 8) == 0 ? qobject_cast<FileExporterBibUtils *>(exporter)->format() : BibUtils::Format::InvalidFormat; 1016 if (bibutilsformat == BibUtils::Format::InvalidFormat) 1017 bibutilsbuffer[0] = '\0'; 1018 else 1019 snprintf(bibutilsbuffer, buffersize, ", format=%s", bibutilsformat == BibUtils::Format::BibTeX ? "BibTeX" : bibutilsformat == BibUtils::Format::BibLaTeX ? "BibLaTeX" : bibutilsformat == BibUtils::Format::RIS ? "RIS" : bibutilsformat == BibUtils::Format::WordBib ? "WordBib" : "???"); 1020 1021 File *file = new File(); 1022 QSharedPointer<Entry> entry = QSharedPointer<Entry>(new Entry(Entry::etArticle, QStringLiteral("jabRefFieldFile"))); 1023 Value value; 1024 value.append(QSharedPointer<VerbatimText>(new VerbatimText(QStringLiteral("file.pdf")))); 1025 entry->insert(Entry::ftFile, value); 1026 file->append(entry); 1027 int ret = snprintf(buffer, buffersize, "Field 'file' with just a filename (exporter=%s, importer=%s%s)", exporter->metaObject()->className(), importer->metaObject()->className(), bibutilsbuffer); 1028 if (ret > 0) 1029 QTest::newRow(buffer) << exporter << importer << file; 1030 1031 file = new File(); 1032 entry = QSharedPointer<Entry>(new Entry(Entry::etArticle, QStringLiteral("jabRefFieldFile"))); 1033 value.clear(); 1034 VerbatimText *verbatimText = new VerbatimText(QStringLiteral("file.pdf")); 1035 verbatimText->setComment(QStringLiteral("Some PDF file")); 1036 value.append(QSharedPointer<VerbatimText>(verbatimText)); 1037 entry->insert(Entry::ftFile, value); 1038 file->append(entry); 1039 ret = snprintf(buffer, buffersize, "Field 'file' with a JabRef-like value (exporter=%s, importer=%s%s)", exporter->metaObject()->className(), importer->metaObject()->className(), bibutilsbuffer); 1040 if (ret > 0) 1041 QTest::newRow(buffer) << exporter << importer << file; 1042 1043 file = new File(); 1044 entry = QSharedPointer<Entry>(new Entry(Entry::etArticle, QStringLiteral("jabRefFieldFile"))); 1045 value.clear(); 1046 verbatimText = new VerbatimText(QStringLiteral("file.pdf")); 1047 verbatimText->setComment(); 1048 value.append(QSharedPointer<VerbatimText>(verbatimText)); 1049 entry->insert(Entry::ftFile, value); 1050 file->append(entry); 1051 ret = snprintf(buffer, buffersize, "Field 'file' with a JabRef-like value and empty comment (exporter=%s, importer=%s%s)", exporter->metaObject()->className(), importer->metaObject()->className(), bibutilsbuffer); 1052 if (ret > 0) 1053 QTest::newRow(buffer) << exporter << importer << file; 1054 1055 } 1056 } 1057 1058 void KBibTeXIOTest::jabRefFieldFile() 1059 { 1060 QFETCH(FileExporter *, exporter); 1061 QFETCH(FileImporter *, importer); 1062 QFETCH(File *, inputBibliography); 1063 1064 const QString inputAsString = exporter->toString(inputBibliography); 1065 QVERIFY(!inputAsString.isEmpty()); 1066 1067 File *reimportedBibliography = importer->fromString(inputAsString); 1068 QVERIFY(reimportedBibliography != nullptr); 1069 1070 /// Thorough check if both files contain the same elements/entries and if those are identical 1071 QVERIFY(reimportedBibliography->operator ==(*inputBibliography)); 1072 } 1073 1074 void KBibTeXIOTest::initTestCase() 1075 { 1076 qRegisterMetaType<FileImporter::MessageSeverity>(); 1077 } 1078 1079 QTEST_MAIN(KBibTeXIOTest) 1080 1081 #include "kbibtexiotest.moc"