File indexing completed on 2024-04-28 15:25:14
0001 /** 0002 * This file is part of the KDE project 0003 * 0004 * Copyright (C) 2006 Nikolas Zimmermann <zimmermann@kde.org> 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Library General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library 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 GNU 0014 * Library General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Library General Public License 0017 * along with this library; see the file COPYING.LIB. If not, write to 0018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0019 * Boston, MA 02110-1301, USA. 0020 * 0021 */ 0022 0023 #include <assert.h> 0024 #include <signal.h> 0025 0026 #include <QFile> 0027 #include <QTimer> 0028 #include <QFileInfo> 0029 #include <QTextStream> 0030 #include <QFileDialog> 0031 #include <QMainWindow> 0032 0033 #include <kiconloader.h> 0034 #include <kmessagebox.h> 0035 #include <kconfig.h> 0036 0037 // Taken from QUrl 0038 #define Q_HAS_FLAG(a, b) ( ((a) & (b)) == (b) ) 0039 #define Q_SET_FLAG(a, b) { (a) |= (b); } 0040 #define Q_UNSET_FLAG(a, b) { (a) &= ~(b); } 0041 0042 TestRegressionWindow::TestRegressionWindow(QWidget *parent) 0043 : QMainWindow(parent), m_flags(None), m_runCounter(0), m_testCounter(0), m_totalTests(0), 0044 m_totalTestsJS(0), m_totalTestsDOMTS(0), m_lastResult(Unknown), 0045 m_browserPart(0), m_activeProcess(0), m_activeTreeItem(0), 0046 m_suspended(false), m_justProcessingQueue(false) 0047 { 0048 m_ui.setupUi(this); 0049 0050 // Setup actions/connections 0051 connect(m_ui.actionOnly_run_JS_tests, SIGNAL(toggled(bool)), SLOT(toggleJSTests(bool))); 0052 connect(m_ui.actionOnly_run_HTML_tests, SIGNAL(toggled(bool)), SLOT(toggleHTMLTests(bool))); 0053 connect(m_ui.actionDo_not_suppress_debug_output, SIGNAL(toggled(bool)), SLOT(toggleDebugOutput(bool))); 0054 connect(m_ui.actionDo_not_use_Xvfb, SIGNAL(toggled(bool)), SLOT(toggleNoXvfbUse(bool))); 0055 connect(m_ui.actionSpecify_tests_directory, SIGNAL(triggered(bool)), SLOT(setTestsDirectory())); 0056 connect(m_ui.actionSpecify_khtml_directory, SIGNAL(triggered(bool)), SLOT(setKHTMLDirectory())); 0057 connect(m_ui.actionSpecify_output_directory, SIGNAL(triggered(bool)), SLOT(setOutputDirectory())); 0058 connect(m_ui.actionRun_tests, SIGNAL(triggered(bool)), SLOT(runTests())); 0059 0060 connect(m_ui.pauseContinueButton, SIGNAL(clicked(bool)), SLOT(pauseContinueButtonClicked())); 0061 connect(m_ui.saveLogButton, SIGNAL(clicked(bool)), SLOT(saveLogButtonClicked())); 0062 0063 connect(m_ui.treeWidget, SIGNAL(customContextMenuRequested(QPoint)), 0064 this, SLOT(treeWidgetContextMenuRequested(QPoint))); 0065 0066 // Setup actions' default state 0067 m_ui.progressBar->setValue(0); 0068 m_ui.textEdit->setReadOnly(true); 0069 m_ui.actionRun_tests->setEnabled(false); 0070 m_ui.pauseContinueButton->setEnabled(false); 0071 0072 m_ui.treeWidget->headerItem()->setTextAlignment(0, Qt::AlignLeft); 0073 m_ui.treeWidget->headerItem()->setText(0, i18n("Available Tests: 0")); 0074 0075 // Load default values for tests directory/khtml directory... 0076 KConfig config("testregressiongui", KConfig::SimpleConfig); 0077 KConfigGroup grp = config.group("<default>"); 0078 0079 m_testsUrl = QUrl::fromLocalFile(grp.readPathEntry("TestsDirectory", QString())); 0080 m_khtmlUrl = QUrl::fromLocalFile(grp.readPathEntry("KHTMLDirectory", QString())); 0081 0082 initTestsDirectory(); 0083 0084 // Init early visible items in the text edit... 0085 initLegend(); 0086 initOutputBrowser(); 0087 } 0088 0089 TestRegressionWindow::~TestRegressionWindow() 0090 { 0091 if (m_activeProcess) { 0092 m_activeProcess->kill(); 0093 0094 /* This leads to: 0095 * QProcess object destroyed while process is still running. 0096 * Any idea why?? 0097 delete m_activeProcess; 0098 */ 0099 0100 m_activeProcess = 0; 0101 } 0102 } 0103 0104 void TestRegressionWindow::toggleJSTests(bool checked) 0105 { 0106 if (checked) { 0107 Q_SET_FLAG(m_flags, JSTests) 0108 Q_UNSET_FLAG(m_flags, HTMLTests) 0109 0110 m_ui.actionOnly_run_HTML_tests->setChecked(false); 0111 } else 0112 Q_UNSET_FLAG(m_flags, JSTests) 0113 0114 // Eventually update progress bar range... 0115 updateProgressBarRange(); 0116 } 0117 0118 void TestRegressionWindow::toggleHTMLTests(bool checked) 0119 { 0120 if (checked) { 0121 Q_SET_FLAG(m_flags, HTMLTests) 0122 Q_UNSET_FLAG(m_flags, JSTests) 0123 0124 m_ui.actionOnly_run_JS_tests->setChecked(false); 0125 } else 0126 Q_UNSET_FLAG(m_flags, HTMLTests) 0127 0128 // Eventually update progress bar range... 0129 updateProgressBarRange(); 0130 } 0131 0132 void TestRegressionWindow::toggleDebugOutput(bool checked) 0133 { 0134 if (checked) 0135 Q_SET_FLAG(m_flags, DebugOutput) 0136 else 0137 Q_UNSET_FLAG(m_flags, DebugOutput) 0138 } 0139 0140 void TestRegressionWindow::toggleNoXvfbUse(bool checked) 0141 { 0142 if (checked) 0143 Q_SET_FLAG(m_flags, NoXvfbUse) 0144 else 0145 Q_UNSET_FLAG(m_flags, NoXvfbUse) 0146 } 0147 0148 void TestRegressionWindow::setTestsDirectory() 0149 { 0150 m_testsUrl = QFileDialog::getExistingDirectory(); 0151 0152 initTestsDirectory(); 0153 loadOutputHTML(); 0154 } 0155 0156 void TestRegressionWindow::setOutputDirectory() 0157 { 0158 m_outputUrl = QFileDialog::getExistingDirectory(); 0159 loadOutputHTML(); 0160 } 0161 0162 void TestRegressionWindow::initTestsDirectory() 0163 { 0164 bool okay = !m_testsUrl.isEmpty(); 0165 if (okay) { 0166 const char *subdirs[] = { "tests", "baseline", "output", "resources" }; 0167 for (int i = 0; i <= 3; i++) { 0168 QFileInfo sourceDir(m_testsUrl.path() + "/" + subdirs[i]); //krazy:exclude=duoblequote_chars DOM demands chars 0169 if (!sourceDir.exists() || !sourceDir.isDir()) { 0170 KMessageBox::error(0, i18n("Please choose a valid 'khtmltests/regression/' directory.")); 0171 0172 okay = false; 0173 m_testsUrl = QUrl(); 0174 break; 0175 } 0176 } 0177 } 0178 0179 if (okay) { 0180 // Clean up... 0181 m_itemMap.clear(); 0182 m_ignoreMap.clear(); 0183 m_failureMap.clear(); 0184 m_directoryMap.clear(); 0185 0186 m_ui.treeWidget->clear(); 0187 0188 if (!m_khtmlUrl.isEmpty()) { 0189 m_ui.actionRun_tests->setEnabled(true); 0190 } 0191 0192 // Initialize map (to prevent assert below)... 0193 m_directoryMap.insert(QString(), QStringList()); 0194 0195 // Setup root tree widget item... 0196 (void) new QTreeWidgetItem(m_ui.treeWidget, QStringList(m_testsUrl.path() + "/tests")); 0197 0198 // Check for ignore & failure file in root directory... 0199 QString ignoreFile = m_testsUrl.path() + "/tests/ignore"; 0200 QString failureFile = m_testsUrl.path() + "/tests/KNOWN_FAILURES"; 0201 0202 QStringList ignoreFileList = readListFile(ignoreFile); 0203 QStringList failureFileList = readListFile(failureFile); 0204 0205 if (!ignoreFileList.isEmpty()) { 0206 m_ignoreMap.insert(QString(), ignoreFileList); 0207 } 0208 0209 if (!failureFileList.isEmpty()) { 0210 m_failureMap.insert(QString(), failureFileList); 0211 } 0212 0213 // Remember directory... 0214 KConfig config("testregressiongui", KConfig::SimpleConfig); 0215 KConfigGroup grp = config.group("<default>"); 0216 grp.writePathEntry("TestsDirectory", m_testsUrl.path()); 0217 0218 // Start listing directory... 0219 QUrl listUrl(m_testsUrl); 0220 listUrl.setPath(listUrl.path() + "/tests"); 0221 KIO::ListJob *job = KIO::listRecursive(listUrl, KIO::HideProgressInfo, false /* no hidden files */); 0222 0223 connect(job, SIGNAL(result(KJob*)), SLOT(directoryListingFinished(KJob*))); 0224 0225 connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), 0226 this, SLOT(directoryListingResult(KIO::Job*,KIO::UDSEntryList))); 0227 } 0228 } 0229 0230 void TestRegressionWindow::setKHTMLDirectory() 0231 { 0232 m_khtmlUrl = QFileDialog::getExistingDirectory(); 0233 0234 if (!m_khtmlUrl.isEmpty()) { 0235 const char *subdirs[] = { "css", "dom", "xml", "html" }; // That's enough ;-) 0236 for (int i = 0; i <= 3; i++) { 0237 QFileInfo sourceDir(m_khtmlUrl.path() + "/" + subdirs[i]); //krazy:exclude=duoblequote_chars DOM demands chars 0238 if (!sourceDir.exists() || !sourceDir.isDir()) { 0239 KMessageBox::error(0, i18n("Please choose a valid 'khtml/' build directory.")); 0240 0241 m_khtmlUrl = QUrl(); 0242 break; 0243 } 0244 } 0245 0246 // Remember directory... 0247 KConfig config("testregressiongui", KConfig::SimpleConfig); 0248 KConfigGroup grp = config.group("<default>"); 0249 grp.writePathEntry("KHTMLDirectory", m_khtmlUrl.path()); 0250 0251 if (!m_testsUrl.isEmpty() && !m_khtmlUrl.isEmpty()) { 0252 m_ui.actionRun_tests->setEnabled(true); 0253 } 0254 } 0255 } 0256 0257 void TestRegressionWindow::directoryListingResult(KIO::Job *, const KIO::UDSEntryList &list) 0258 { 0259 KIO::UDSEntryList::ConstIterator it = list.constBegin(); 0260 const KIO::UDSEntryList::ConstIterator end = list.constEnd(); 0261 0262 for (; it != end; ++it) { 0263 const KIO::UDSEntry &entry = *it; 0264 0265 QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); 0266 if (entry.isDir()) { // Create new map entry... 0267 assert(m_directoryMap.constFind(name) == m_directoryMap.constEnd()); 0268 m_directoryMap.insert(name, QStringList()); 0269 0270 QString ignoreFile = m_testsUrl.path() + "/tests/" + name + "/ignore"; 0271 QString failureFile = m_testsUrl.path() + "/tests/" + name + "/KNOWN_FAILURES"; 0272 0273 QStringList ignoreFileList = readListFile(ignoreFile); 0274 QStringList failureFileList = readListFile(failureFile); 0275 0276 if (!ignoreFileList.isEmpty()) { 0277 m_ignoreMap.insert(name, ignoreFileList); 0278 } 0279 0280 if (!failureFileList.isEmpty()) { 0281 m_failureMap.insert(name, failureFileList); 0282 } 0283 } else if (name.endsWith(".html") || name.endsWith(".htm") || 0284 name.endsWith(".xhtml") || name.endsWith(".xml") || name.endsWith(".js")) { 0285 int lastSlashPos = name.lastIndexOf('/'); 0286 0287 QString cachedDirectory = (lastSlashPos > 0 ? name.mid(0, lastSlashPos) : QString()); 0288 QString cachedFilename = name.mid(lastSlashPos + 1); 0289 0290 assert(m_directoryMap.constFind(cachedDirectory) != m_directoryMap.constEnd()); 0291 m_directoryMap[cachedDirectory].append(cachedFilename); 0292 } 0293 } 0294 } 0295 0296 void TestRegressionWindow::directoryListingFinished(KJob *) 0297 { 0298 QTreeWidgetItem *topLevelItem = m_ui.treeWidget->topLevelItem(0); 0299 0300 // Gather a lot of statistics... 0301 unsigned long availableDomFiles = 0; 0302 unsigned long availableDumpFiles = 0; 0303 unsigned long availableRenderFiles = 0; 0304 0305 unsigned long ignoredJSTests = 0; 0306 unsigned long availableJSTests = 0; 0307 0308 unsigned long ignoredXMLTests = 0; 0309 unsigned long availableXMLTests = 0; 0310 0311 unsigned long ignoredHTMLTests = 0; 0312 unsigned long availableHTMLTests = 0; 0313 0314 unsigned long ignoredDOMTSTests = 0; 0315 unsigned long availableDOMTSTests = 0; 0316 0317 // Start the actual data processing... 0318 QMap<QString, QStringList>::const_iterator it = m_directoryMap.constBegin(); 0319 const QMap<QString, QStringList>::const_iterator end = m_directoryMap.constEnd(); 0320 0321 for (; it != end; ++it) { 0322 QString directory = it.key(); 0323 QStringList filenames = it.value(); 0324 0325 if (filenames.isEmpty()) { // Do not add empty directories at all... 0326 continue; 0327 } 0328 0329 bool hasIgnores = (m_ignoreMap.constFind(directory) != m_directoryMap.constEnd()); 0330 bool hasFailures = (m_failureMap.constFind(directory) != m_failureMap.constEnd()); 0331 0332 // Extract parent directory... 0333 int position = directory.lastIndexOf('/'); 0334 0335 QString parentDirectory = directory.mid(0, (position == -1 ? 0 : position)); 0336 QString parentDirectoryItem = directory.mid(position + 1); 0337 0338 bool hasParentIgnores = (m_ignoreMap.constFind(parentDirectory) != m_directoryMap.constEnd()); 0339 bool hasParentFailures = (m_failureMap.constFind(parentDirectory) != m_failureMap.constEnd()); 0340 0341 // Sort in ascending order... 0342 filenames.sort(); 0343 0344 QStringList::const_iterator it2 = filenames.constBegin(); 0345 const QStringList::const_iterator end2 = filenames.constEnd(); 0346 0347 // Create new tree widget item for the active directory... 0348 QTreeWidgetItem *parent = topLevelItem; 0349 0350 if (!directory.isEmpty()) { 0351 parent = new QTreeWidgetItem(topLevelItem, QStringList(directory)); 0352 0353 // Directory is completely ignored, mark it 'yellow'... 0354 if (hasParentIgnores && m_ignoreMap[parentDirectory].contains(parentDirectoryItem)) { 0355 parent->setIcon(0, m_ignorePixmap); 0356 } 0357 0358 // Directory is completely known to fail, mark it 'red'... 0359 if (hasParentFailures && m_failureMap[parentDirectory].contains(parentDirectoryItem)) { 0360 parent->setIcon(0, m_failKnownPixmap); 0361 } 0362 } 0363 0364 // Add all contained files as new items below 'parent'... 0365 for (; it2 != end2; ++it2) { 0366 QString test = (*it2); 0367 QString cacheName = directory + "/" + test; //krazy:exclude=duoblequote_chars DOM demands chars 0368 0369 QTreeWidgetItem *testItem = new QTreeWidgetItem(parent, QStringList(QUrl(test).path())); 0370 0371 // Remember name <-> item pair... 0372 assert(m_itemMap.contains(cacheName)); 0373 m_itemMap.insert(cacheName, testItem); 0374 0375 bool ignore = (hasIgnores && m_ignoreMap[directory].contains(test)); 0376 bool ignoreParent = (hasParentIgnores && m_ignoreMap[parentDirectory].contains(parentDirectoryItem)); 0377 0378 bool failure = (hasFailures && m_failureMap[directory].contains(test)); 0379 0380 // Check baseline directory for this test... 0381 QString baseLinePath = m_testsUrl.path() + "/baseline/" + cacheName; 0382 0383 bool dom[9], render[9]; 0384 for (unsigned int i = 0; i < 9; ++i) { 0385 if (i == 0) { 0386 dom[i] = (QFileInfo(baseLinePath + "-dom").exists()); 0387 render[i] = (QFileInfo(baseLinePath + "-render").exists()); 0388 } else { 0389 dom[i] = (QFileInfo(baseLinePath + "-" + QString::number(i) + "-dom").exists()); //krazy:exclude=duoblequote_chars DOM demands chars 0390 render[i] = (QFileInfo(baseLinePath + "-" + QString::number(i) + "-render").exists()); //krazy:exclude=duoblequote_chars DOM demands chars 0391 } 0392 } 0393 0394 bool dump = (QFileInfo(baseLinePath + "-dump.png").exists()); 0395 0396 // Ignored tests are marked 'yellow'... 0397 if (ignore) { 0398 testItem->setIcon(0, m_ignorePixmap); 0399 } 0400 0401 // Tests, known to fail, are marked 'red'... 0402 if (failure) { 0403 testItem->setIcon(0, m_failKnownPixmap); 0404 } 0405 0406 // Detect whether the tests has no corresponding baseline items... 0407 if (!ignore && !failure) { 0408 if (!dom[0] && !dump && !render && !cacheName.endsWith(".js") && !cacheName.startsWith("domts")) { 0409 // See if parent directory is completely ignored... 0410 if (!ignoreParent) { 0411 testItem->setIcon(0, m_noBaselinePixmap); 0412 } 0413 } 0414 } 0415 0416 // Update statistics... 0417 if (dump) { 0418 availableDumpFiles++; 0419 } 0420 0421 for (unsigned i = 0; i < 9; ++i) { 0422 if (dom[i]) { 0423 availableDomFiles++; 0424 } 0425 0426 if (render[i]) { 0427 availableRenderFiles++; 0428 } 0429 } 0430 0431 // Count DOM Testsuite files separated... (these have no baseline items!) 0432 if (cacheName.startsWith("domts")) { 0433 // See if parent directory is completely ignored... 0434 if (ignore || ignoreParent) { 0435 ignoredDOMTSTests++; 0436 } else { 0437 availableDOMTSTests++; 0438 } 0439 } 0440 0441 if (cacheName.endsWith(".html") || cacheName.endsWith(".htm") || cacheName.endsWith(".xhtml")) { 0442 if (ignore || ignoreParent) { 0443 ignoredHTMLTests++; 0444 } else { 0445 availableHTMLTests++; 0446 } 0447 } else if (cacheName.endsWith(".xml")) { 0448 if (ignore || ignoreParent) { 0449 ignoredXMLTests++; 0450 } else { 0451 availableXMLTests++; 0452 } 0453 } else if (cacheName.endsWith(".js")) { 0454 unsigned long containedTests = 0; 0455 0456 // Try hard to _ESTIMATE_ the number of tests... 0457 // I really meant estimate, no way to calculate it perfectly. 0458 0459 QString jsFilePath = m_testsUrl.path() + "/tests/" + cacheName; 0460 assert(QFileInfo(jsFilePath).exists() == true); 0461 0462 QStringList fileList = readListFile(jsFilePath); 0463 QString fileContent = fileList.join(""); 0464 0465 // #1 -> Check js file for the 'reportResult' calls... 0466 containedTests = fileContent.count("reportResult"); 0467 0468 // #2 -> Check js file for 'openPage' calls... 0469 containedTests += fileContent.count("openPage"); 0470 0471 // #3 -> Check js file for 'checkOutput' calls... 0472 containedTests += fileContent.count("checkOutput"); 0473 0474 // #4 -> Fallback for ie. mozilla/ecma files... 0475 if (containedTests == 0) { // Doesn't use 'reportResult' scheme... 0476 containedTests++; 0477 } 0478 0479 if (ignore || ignoreParent) { 0480 ignoredJSTests += containedTests; 0481 } else { 0482 availableJSTests += containedTests; 0483 } 0484 } 0485 } 0486 } 0487 0488 // Now we can calculate all ignored/available tests... 0489 unsigned long ignoredTests = ignoredJSTests + ignoredXMLTests + ignoredHTMLTests; 0490 unsigned long availableTests = availableJSTests + availableXMLTests + availableHTMLTests; 0491 0492 // This estimates the number of total tests, depending on the mode... 0493 m_totalTests = availableDomFiles + availableDumpFiles + availableRenderFiles + 0494 availableDOMTSTests + availableJSTests; 0495 0496 m_totalTestsJS = availableJSTests; 0497 m_totalTestsDOMTS = availableDOMTSTests; 0498 0499 // Update progress bar range... 0500 updateProgressBarRange(); 0501 0502 QString statistics = QString("<body><table border='0' align='center' cellspacing='15'>") + 0503 QString("<tr valign='top'><td colspan='3'><center><b>Statistics</b></center></td></tr>") + 0504 QString("<tr valign='middle'><td>JS Tests</td><td>" + QString::number(availableJSTests) + "</td><td>(" + QString::number(ignoredJSTests) + " ignored)</td></tr>") + 0505 QString("<tr valign='middle'><td>XML Tests</td><td>" + QString::number(availableXMLTests) + "</td><td>(" + QString::number(ignoredXMLTests) + " ignored)</td></tr>") + 0506 QString("<tr valign='middle'><td>HTML Tests</td><td>" + QString::number(availableHTMLTests) + "</td><td>(" + QString::number(ignoredHTMLTests) + " ignored)</td></tr>") + 0507 QString("</table></body>"); 0508 0509 // Go to end... 0510 QTextCursor cursor = m_ui.textEdit->textCursor(); 0511 cursor.movePosition(QTextCursor::End); 0512 m_ui.textEdit->setTextCursor(cursor); 0513 0514 // Insert statistics... 0515 m_ui.textEdit->insertHtml(statistics); 0516 0517 // Update treeview... 0518 m_ui.treeWidget->headerItem()->setText(0, i18n("Available Tests: %1 (ignored: %2)", availableTests, ignoredTests)); 0519 } 0520 0521 void TestRegressionWindow::updateProgressBarRange() const 0522 { 0523 if (m_totalTests != 0 && m_totalTestsJS != 0) { 0524 unsigned long totalTests = m_totalTests; 0525 0526 if (Q_HAS_FLAG(m_flags, JSTests)) { 0527 totalTests = m_totalTestsJS; 0528 } else if (Q_HAS_FLAG(m_flags, HTMLTests)) { 0529 totalTests -= m_totalTestsJS; 0530 totalTests -= m_totalTestsDOMTS; 0531 } 0532 0533 m_ui.progressBar->setRange(0, totalTests); 0534 } 0535 } 0536 0537 void TestRegressionWindow::pauseContinueButtonClicked() 0538 { 0539 assert(m_activeProcess != 0); 0540 0541 if (!m_suspended) { 0542 // Suspend process 0543 kill(m_activeProcess->pid(), SIGSTOP); 0544 0545 m_suspended = true; 0546 m_ui.pauseContinueButton->setText(i18n("Continue")); 0547 } else { 0548 // Continue process 0549 kill(m_activeProcess->pid(), SIGCONT); 0550 0551 m_suspended = false; 0552 m_ui.pauseContinueButton->setText(i18n("Pause")); 0553 } 0554 } 0555 0556 void TestRegressionWindow::saveLogButtonClicked() 0557 { 0558 assert(m_activeProcess == 0); 0559 m_saveLogUrl = QFileDialog::getExistingDirectory(); 0560 0561 QString fileName = m_saveLogUrl.path() + "/logOutput.html"; 0562 if (QFileInfo(fileName).exists()) { 0563 // Remove file if already existent... 0564 QFile file(fileName); 0565 if (!file.remove()) { 0566 kError() << " Can't remove " << fileName; 0567 exit(1); 0568 } 0569 } 0570 } 0571 0572 void TestRegressionWindow::runTests() 0573 { 0574 // Run in all-in-one mode... 0575 m_runCounter = 0; 0576 m_testCounter = 0; 0577 0578 initRegressionTesting(QString()); 0579 } 0580 0581 void TestRegressionWindow::runSingleTest() 0582 { 0583 assert(m_activeTreeItem != 0); 0584 0585 QString testFileName = pathFromItem(m_activeTreeItem); 0586 0587 // Run in single-test mode... 0588 m_runCounter = 0; 0589 m_testCounter = -1; 0590 0591 initRegressionTesting(testFileName); 0592 } 0593 0594 void TestRegressionWindow::initRegressionTesting(const QString &testFileName) 0595 { 0596 assert(m_activeProcess == 0); 0597 0598 m_activeProcess = new QProcess(); 0599 m_activeProcess->setReadChannelMode(QProcess::MergedChannels); 0600 0601 QStringList environment = QProcess::systemEnvironment(); 0602 environment << "KDE_DEBUG=false"; // No Dr. Konqi please! 0603 0604 QString program = m_khtmlUrl.path() + "/.libs/testregression"; 0605 QString program2 = m_khtmlUrl.path() + "/testregression"; // with CMake, it's in $buildir/bin 0606 0607 if (!QFileInfo(program).exists()) { 0608 if (!QFileInfo(program2).exists()) { 0609 KMessageBox::error(0, i18n("Cannot find testregression executable.")); 0610 return; 0611 } else { 0612 program = program2; 0613 } 0614 } 0615 0616 QStringList arguments; 0617 arguments << "--base" << m_testsUrl.path(); 0618 0619 if (!m_outputUrl.isEmpty()) { 0620 arguments << "--output" << m_outputUrl.path(); 0621 } 0622 if (!testFileName.isEmpty()) { 0623 arguments << "--test" << testFileName; 0624 } 0625 0626 if (Q_HAS_FLAG(m_flags, JSTests)) { 0627 arguments << "--js"; 0628 } 0629 if (Q_HAS_FLAG(m_flags, HTMLTests)) { 0630 arguments << "--html"; 0631 } 0632 if (Q_HAS_FLAG(m_flags, DebugOutput)) { 0633 arguments << "--debug"; 0634 } 0635 if (Q_HAS_FLAG(m_flags, NoXvfbUse)) { 0636 arguments << "--noxvfb"; 0637 } 0638 0639 connect(m_activeProcess, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(testerExited(int,QProcess::ExitStatus))); 0640 connect(m_activeProcess, SIGNAL(readyReadStandardOutput()), SLOT(testerReceivedData())); 0641 0642 // Clear processing queue before starting... 0643 m_processingQueue.clear(); 0644 0645 // Clean up gui... 0646 m_ui.textEdit->clear(); 0647 m_ui.progressBar->reset(); 0648 m_ui.saveLogButton->setEnabled(false); 0649 m_ui.actionRun_tests->setEnabled(false); 0650 m_ui.pauseContinueButton->setEnabled(true); 0651 0652 // Start regression testing process... 0653 m_activeProcess->setEnvironment(environment); 0654 m_activeProcess->start(program, arguments, QIODevice::ReadOnly); 0655 } 0656 0657 void TestRegressionWindow::initOutputBrowser() 0658 { 0659 assert(m_browserPart == 0); 0660 m_browserPart = new KHTMLPart(m_ui.secondTab, m_ui.secondTab, KHTMLPart::BrowserViewGUI); 0661 0662 // Setup vertical layout for the browser widget... 0663 QVBoxLayout *layout = new QVBoxLayout(); 0664 layout->addWidget(m_browserPart->widget()); 0665 m_ui.secondTab->setLayout(layout); 0666 0667 m_browserPart->setJavaEnabled(true); 0668 m_browserPart->setJScriptEnabled(true); 0669 m_browserPart->setPluginsEnabled(true); 0670 m_browserPart->setURLCursor(QCursor(Qt::PointingHandCursor)); 0671 0672 m_browserPart->widget()->show(); 0673 0674 // Check if there is already an output/index.html present... 0675 loadOutputHTML(); 0676 } 0677 0678 void TestRegressionWindow::loadOutputHTML() const 0679 { 0680 if (m_testsUrl.isEmpty()) { 0681 return; 0682 } 0683 0684 QString fileName = m_testsUrl.path() + "/output/index.html"; 0685 if (!m_outputUrl.isEmpty()) { 0686 fileName = m_outputUrl.path() + "/index.html"; 0687 } 0688 0689 QFileInfo indexHtml(fileName); 0690 if (indexHtml.exists()) { 0691 m_browserPart->openUrl(QUrl::fromLocalFile(fileName)); 0692 m_ui.tabWidget->setTabEnabled(1, true); 0693 } else { 0694 m_ui.tabWidget->setTabEnabled(1, false); 0695 } 0696 } 0697 0698 void TestRegressionWindow::updateItemStatus(TestResult result, QTreeWidgetItem *item, const QString &testFileName) 0699 { 0700 if (!item) { 0701 return; 0702 } 0703 0704 // Ensure item is visible... 0705 QTreeWidgetItem *parent = item; 0706 while (parent != 0) { 0707 m_ui.treeWidget->setItemExpanded(parent, true); 0708 parent = parent->parent(); 0709 } 0710 0711 m_ui.treeWidget->scrollToItem(item); 0712 0713 bool updateIcon = true; 0714 if (m_lastName == testFileName && !m_lastName.isEmpty()) { 0715 if (m_lastResult == result) { 0716 updateIcon = false; 0717 } else if ((m_lastResult == Pass || m_lastResult == PassUnexpected) && 0718 (result == Fail || result == FailKnown || result == Crash)) { 0719 // If one part of the test (render/dom/paint) passed, 0720 // and the current part fails, update to 'failed' icon... 0721 updateIcon = true; 0722 } else if ((m_lastResult == Fail || m_lastResult == FailKnown || m_lastResult == Crash) && 0723 (result == Pass || result == PassUnexpected)) { 0724 // If one part of the test (render/dom/paint) failed, 0725 // and the current part passes, don't update to 'passed' icon... 0726 updateIcon = false; 0727 } 0728 } 0729 0730 // Update icon, if necessary... 0731 if (updateIcon) { 0732 if (result == Crash) { 0733 item->setIcon(0, m_crashPixmap); 0734 } else if (result == Fail) { 0735 item->setIcon(0, m_failPixmap); 0736 } else if (result == FailKnown) { 0737 item->setIcon(0, m_failKnownPixmap); 0738 } else if (result == Pass) { 0739 item->setIcon(0, m_passPixmap); 0740 } else if (result == PassUnexpected) { 0741 item->setIcon(0, m_passUnexpectedPixmap); 0742 } else { // Unhandled state... 0743 assert(false); 0744 } 0745 } 0746 0747 // Remember test & result... 0748 m_lastResult = result; 0749 m_lastName = testFileName; 0750 m_activeTreeItem = item; 0751 } 0752 0753 void TestRegressionWindow::initLegend() 0754 { 0755 // Init pixmaps... 0756 m_failPixmap = QPixmap(":/test/pics/fail.xpm"); 0757 m_failKnownPixmap = QPixmap(":/test/pics/failKnown.xpm"); 0758 m_passPixmap = QPixmap(":/test/pics/pass.xpm"); 0759 m_passUnexpectedPixmap = QPixmap(":/test/pics/passUnexpected.xpm"); 0760 m_ignorePixmap = QPixmap(":/test/pics/ignore.xpm"); 0761 m_crashPixmap = QPixmap(":/test/pics/crash.xpm"); 0762 m_noBaselinePixmap = QPixmap(":/test/pics/noBaseline.xpm"); 0763 0764 QString legend = QLatin1String("<body><center><font size='8'>Welcome to the khtml<br/>") + 0765 QLatin1String("regression testing tool!</font></center><br/><br/>") + 0766 QLatin1String("<table border='0' align='center' cellspacing='15'>") + 0767 QLatin1String("<tr valign='top'><td colspan='2'><center><b>Legend</b></center></td></tr>") + 0768 QLatin1String("<tr valign='middle'><td>Pass</td><td><img src=':/test/pics/pass.xpm'></td></tr>") + 0769 QLatin1String("<tr valign='middle'><td>Pass unexpected</td><td><img src=':/test/pics/passUnexpected.xpm'></td></tr>") + 0770 QLatin1String("<tr valign='middle'><td>Fail</td><td><img src=':/test/pics/fail.xpm'></td></tr>") + 0771 QLatin1String("<tr valign='middle'><td>Fail known</td><td><img src=':/test/pics/failKnown.xpm'></td></tr>") + 0772 QLatin1String("<tr valign='middle'><td>Ignore</td><td><img src=':/test/pics/ignore.xpm'></td></tr>") + 0773 QLatin1String("<tr valign='middle'><td>Baseline missing</td><td><img src=':/test/pics/noBaseline.xpm'></td></tr>") + 0774 QLatin1String("<tr valign='middle'><td>Crash</td><td><img src=':/test/pics/crash.xpm'></td></tr>") + 0775 QLatin1String("</table></body>"); 0776 0777 m_ui.textEdit->setHtml(legend); 0778 } 0779 0780 void TestRegressionWindow::testerExited(int /* exitCode */, QProcess::ExitStatus exitStatus) 0781 { 0782 assert(m_activeProcess != 0); 0783 assert(m_activeTreeItem != 0); 0784 0785 if (exitStatus == QProcess::CrashExit) { // Special case: crash! 0786 QTreeWidgetItem *useItem = m_activeTreeItem; 0787 0788 if (m_testCounter >= 0 || m_runCounter > 0) { // Single-tests mode invoked on a directory OR All-test-mode 0789 QTreeWidgetItem *parent = useItem->parent(); 0790 assert(parent != 0); 0791 0792 useItem = parent->child(parent->indexOfChild(useItem) + 1); 0793 assert(useItem != 0); 0794 } 0795 0796 // Reflect crashed test... 0797 updateItemStatus(Crash, useItem, QString()); 0798 } 0799 0800 if (m_testCounter >= 0) { // All-tests mode 0801 m_ui.progressBar->setValue(m_ui.progressBar->maximum()); 0802 } 0803 0804 // Eventually save log output... 0805 if (!m_saveLogUrl.isEmpty()) { 0806 // We should close our written log with </body></html>. 0807 m_processingQueue.enqueue(QString::fromLatin1("\n</body>\n</html>")); 0808 0809 if (!m_justProcessingQueue) { 0810 m_justProcessingQueue = true; 0811 QTimer::singleShot(50, this, SLOT(processQueue())); 0812 } 0813 } 0814 0815 // Cleanup gui... 0816 m_ui.saveLogButton->setEnabled(true); 0817 m_ui.actionRun_tests->setEnabled(true); 0818 m_ui.pauseContinueButton->setEnabled(false); 0819 0820 // Check if there is already an output/index.html present... 0821 loadOutputHTML(); 0822 0823 // Cleanup data.. 0824 delete m_activeProcess; 0825 m_activeProcess = 0; 0826 0827 m_runCounter = 0; 0828 m_testCounter = 0; 0829 m_activeTreeItem = 0; 0830 } 0831 0832 void TestRegressionWindow::testerReceivedData() 0833 { 0834 assert(m_activeProcess != 0); 0835 0836 QString data(m_activeProcess->readAllStandardOutput()); 0837 QStringList list = data.split('\n'); 0838 0839 QStringList::const_iterator it = list.constBegin(); 0840 const QStringList::const_iterator end = list.constEnd(); 0841 0842 for (; it != end; ++it) { 0843 QString temp = *it; 0844 if (!temp.isEmpty()) { 0845 m_processingQueue.enqueue(temp); 0846 } 0847 } 0848 0849 if (!m_justProcessingQueue) { 0850 m_justProcessingQueue = true; 0851 QTimer::singleShot(50, this, SLOT(processQueue())); 0852 } 0853 } 0854 0855 void TestRegressionWindow::processQueue() 0856 { 0857 while (!m_processingQueue.isEmpty()) { 0858 QString data = m_processingQueue.dequeue(); 0859 TestResult result = Unknown; 0860 0861 QString cacheName = extractTestNameFromData(data, result); 0862 0863 if (result != Unknown) { // Yes, we're dealing with a test result... 0864 if (cacheName.isEmpty()) { // Make sure everything is alright! 0865 kError() << "Couldn't extract cacheName from data=\"" << data << "\"! Ignoring!"; 0866 continue; 0867 } 0868 } 0869 0870 parseRegressionTestingOutput(data, result, cacheName); 0871 } 0872 0873 m_justProcessingQueue = false; 0874 } 0875 0876 void TestRegressionWindow::addToIgnores() 0877 { 0878 assert(m_activeTreeItem != 0); 0879 0880 QString treeItemText = pathFromItem(m_activeTreeItem); 0881 0882 // Extract directory/file name... 0883 int position = treeItemText.lastIndexOf('/'); 0884 0885 QString directory = treeItemText.mid(0, (position == -1 ? 0 : position)); 0886 QString fileName = treeItemText.mid(position + 1); 0887 0888 // Read corresponding ignore file.. 0889 QString ignoreFile = m_testsUrl.path() + "/tests/" + directory + "/ignore"; 0890 QStringList ignoreFileList = readListFile(ignoreFile); 0891 0892 if (!ignoreFileList.contains(fileName)) { 0893 ignoreFileList.append(fileName); 0894 } 0895 0896 // Commit changes... 0897 writeListFile(ignoreFile, ignoreFileList); 0898 0899 // Reset icon status... 0900 m_activeTreeItem->setIcon(0, m_ignorePixmap); 0901 } 0902 0903 void TestRegressionWindow::removeFromIgnores() 0904 { 0905 assert(m_activeTreeItem != 0); 0906 0907 QString treeItemText = pathFromItem(m_activeTreeItem); 0908 0909 // Extract directory/file name... 0910 int position = treeItemText.lastIndexOf('/'); 0911 0912 QString directory = treeItemText.mid(0, (position == -1 ? 0 : position)); 0913 QString fileName = treeItemText.mid(position + 1); 0914 0915 // Read corresponding ignore file.. 0916 QString ignoreFile = m_testsUrl.path() + "/tests/" + directory + "/ignore"; 0917 QStringList ignoreFileList = readListFile(ignoreFile); 0918 0919 if (ignoreFileList.contains(fileName)) { 0920 ignoreFileList.removeAll(fileName); 0921 } 0922 0923 // Commit changes... 0924 writeListFile(ignoreFile, ignoreFileList); 0925 0926 // Reset icon status... 0927 m_activeTreeItem->setIcon(0, QPixmap()); 0928 } 0929 0930 QString TestRegressionWindow::pathFromItem(const QTreeWidgetItem *item) const 0931 { 0932 QString path = item->text(0); 0933 0934 QTreeWidgetItem *parent = item->parent(); 0935 while (parent != 0) { 0936 if (parent->parent() != 0) { 0937 path.prepend(parent->text(0) + "/"); //krazy:exclude=duoblequote_chars DOM demands chars 0938 } 0939 0940 parent = parent->parent(); 0941 } 0942 0943 return path; 0944 } 0945 0946 QString TestRegressionWindow::extractTestNameFromData(QString &data, TestResult &result) const 0947 { 0948 if (data.indexOf("PASS") >= 0 || data.indexOf("FAIL") >= 0) { 0949 // Name extraction regexps... 0950 QString bracesSelector("[0-9a-zA-Z-_<>\\* +-,.:!?$'\"=/\\[\\]\\(\\)]*"); 0951 0952 QRegExp expPass("PASS: (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars 0953 QRegExp expPassUnexpected("PASS \\(unexpected!\\): (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars 0954 0955 QRegExp expFail("FAIL: (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars 0956 QRegExp expFailKnown("FAIL \\(known\\): (" + bracesSelector + ")"); //krazy:exclude=duoblequote_chars DOM demands chars 0957 0958 // Extract name of test... (while using regexps as rare as possible!) 0959 int pos = -1; 0960 QString test; 0961 0962 QRegExp cleanTest(" \\[" + bracesSelector + "\\]"); 0963 0964 pos = expPass.indexIn(data); 0965 if (pos > -1) { 0966 test = expPass.cap(1); 0967 result = Pass; 0968 } 0969 0970 if (result == Unknown) { 0971 pos = expPassUnexpected.indexIn(data); 0972 if (pos > -1) { 0973 test = expPassUnexpected.cap(1); 0974 result = PassUnexpected; 0975 } 0976 } 0977 0978 if (result == Unknown) { 0979 pos = expFail.indexIn(data); 0980 if (pos > -1) { 0981 test = expFail.cap(1); 0982 result = Fail; 0983 } 0984 } 0985 0986 if (result == Unknown) { 0987 pos = expFailKnown.indexIn(data); 0988 if (pos > -1) { 0989 test = expFailKnown.cap(1); 0990 result = FailKnown; 0991 } 0992 } 0993 0994 if (!test.isEmpty() && result != Unknown) { // Got information about test... 0995 // Clean up first, so we only get the file name... 0996 test.replace(cleanTest, QString()); 0997 0998 // Extract cached directory/filename pair... 0999 int lastSlashPos = test.lastIndexOf('/'); 1000 1001 QString cachedDirectory = (lastSlashPos > 0 ? test.mid(0, lastSlashPos) : QString()); 1002 QString cachedFilename = test.mid(lastSlashPos + 1); 1003 1004 if (cachedDirectory == ".") { // Handle cases like "./empty.html" 1005 cachedDirectory.clear(); 1006 } 1007 1008 assert(m_directoryMap.constFind(cachedDirectory) != m_directoryMap.constEnd()); 1009 1010 QString cacheName = cachedDirectory + "/" + cachedFilename; //krazy:exclude=duoblequote_chars DOM demands chars 1011 if (m_itemMap.constFind(cacheName) != m_itemMap.constEnd()) { 1012 // Highlight test... 1013 data.replace(expPass, "<b><font color='green'>PASS:\t\\1</font></b>"); 1014 data.replace(expPassUnexpected, "<b><font color='green'>PASS (unexpected!):\t\\1</font></b>"); 1015 data.replace(expFail, "<b><font color='red'>FAIL:\t\\1</font></b>"); 1016 data.replace(expFailKnown, "<b><font color='red'>FAIL (known):\t\\1</font></b>"); 1017 1018 return cacheName; 1019 } 1020 } 1021 } 1022 1023 return QString(); 1024 } 1025 1026 void TestRegressionWindow::parseRegressionTestingOutput(QString data, TestResult result, const QString &cacheName) 1027 { 1028 if (!cacheName.isEmpty()) { 1029 if (m_testCounter >= 0) { // Only increment in all-tests mode... 1030 m_testCounter++; 1031 } 1032 1033 m_runCounter++; // Always increment... 1034 1035 // Update the icon... 1036 updateItemStatus(result, m_itemMap[cacheName], cacheName); 1037 } 1038 1039 // Apply some nice formatting for the statistics... 1040 if (data.indexOf("Total") >= 0 || data.indexOf("Passes") >= 0 || data.indexOf("Tests completed") >= 0 || 1041 data.indexOf("Errors:") >= 0 || data.indexOf("Failures:") >= 0) { 1042 QRegExp expTotal("Total: ([0-9]*)"); 1043 QRegExp expPasses("Passes: ([0-9 a-z\\(\\)]*)"); 1044 QRegExp expErrors("Errors: ([0-9 a-z]*)"); 1045 QRegExp expFailures("Failures: ([0-9 a-z\\(\\)]*)"); 1046 1047 data.replace("Tests completed.", "<br><center><h2>Tests completed.</h2></center>"); 1048 data.replace(expTotal, "<b><font size='4'>Total: \\1</font></b>"); 1049 data.replace(expPasses, "<b><font size='4' color='green'>Passes: \\1</font></b>"); 1050 data.replace(expErrors, "<b><font size='4' color='blue'>Errors: \\1</font></b>"); 1051 data.replace(expFailures, "<b><font size='4' color='red'>Failures: \\1</font></b>"); 1052 } 1053 1054 if (!data.contains("</body>\n</html>")) { // Don't put <br> behind </html>! 1055 data.append("<br>"); 1056 } 1057 1058 // Update text edit... 1059 updateLogOutput(data); 1060 1061 // Update progressbar... 1062 if (m_testCounter > 0) { 1063 m_ui.progressBar->setValue(m_testCounter); 1064 } 1065 } 1066 1067 void TestRegressionWindow::treeWidgetContextMenuRequested(const QPoint &pos) 1068 { 1069 if ((m_testCounter == -1 && m_activeProcess) || (m_testCounter > 0 && m_activeProcess) || 1070 m_testsUrl.isEmpty() || m_khtmlUrl.isEmpty()) { // Still processing/not ready yet... 1071 return; 1072 } 1073 1074 QTreeWidgetItem *item = m_ui.treeWidget->itemAt(pos); 1075 if (item && item != m_ui.treeWidget->topLevelItem(0)) { 1076 m_activeTreeItem = item; 1077 1078 // Build & show popup menu... 1079 QMenu menu(m_ui.treeWidget); 1080 1081 menu.addAction(SmallIcon("media-playback-start"), i18n("Run test..."), this, SLOT(runSingleTest())); 1082 menu.addSeparator(); 1083 menu.addAction(SmallIcon("list-add"), i18n("Add to ignores..."), this, SLOT(addToIgnores())); 1084 menu.addAction(SmallIcon("dialog-cancel"), i18n("Remove from ignores..."), this, SLOT(removeFromIgnores())); 1085 1086 if (!menu.exec(m_ui.treeWidget->mapToGlobal(pos))) { 1087 m_activeTreeItem = 0; // Needs reset... 1088 } 1089 } 1090 } 1091 1092 void TestRegressionWindow::updateLogOutput(const QString &data) 1093 { 1094 QTextCursor cursor = m_ui.textEdit->textCursor(); 1095 1096 // Append 'data'... 1097 m_ui.textEdit->insertHtml(data); 1098 1099 // Keep a maximum of 100 lines in the log... 1100 const int maxLogLines = 100; 1101 1102 long logLines = countLogLines(); 1103 if (logLines > maxLogLines) { 1104 cursor.movePosition(QTextCursor::Start); 1105 cursor.movePosition(QTextCursor::Down, QTextCursor::KeepAnchor, logLines - maxLogLines); 1106 cursor.removeSelectedText(); 1107 } 1108 1109 // Go to end... 1110 cursor.movePosition(QTextCursor::End); 1111 m_ui.textEdit->setTextCursor(cursor); 1112 1113 // Eventually save log output... 1114 if (!m_saveLogUrl.isEmpty()) { 1115 QString fileName = m_saveLogUrl.path() + "/logOutput.html"; 1116 QIODevice::OpenMode fileFlags = QIODevice::WriteOnly; 1117 1118 bool fileExists = QFileInfo(fileName).exists(); 1119 if (fileExists) { 1120 fileFlags |= QIODevice::Append; 1121 } 1122 1123 QFile file(fileName); 1124 if (!file.open(fileFlags)) { 1125 kError() << " Can't open " << fileName; 1126 exit(1); 1127 } 1128 1129 if (!fileExists) { 1130 file.write(QString::fromLatin1("<html>\n<body>\n").toLatin1()); 1131 } 1132 1133 file.write(QString(data + "\n").toLatin1()); //krazy:exclude=duoblequote_chars DOM demands chars 1134 file.close(); 1135 1136 // Reset save log url, if we reached the end... 1137 if (data.contains("</body>\n</html>")) { 1138 m_saveLogUrl = QUrl(); 1139 } 1140 } 1141 } 1142 1143 unsigned long TestRegressionWindow::countLogLines() const 1144 { 1145 QTextCursor cursor = m_ui.textEdit->textCursor(); 1146 cursor.movePosition(QTextCursor::Start); 1147 1148 unsigned long lines = 0; 1149 while (cursor.movePosition(QTextCursor::Down, QTextCursor::MoveAnchor)) { 1150 lines++; 1151 } 1152 1153 return lines; 1154 } 1155 1156 QStringList TestRegressionWindow::readListFile(const QString &fileName) const 1157 { 1158 QStringList files; 1159 1160 QFileInfo fileInfo(fileName); 1161 if (fileInfo.exists()) { 1162 QFile file(fileName); 1163 if (!file.open(QIODevice::ReadOnly)) { 1164 kError() << " Can't open " << fileName; 1165 exit(1); 1166 } 1167 1168 QString line; 1169 1170 QTextStream fileStream(&file); 1171 while (!(line = fileStream.readLine()).isNull()) { 1172 files.append(line); 1173 } 1174 1175 file.close(); 1176 } 1177 1178 return files; 1179 } 1180 1181 void TestRegressionWindow::writeListFile(const QString &fileName, const QStringList &content) const 1182 { 1183 QFile file(fileName); 1184 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { 1185 kError() << " Can't open " << fileName; 1186 exit(1); 1187 } 1188 1189 file.write(content.join("\n").toLatin1()); 1190 file.close(); 1191 } 1192 1193 #include "moc_test_regression_gui_window.cpp"