| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com> |
| 4 | ** Copyright (C) 2016 The Qt Company Ltd. |
| 5 | ** Contact: https://www.qt.io/licensing/ |
| 6 | ** |
| 7 | ** This file is part of the test suite of the Qt Toolkit. |
| 8 | ** |
| 9 | ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ |
| 10 | ** Commercial License Usage |
| 11 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 12 | ** accordance with the commercial license agreement provided with the |
| 13 | ** Software or, alternatively, in accordance with the terms contained in |
| 14 | ** a written agreement between you and The Qt Company. For licensing terms |
| 15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 16 | ** information use the contact form at https://www.qt.io/contact-us. |
| 17 | ** |
| 18 | ** GNU General Public License Usage |
| 19 | ** Alternatively, this file may be used under the terms of the GNU |
| 20 | ** General Public License version 3 as published by the Free Software |
| 21 | ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT |
| 22 | ** included in the packaging of this file. Please review the following |
| 23 | ** information to ensure the GNU General Public License requirements will |
| 24 | ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| 25 | ** |
| 26 | ** $QT_END_LICENSE$ |
| 27 | ** |
| 28 | ****************************************************************************/ |
| 29 | |
| 30 | #include <QtTest/QtTest> |
| 31 | #include <QtCore/QString> |
| 32 | #include <QtCore/QCoreApplication> |
| 33 | #include <QtCore/QByteArray> |
| 34 | #include <QtCore/QDir> |
| 35 | #include <QtCore/QFile> |
| 36 | #include <QtCore/QProcess> |
| 37 | #include <QtCore/QDirIterator> |
| 38 | #include <QtCore/QMap> |
| 39 | #include <QtCore/QList> |
| 40 | #include <QtCore/QResource> |
| 41 | #include <QtCore/QLocale> |
| 42 | #include <QtCore/QtGlobal> |
| 43 | |
| 44 | #include <algorithm> |
| 45 | |
| 46 | typedef QMap<QString, QString> QStringMap; |
| 47 | Q_DECLARE_METATYPE(QStringMap) |
| 48 | |
| 49 | static QByteArray msgProcessStartFailed(const QProcess &p) |
| 50 | { |
| 51 | const QString result = QLatin1String("Could not start \"" ) |
| 52 | + QDir::toNativeSeparators(pathName: p.program()) + QLatin1String("\": " ) |
| 53 | + p.errorString(); |
| 54 | return result.toLocal8Bit(); |
| 55 | } |
| 56 | |
| 57 | static QByteArray msgProcessTimeout(const QProcess &p) |
| 58 | { |
| 59 | return '"' + QDir::toNativeSeparators(pathName: p.program()).toLocal8Bit() |
| 60 | + "\" timed out." ; |
| 61 | } |
| 62 | |
| 63 | static QByteArray msgProcessCrashed(QProcess &p) |
| 64 | { |
| 65 | return '"' + QDir::toNativeSeparators(pathName: p.program()).toLocal8Bit() |
| 66 | + "\" crashed.\n" + p.readAllStandardError(); |
| 67 | } |
| 68 | |
| 69 | static QByteArray msgProcessFailed(QProcess &p) |
| 70 | { |
| 71 | return '"' + QDir::toNativeSeparators(pathName: p.program()).toLocal8Bit() |
| 72 | + "\" returned " + QByteArray::number(p.exitCode()) + ":\n" |
| 73 | + p.readAllStandardError(); |
| 74 | } |
| 75 | |
| 76 | class tst_rcc : public QObject |
| 77 | { |
| 78 | Q_OBJECT |
| 79 | |
| 80 | private slots: |
| 81 | void initTestCase(); |
| 82 | |
| 83 | void rcc_data(); |
| 84 | void rcc(); |
| 85 | |
| 86 | void binary_data(); |
| 87 | void binary(); |
| 88 | |
| 89 | void readback_data(); |
| 90 | void readback(); |
| 91 | |
| 92 | void depFileGeneration_data(); |
| 93 | void depFileGeneration(); |
| 94 | |
| 95 | void python(); |
| 96 | |
| 97 | void cleanupTestCase(); |
| 98 | |
| 99 | private: |
| 100 | QString m_rcc; |
| 101 | QString m_dataPath; |
| 102 | }; |
| 103 | |
| 104 | void tst_rcc::initTestCase() |
| 105 | { |
| 106 | // rcc uses a QHash to store files in the resource system. |
| 107 | // we must force a certain hash order when testing or tst_rcc will fail, see QTBUG-25078 |
| 108 | QVERIFY(qputenv("QT_RCC_TEST" , "1" )); |
| 109 | m_rcc = QLibraryInfo::location(QLibraryInfo::BinariesPath) + QLatin1String("/rcc" ); |
| 110 | |
| 111 | m_dataPath = QFINDTESTDATA("data" ); |
| 112 | QVERIFY(!m_dataPath.isEmpty()); |
| 113 | } |
| 114 | |
| 115 | |
| 116 | static inline bool (const QString &line) |
| 117 | { |
| 118 | return line.startsWith(c: QLatin1Char('#')); |
| 119 | } |
| 120 | |
| 121 | static QString doCompare(const QStringList &actual, const QStringList &expected, |
| 122 | const QString &timeStampPath) |
| 123 | { |
| 124 | if (actual.size() != expected.size()) { |
| 125 | return QString("Length count different: actual: %1, expected: %2" ) |
| 126 | .arg(a: actual.size()).arg(a: expected.size()); |
| 127 | } |
| 128 | |
| 129 | QByteArray ba; |
| 130 | const bool isPython = isPythonComment(line: expected.constFirst()); |
| 131 | for (int i = 0, n = expected.size(); i != n; ++i) { |
| 132 | QString expectedLine = expected.at(i); |
| 133 | if (expectedLine.startsWith(s: "IGNORE:" )) |
| 134 | continue; |
| 135 | if (isPython && isPythonComment(line: expectedLine) && isPythonComment(line: actual.at(i))) |
| 136 | continue; |
| 137 | if (expectedLine.startsWith(s: "TIMESTAMP:" )) { |
| 138 | const QString relativePath = expectedLine.mid(position: strlen(s: "TIMESTAMP:" )); |
| 139 | const QFileInfo fi(timeStampPath + QLatin1Char('/') + relativePath); |
| 140 | if (!fi.isFile()) { |
| 141 | ba.append(a: "File " + fi.absoluteFilePath().toUtf8() + " does not exist!" ); |
| 142 | break; |
| 143 | } |
| 144 | const quint64 timeStamp = quint64(fi.lastModified().toMSecsSinceEpoch()); |
| 145 | expectedLine.clear(); |
| 146 | for (int shift = 56; shift >= 0; shift -= 8) { |
| 147 | expectedLine.append(s: QLatin1String("0x" )); |
| 148 | expectedLine.append(s: QString::number(quint8(timeStamp >> shift), base: 16)); |
| 149 | expectedLine.append(c: QLatin1Char(',')); |
| 150 | } |
| 151 | } |
| 152 | if (expectedLine != actual.at(i)) { |
| 153 | qDebug() << "LINES" << (i + 1) << "DIFFER" ; |
| 154 | ba.append( |
| 155 | a: "\n<<<<<< actual\n" + actual.at(i).toUtf8() + "\n======\n" + expectedLine.toUtf8() |
| 156 | + "\n>>>>>> expected\n" |
| 157 | ); |
| 158 | } |
| 159 | } |
| 160 | return ba; |
| 161 | } |
| 162 | |
| 163 | void tst_rcc::rcc_data() |
| 164 | { |
| 165 | QTest::addColumn<QString>(name: "directory" ); |
| 166 | QTest::addColumn<QString>(name: "qrcfile" ); |
| 167 | QTest::addColumn<QString>(name: "expected" ); |
| 168 | |
| 169 | const QString imagesPath = m_dataPath + QLatin1String("/images" ); |
| 170 | QTest::newRow(dataTag: "images" ) << imagesPath << "images.qrc" << "images.expected" ; |
| 171 | |
| 172 | const QString sizesPath = m_dataPath + QLatin1String("/sizes" ); |
| 173 | QTest::newRow(dataTag: "size-0" ) << sizesPath << "size-0.qrc" << "size-0.expected" ; |
| 174 | QTest::newRow(dataTag: "size-1" ) << sizesPath << "size-1.qrc" << "size-1.expected" ; |
| 175 | QTest::newRow(dataTag: "size-2-0-35-1" ) << sizesPath << "size-2-0-35-1.qrc" << "size-2-0-35-1.expected" ; |
| 176 | } |
| 177 | |
| 178 | static QStringList readLinesFromFile(const QString &fileName, |
| 179 | Qt::SplitBehavior splitBehavior) |
| 180 | { |
| 181 | QFile file(fileName); |
| 182 | |
| 183 | bool ok = file.open(flags: QIODevice::ReadOnly | QIODevice::Text); |
| 184 | if (!ok) { |
| 185 | QWARN(qPrintable(QString::fromLatin1("Could not open testdata file %1: %2" ) |
| 186 | .arg(fileName, file.errorString()))); |
| 187 | } |
| 188 | |
| 189 | return QString::fromUtf8(str: file.readAll()).split(sep: QLatin1Char('\n'), behavior: splitBehavior); |
| 190 | } |
| 191 | |
| 192 | void tst_rcc::rcc() |
| 193 | { |
| 194 | QFETCH(QString, directory); |
| 195 | QFETCH(QString, qrcfile); |
| 196 | QFETCH(QString, expected); |
| 197 | |
| 198 | // If the file expectedoutput.txt exists, compare the |
| 199 | // console output with the content of that file |
| 200 | |
| 201 | // Launch; force no compression, otherwise the output would be different |
| 202 | // depending on the compression algorithm we're using |
| 203 | QProcess process; |
| 204 | process.setWorkingDirectory(directory); |
| 205 | process.start(program: m_rcc, arguments: { "-no-compress" , qrcfile }); |
| 206 | QVERIFY2(process.waitForStarted(), msgProcessStartFailed(process).constData()); |
| 207 | if (!process.waitForFinished()) { |
| 208 | process.kill(); |
| 209 | QFAIL(msgProcessTimeout(process).constData()); |
| 210 | } |
| 211 | QVERIFY2(process.exitStatus() == QProcess::NormalExit, |
| 212 | msgProcessCrashed(process).constData()); |
| 213 | QVERIFY2(process.exitCode() == 0, |
| 214 | msgProcessFailed(process).constData()); |
| 215 | |
| 216 | const QChar cr = QLatin1Char('\r'); |
| 217 | const QString err = QString::fromLocal8Bit(str: process.readAllStandardError()).remove(c: cr); |
| 218 | const QString out = QString::fromLatin1(str: process.readAllStandardOutput()).remove(c: cr); |
| 219 | |
| 220 | if (!err.isEmpty()) { |
| 221 | qDebug() << "UNEXPECTED STDERR CONTENTS: " << err; |
| 222 | QFAIL("UNEXPECTED STDERR CONTENTS" ); |
| 223 | } |
| 224 | |
| 225 | const QChar nl = QLatin1Char('\n'); |
| 226 | const QStringList actualLines = out.split(sep: nl); |
| 227 | |
| 228 | const QStringList expectedLines = |
| 229 | readLinesFromFile(fileName: directory + QLatin1Char('/') + expected, splitBehavior: Qt::KeepEmptyParts); |
| 230 | QVERIFY(!expectedLines.isEmpty()); |
| 231 | |
| 232 | const QString diff = doCompare(actual: actualLines, expected: expectedLines, timeStampPath: directory); |
| 233 | if (diff.size()) |
| 234 | QFAIL(qPrintable(diff)); |
| 235 | } |
| 236 | |
| 237 | static QStringMap readExpectedFiles(const QString &fileName) |
| 238 | { |
| 239 | QStringMap expectedFiles; |
| 240 | |
| 241 | QStringList lines = readLinesFromFile(fileName, splitBehavior: Qt::SkipEmptyParts); |
| 242 | foreach (const QString &line, lines) { |
| 243 | QString resourceFileName = line.section(asep: QLatin1Char(' '), astart: 0, aend: 0, aflags: QString::SectionSkipEmpty); |
| 244 | QString actualFileName = line.section(asep: QLatin1Char(' '), astart: 1, aend: 1, aflags: QString::SectionSkipEmpty); |
| 245 | expectedFiles[resourceFileName] = actualFileName; |
| 246 | } |
| 247 | |
| 248 | return expectedFiles; |
| 249 | } |
| 250 | |
| 251 | /* |
| 252 | The following test looks for all *.qrc files under data/binary/. For each |
| 253 | .qrc file found, these files are processed (assuming the file found is |
| 254 | called "base.qrc"): |
| 255 | |
| 256 | - base.qrc : processed by rcc; creates base.rcc |
| 257 | - base.locale : (optional) list of locales to test, one per line |
| 258 | - base.expected : list of pairs (file path in resource, path to real file), |
| 259 | one per line; the pair separated by a whitespace; the paths to real files |
| 260 | relative to data/binary/ (for testing the C locale) |
| 261 | - base.localeName.expected : for each localeName in the base.locale file, |
| 262 | as the above .expected file |
| 263 | */ |
| 264 | |
| 265 | void tst_rcc::binary_data() |
| 266 | { |
| 267 | QTest::addColumn<QString>(name: "resourceFile" ); |
| 268 | QTest::addColumn<QLocale>(name: "locale" ); |
| 269 | QTest::addColumn<QString>(name: "baseDirectory" ); |
| 270 | QTest::addColumn<QStringMap>(name: "expectedFiles" ); |
| 271 | |
| 272 | QString dataPath = m_dataPath + QLatin1String("/binary/" ); |
| 273 | |
| 274 | QDirIterator iter(dataPath, QStringList() << QLatin1String("*.qrc" )); |
| 275 | while (iter.hasNext()) |
| 276 | { |
| 277 | iter.next(); |
| 278 | QFileInfo qrcFileInfo = iter.fileInfo(); |
| 279 | QString absoluteBaseName = QFileInfo(qrcFileInfo.absolutePath(), qrcFileInfo.baseName()).absoluteFilePath(); |
| 280 | QString rccFileName = absoluteBaseName + QLatin1String(".rcc" ); |
| 281 | |
| 282 | // same as above: force no compression |
| 283 | QProcess rccProcess; |
| 284 | rccProcess.setWorkingDirectory(dataPath); |
| 285 | rccProcess.start(program: m_rcc, arguments: { "-binary" , "-no-compress" , "-o" , rccFileName, qrcFileInfo.absoluteFilePath() }); |
| 286 | QVERIFY2(rccProcess.waitForStarted(), msgProcessStartFailed(rccProcess).constData()); |
| 287 | if (!rccProcess.waitForFinished()) { |
| 288 | rccProcess.kill(); |
| 289 | QFAIL(msgProcessTimeout(rccProcess).constData()); |
| 290 | } |
| 291 | QVERIFY2(rccProcess.exitStatus() == QProcess::NormalExit, |
| 292 | msgProcessCrashed(rccProcess).constData()); |
| 293 | QVERIFY2(rccProcess.exitCode() == 0, |
| 294 | msgProcessFailed(rccProcess).constData()); |
| 295 | |
| 296 | QByteArray output = rccProcess.readAllStandardOutput(); |
| 297 | if (!output.isEmpty()) |
| 298 | qWarning(msg: "rcc stdout: %s" , output.constData()); |
| 299 | |
| 300 | output = rccProcess.readAllStandardError(); |
| 301 | if (!output.isEmpty()) |
| 302 | qWarning(msg: "rcc stderr: %s" , output.constData()); |
| 303 | |
| 304 | QString localeFileName = absoluteBaseName + QLatin1String(".locale" ); |
| 305 | QFile localeFile(localeFileName); |
| 306 | if (localeFile.exists()) { |
| 307 | QStringList locales = readLinesFromFile(fileName: localeFileName, splitBehavior: Qt::SkipEmptyParts); |
| 308 | foreach (const QString &locale, locales) { |
| 309 | QString expectedFileName = QString::fromLatin1(str: "%1.%2.%3" ).arg(args&: absoluteBaseName, args: locale, args: QLatin1String("expected" )); |
| 310 | QStringMap expectedFiles = readExpectedFiles(fileName: expectedFileName); |
| 311 | QTest::newRow(qPrintable(qrcFileInfo.baseName() + QLatin1Char('_') + locale)) << rccFileName |
| 312 | << QLocale(locale) |
| 313 | << dataPath |
| 314 | << expectedFiles; |
| 315 | } |
| 316 | } |
| 317 | |
| 318 | // always test for the C locale as well |
| 319 | QString expectedFileName = absoluteBaseName + QLatin1String(".expected" ); |
| 320 | QStringMap expectedFiles = readExpectedFiles(fileName: expectedFileName); |
| 321 | QTest::newRow(qPrintable(qrcFileInfo.baseName() + QLatin1String("_C" ))) << rccFileName |
| 322 | << QLocale::c() |
| 323 | << dataPath |
| 324 | << expectedFiles; |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | void tst_rcc::binary() |
| 329 | { |
| 330 | QFETCH(QString, baseDirectory); |
| 331 | QFETCH(QString, resourceFile); |
| 332 | QFETCH(QLocale, locale); |
| 333 | QFETCH(QStringMap, expectedFiles); |
| 334 | |
| 335 | const QString rootPrefix = QLatin1String("/test_root/" ); |
| 336 | const QString resourceRootPrefix = QLatin1Char(':') + rootPrefix; |
| 337 | |
| 338 | QLocale oldDefaultLocale; |
| 339 | QLocale::setDefault(locale); |
| 340 | QVERIFY(QFile::exists(resourceFile)); |
| 341 | QVERIFY(QResource::registerResource(resourceFile, rootPrefix)); |
| 342 | |
| 343 | { // need to destroy the iterators on the resource, in order to be able to unregister it |
| 344 | |
| 345 | // read all the files inside the resources |
| 346 | QDirIterator iter(resourceRootPrefix, QDir::Files, QDirIterator::Subdirectories); |
| 347 | QList<QString> filesFound; |
| 348 | while (iter.hasNext()) |
| 349 | filesFound << iter.next(); |
| 350 | |
| 351 | // add the test root prefix to the expected file names |
| 352 | QList<QString> expectedFileNames = expectedFiles.keys(); |
| 353 | for (QList<QString>::iterator i = expectedFileNames.begin(); i < expectedFileNames.end(); ++i) { |
| 354 | // poor man's canonicalPath, which doesn't work with resources |
| 355 | if ((*i).startsWith(c: QLatin1Char('/'))) |
| 356 | (*i).remove(i: 0, len: 1); |
| 357 | *i = resourceRootPrefix + *i; |
| 358 | } |
| 359 | |
| 360 | // check that we have all (and only) the expected files |
| 361 | std::sort(first: filesFound.begin(), last: filesFound.end()); |
| 362 | std::sort(first: expectedFileNames.begin(), last: expectedFileNames.end()); |
| 363 | QCOMPARE(filesFound, expectedFileNames); |
| 364 | |
| 365 | // now actually check the file contents |
| 366 | QDir directory(baseDirectory); |
| 367 | for (QStringMap::const_iterator i = expectedFiles.constBegin(); i != expectedFiles.constEnd(); ++i) { |
| 368 | QString resourceFileName = i.key(); |
| 369 | QString actualFileName = i.value(); |
| 370 | |
| 371 | QFile resourceFile(resourceRootPrefix + resourceFileName); |
| 372 | QVERIFY(resourceFile.open(QIODevice::ReadOnly)); |
| 373 | QByteArray resourceData = resourceFile.readAll(); |
| 374 | resourceFile.close(); |
| 375 | |
| 376 | QFile actualFile(QFileInfo(directory, actualFileName).absoluteFilePath()); |
| 377 | QVERIFY(actualFile.open(QIODevice::ReadOnly)); |
| 378 | QByteArray actualData = actualFile.readAll(); |
| 379 | actualFile.close(); |
| 380 | QCOMPARE(resourceData, actualData); |
| 381 | } |
| 382 | |
| 383 | } |
| 384 | |
| 385 | QVERIFY(QResource::unregisterResource(resourceFile, rootPrefix)); |
| 386 | QLocale::setDefault(oldDefaultLocale); |
| 387 | } |
| 388 | |
| 389 | void tst_rcc::readback_data() |
| 390 | { |
| 391 | QTest::addColumn<QString>(name: "resourceName" ); |
| 392 | QTest::addColumn<QString>(name: "fileSystemName" ); |
| 393 | |
| 394 | QTest::newRow(dataTag: "data-0" ) << ":data/data-0.txt" << "sizes/data/data-0.txt" ; |
| 395 | QTest::newRow(dataTag: "data-1" ) << ":data/data-1.txt" << "sizes/data/data-1.txt" ; |
| 396 | QTest::newRow(dataTag: "data-2" ) << ":data/data-2.txt" << "sizes/data/data-2.txt" ; |
| 397 | QTest::newRow(dataTag: "data-35" ) << ":data/data-35.txt" << "sizes/data/data-35.txt" ; |
| 398 | QTest::newRow(dataTag: "circle" ) << ":images/circle.png" << "images/images/circle.png" ; |
| 399 | QTest::newRow(dataTag: "square" ) << ":images/square.png" << "images/images/square.png" ; |
| 400 | QTest::newRow(dataTag: "triangle" ) << ":images/subdir/triangle.png" |
| 401 | << "images/images/subdir/triangle.png" ; |
| 402 | } |
| 403 | |
| 404 | void tst_rcc::readback() |
| 405 | { |
| 406 | QFETCH(QString, resourceName); |
| 407 | QFETCH(QString, fileSystemName); |
| 408 | |
| 409 | QFile resourceFile(resourceName); |
| 410 | QVERIFY(resourceFile.open(QIODevice::ReadOnly)); |
| 411 | QByteArray resourceData = resourceFile.readAll(); |
| 412 | resourceFile.close(); |
| 413 | |
| 414 | QFile fileSystemFile(m_dataPath + QLatin1Char('/') + fileSystemName); |
| 415 | QVERIFY(fileSystemFile.open(QIODevice::ReadOnly)); |
| 416 | QByteArray fileSystemData = fileSystemFile.readAll(); |
| 417 | fileSystemFile.close(); |
| 418 | |
| 419 | QCOMPARE(resourceData, fileSystemData); |
| 420 | } |
| 421 | |
| 422 | void tst_rcc::depFileGeneration_data() |
| 423 | { |
| 424 | QTest::addColumn<QString>(name: "qrcfile" ); |
| 425 | QTest::addColumn<QString>(name: "depfile" ); |
| 426 | QTest::addColumn<QString>(name: "expected" ); |
| 427 | |
| 428 | QTest::newRow(dataTag: "simple" ) << "simple.qrc" << "simple.d" << "simple.d.expected" ; |
| 429 | QTest::newRow(dataTag: "specialchar" ) << "specialchar.qrc" << "specialchar.d" << "specialchar.d.expected" ; |
| 430 | } |
| 431 | |
| 432 | void tst_rcc::depFileGeneration() |
| 433 | { |
| 434 | QFETCH(QString, qrcfile); |
| 435 | QFETCH(QString, depfile); |
| 436 | QFETCH(QString, expected); |
| 437 | const QString directory = m_dataPath + QLatin1String("/depfile" ); |
| 438 | |
| 439 | QProcess process; |
| 440 | process.setWorkingDirectory(directory); |
| 441 | process.start(program: m_rcc, arguments: { "-d" , depfile, "-o" , qrcfile + ".cpp" , qrcfile }); |
| 442 | QVERIFY2(process.waitForStarted(), msgProcessStartFailed(process).constData()); |
| 443 | if (!process.waitForFinished()) { |
| 444 | process.kill(); |
| 445 | QFAIL(msgProcessTimeout(process).constData()); |
| 446 | } |
| 447 | QVERIFY2(process.exitStatus() == QProcess::NormalExit, |
| 448 | msgProcessCrashed(process).constData()); |
| 449 | QVERIFY2(process.exitCode() == 0, |
| 450 | msgProcessFailed(process).constData()); |
| 451 | |
| 452 | QFile depFileOutput(directory + QLatin1String("/" ) + depfile); |
| 453 | QVERIFY(depFileOutput.open(QIODevice::ReadOnly | QIODevice::Text)); |
| 454 | QByteArray depFileData = depFileOutput.readAll(); |
| 455 | depFileOutput.close(); |
| 456 | |
| 457 | QFile depFileExpected(directory + QLatin1String("/" ) + expected); |
| 458 | QVERIFY(depFileExpected.open(QIODevice::ReadOnly | QIODevice::Text)); |
| 459 | QByteArray expectedData = depFileExpected.readAll(); |
| 460 | depFileExpected.close(); |
| 461 | |
| 462 | QCOMPARE(depFileData, expectedData); |
| 463 | } |
| 464 | |
| 465 | void tst_rcc::python() |
| 466 | { |
| 467 | const QString path = m_dataPath + QLatin1String("/sizes" ); |
| 468 | const QString testFileRoot = path + QLatin1String("/size-2-0-35-1" ); |
| 469 | const QString qrcFile = testFileRoot + QLatin1String(".qrc" ); |
| 470 | const QString expectedFile = testFileRoot + QLatin1String("_python.expected" ); |
| 471 | const QString actualFile = testFileRoot + QLatin1String(".rcc" ); |
| 472 | |
| 473 | QProcess process; |
| 474 | process.setWorkingDirectory(path); |
| 475 | process.start(program: m_rcc, arguments: { "-g" , "python" , "-o" , actualFile, qrcFile}); |
| 476 | QVERIFY2(process.waitForStarted(), msgProcessStartFailed(process).constData()); |
| 477 | if (!process.waitForFinished()) { |
| 478 | process.kill(); |
| 479 | QFAIL(msgProcessTimeout(process).constData()); |
| 480 | } |
| 481 | QVERIFY2(process.exitStatus() == QProcess::NormalExit, |
| 482 | msgProcessCrashed(process).constData()); |
| 483 | QVERIFY2(process.exitCode() == 0, |
| 484 | msgProcessFailed(process).constData()); |
| 485 | |
| 486 | const auto actualLines = readLinesFromFile(fileName: actualFile, splitBehavior: Qt::KeepEmptyParts); |
| 487 | QVERIFY(!actualLines.isEmpty()); |
| 488 | const auto expectedLines = readLinesFromFile(fileName: expectedFile, splitBehavior: Qt::KeepEmptyParts); |
| 489 | QVERIFY(!expectedLines.isEmpty()); |
| 490 | const QString diff = doCompare(actual: actualLines, expected: expectedLines, timeStampPath: path); |
| 491 | if (!diff.isEmpty()) |
| 492 | QFAIL(qPrintable(diff)); |
| 493 | } |
| 494 | |
| 495 | void tst_rcc::cleanupTestCase() |
| 496 | { |
| 497 | QDir dataDir(m_dataPath + QLatin1String("/binary" )); |
| 498 | QFileInfoList entries = dataDir.entryInfoList(nameFilters: QStringList() << QLatin1String("*.rcc" )); |
| 499 | QDir dataDepDir(m_dataPath + QLatin1String("/depfile" )); |
| 500 | entries += dataDepDir.entryInfoList(nameFilters: {QLatin1String("*.d" ), QLatin1String("*.qrc.cpp" )}); |
| 501 | foreach (const QFileInfo &entry, entries) |
| 502 | QFile::remove(fileName: entry.absoluteFilePath()); |
| 503 | } |
| 504 | |
| 505 | QTEST_MAIN(tst_rcc) |
| 506 | |
| 507 | #include "tst_rcc.moc" |
| 508 | |