| 1 |
/**************************************************************************** |
| 2 |
** |
| 3 |
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). |
| 4 |
** All rights reserved. |
| 5 |
** Contact: Nokia Corporation (qt-info@nokia.com) |
| 6 |
** |
| 7 |
** This file is part of the test suite of the Qt Toolkit. |
| 8 |
** |
| 9 |
** $QT_BEGIN_LICENSE:LGPL$ |
| 10 |
** GNU Lesser General Public License Usage |
| 11 |
** This file may be used under the terms of the GNU Lesser General Public |
| 12 |
** License version 2.1 as published by the Free Software Foundation and |
| 13 |
** appearing in the file LICENSE.LGPL included in the packaging of this |
| 14 |
** file. Please review the following information to ensure the GNU Lesser |
| 15 |
** General Public License version 2.1 requirements will be met: |
| 16 |
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| 17 |
** |
| 18 |
** In addition, as a special exception, Nokia gives you certain additional |
| 19 |
** rights. These rights are described in the Nokia Qt LGPL Exception |
| 20 |
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| 21 |
** |
| 22 |
** GNU General Public License Usage |
| 23 |
** Alternatively, this file may be used under the terms of the GNU General |
| 24 |
** Public License version 3.0 as published by the Free Software Foundation |
| 25 |
** and appearing in the file LICENSE.GPL included in the packaging of this |
| 26 |
** file. Please review the following information to ensure the GNU General |
| 27 |
** Public License version 3.0 requirements will be met: |
| 28 |
** http://www.gnu.org/copyleft/gpl.html. |
| 29 |
** |
| 30 |
** Other Usage |
| 31 |
** Alternatively, this file may be used in accordance with the terms and |
| 32 |
** conditions contained in a signed written agreement between you and Nokia. |
| 33 |
** |
| 34 |
** |
| 35 |
** |
| 36 |
** |
| 37 |
** |
| 38 |
** $QT_END_LICENSE$ |
| 39 |
** |
| 40 |
****************************************************************************/ |
| 41 |
|
| 42 |
#include <QDir> |
| 43 |
#include <QFile> |
| 44 |
#include <QRegExp> |
| 45 |
#include <QStringList> |
| 46 |
#include <QTest> |
| 47 |
#include <QSet> |
| 48 |
#include <QProcess> |
| 49 |
#include <QDebug> |
| 50 |
|
| 51 |
enum FindSubdirsMode { |
| 52 |
Flat = 0, |
| 53 |
Recursive |
| 54 |
}; |
| 55 |
|
| 56 |
class tst_MakeTestSelfTest: public QObject |
| 57 |
{ |
| 58 |
Q_OBJECT |
| 59 |
|
| 60 |
private slots: |
| 61 |
void tests_auto_pro(); |
| 62 |
|
| 63 |
void tests_pro_files(); |
| 64 |
void tests_pro_files_data(); |
| 65 |
|
| 66 |
void naming_convention(); |
| 67 |
void naming_convention_data(); |
| 68 |
|
| 69 |
void make_check(); |
| 70 |
|
| 71 |
private: |
| 72 |
QStringList find_subdirs(QString const&, FindSubdirsMode, QString const& = QString()); |
| 73 |
|
| 74 |
QSet<QString> all_test_classes; |
| 75 |
}; |
| 76 |
|
| 77 |
bool looks_like_testcase(QString const&,QString*); |
| 78 |
bool looks_like_subdirs(QString const&); |
| 79 |
QStringList find_test_class(QString const&); |
| 80 |
|
| 81 |
/* |
| 82 |
Verify that auto.pro only contains other .pro files (and not directories). |
| 83 |
We enforce this so that we can process every .pro file other than auto.pro |
| 84 |
independently and get all the tests. |
| 85 |
If tests were allowed to appear directly in auto.pro, we'd have the problem |
| 86 |
that we need to somehow run these tests from auto.pro while preventing |
| 87 |
recursion into the other .pro files. |
| 88 |
*/ |
| 89 |
void tst_MakeTestSelfTest::tests_auto_pro() |
| 90 |
{ |
| 91 |
QStringList subdirsList = find_subdirs(SRCDIR "/../auto.pro", Flat); |
| 92 |
if (QTest::currentTestFailed()) { |
| 93 |
return; |
| 94 |
} |
| 95 |
|
| 96 |
foreach (QString const& subdir, subdirsList) { |
| 97 |
QVERIFY2(subdir.endsWith(".pro"), qPrintable(QString( |
| 98 |
"auto.pro contains a subdir `%1'.\n" |
| 99 |
"auto.pro must _only_ contain other .pro files, not actual subdirs.\n" |
| 100 |
"Please move `%1' into some other .pro file referenced by auto.pro." |
| 101 |
).arg(subdir))); |
| 102 |
} |
| 103 |
} |
| 104 |
|
| 105 |
/* Verify that all tests are listed somewhere in one of the autotest .pro files */ |
| 106 |
void tst_MakeTestSelfTest::tests_pro_files() |
| 107 |
{ |
| 108 |
static QStringList lines; |
| 109 |
|
| 110 |
if (lines.isEmpty()) { |
| 111 |
QDir dir(SRCDIR "/.."); |
| 112 |
QStringList proFiles = dir.entryList(QStringList() << "*.pro"); |
| 113 |
foreach (QString const& proFile, proFiles) { |
| 114 |
QString filename = QString("%1/../%2").arg(SRCDIR).arg(proFile); |
| 115 |
QFile file(filename); |
| 116 |
if (!file.open(QIODevice::ReadOnly)) { |
| 117 |
QFAIL(qPrintable(QString("open %1: %2").arg(filename).arg(file.errorString()))); |
| 118 |
} |
| 119 |
while (!file.atEnd()) { |
| 120 |
lines << file.readLine().trimmed(); |
| 121 |
} |
| 122 |
} |
| 123 |
} |
| 124 |
|
| 125 |
QFETCH(QString, subdir); |
| 126 |
QRegExp re(QString("( |=|^|#)%1( |\\\\|$)").arg(QRegExp::escape(subdir))); |
| 127 |
foreach (const QString& line, lines) { |
| 128 |
if (re.indexIn(line) != -1) { |
| 129 |
return; |
| 130 |
} |
| 131 |
} |
| 132 |
|
| 133 |
|
| 134 |
|
| 135 |
QFAIL(qPrintable(QString( |
| 136 |
"Subdir `%1' is missing from tests/auto/*.pro\n" |
| 137 |
"This means the test won't be compiled or run on any platform.\n" |
| 138 |
"If this is intentional, please put the test name in a comment in one of the .pro files.").arg(subdir)) |
| 139 |
); |
| 140 |
|
| 141 |
} |
| 142 |
|
| 143 |
void tst_MakeTestSelfTest::tests_pro_files_data() |
| 144 |
{ |
| 145 |
QTest::addColumn<QString>("subdir"); |
| 146 |
QDir dir(SRCDIR "/.."); |
| 147 |
QStringList subdirs = dir.entryList(QDir::AllDirs|QDir::NoDotAndDotDot); |
| 148 |
|
| 149 |
foreach (const QString& subdir, subdirs) { |
| 150 |
if (subdir == QString::fromLatin1("tmp") |
| 151 |
|| subdir.startsWith(".")) |
| 152 |
{ |
| 153 |
continue; |
| 154 |
} |
| 155 |
QTest::newRow(qPrintable(subdir)) << subdir; |
| 156 |
} |
| 157 |
} |
| 158 |
|
| 159 |
QString format_list(QStringList const& list) |
| 160 |
{ |
| 161 |
if (list.count() == 1) { |
| 162 |
return list.at(0); |
| 163 |
} |
| 164 |
return QString("one of (%1)").arg(list.join(", ")); |
| 165 |
} |
| 166 |
|
| 167 |
void tst_MakeTestSelfTest::naming_convention() |
| 168 |
{ |
| 169 |
QFETCH(QString, subdir); |
| 170 |
QFETCH(QString, target); |
| 171 |
|
| 172 |
QDir dir(SRCDIR "/../" + subdir); |
| 173 |
|
| 174 |
QStringList cppfiles = dir.entryList(QStringList() << "*.h" << "*.cpp"); |
| 175 |
if (cppfiles.isEmpty()) { |
| 176 |
// Common convention is to have test/test.pro and source files in parent dir |
| 177 |
if (dir.dirName() == "test") { |
| 178 |
dir.cdUp(); |
| 179 |
cppfiles = dir.entryList(QStringList() << "*.h" << "*.cpp"); |
| 180 |
} |
| 181 |
|
| 182 |
if (cppfiles.isEmpty()) { |
| 183 |
QSKIP("Couldn't locate source files for test", SkipSingle); |
| 184 |
} |
| 185 |
} |
| 186 |
|
| 187 |
QStringList possible_test_classes; |
| 188 |
foreach (QString const& file, cppfiles) { |
| 189 |
possible_test_classes << find_test_class(dir.path() + "/" + file); |
| 190 |
} |
| 191 |
|
| 192 |
if (possible_test_classes.isEmpty()) { |
| 193 |
QSKIP(qPrintable(QString("Couldn't locate test class in %1").arg(format_list(cppfiles))), SkipSingle); |
| 194 |
} |
| 195 |
|
| 196 |
QVERIFY2(possible_test_classes.contains(target), qPrintable(QString( |
| 197 |
"TARGET is %1, while test class appears to be %2.\n" |
| 198 |
"TARGET and test class _must_ match so that all testcase names can be accurately " |
| 199 |
"determined even if a test fails to compile or run.") |
| 200 |
.arg(target) |
| 201 |
.arg(format_list(possible_test_classes)) |
| 202 |
)); |
| 203 |
|
| 204 |
QVERIFY2(!all_test_classes.contains(target), qPrintable(QString( |
| 205 |
"It looks like there are multiple tests named %1.\n" |
| 206 |
"This makes it impossible to separate results for these tests.\n" |
| 207 |
"Please ensure all tests are uniquely named.") |
| 208 |
.arg(target) |
| 209 |
)); |
| 210 |
|
| 211 |
all_test_classes << target; |
| 212 |
} |
| 213 |
|
| 214 |
void tst_MakeTestSelfTest::naming_convention_data() |
| 215 |
{ |
| 216 |
QTest::addColumn<QString>("subdir"); |
| 217 |
QTest::addColumn<QString>("target"); |
| 218 |
|
| 219 |
foreach (const QString& subdir, find_subdirs(SRCDIR "/../auto.pro", Recursive)) { |
| 220 |
if (QFileInfo(SRCDIR "/../" + subdir).isDir()) { |
| 221 |
QString target; |
| 222 |
if (looks_like_testcase(SRCDIR "/../" + subdir + "/" + QFileInfo(subdir).baseName() + ".pro", &target)) { |
| 223 |
QTest::newRow(qPrintable(subdir)) << subdir << target.toLower(); |
| 224 |
} |
| 225 |
} |
| 226 |
} |
| 227 |
} |
| 228 |
|
| 229 |
/* |
| 230 |
Returns true if a .pro file seems to be for an autotest. |
| 231 |
Running qmake to figure this out takes too long. |
| 232 |
*/ |
| 233 |
bool looks_like_testcase(QString const& pro_file, QString* target) |
| 234 |
{ |
| 235 |
QFile file(pro_file); |
| 236 |
if (!file.open(QIODevice::ReadOnly)) { |
| 237 |
return false; |
| 238 |
} |
| 239 |
|
| 240 |
*target = QString(); |
| 241 |
|
| 242 |
bool loaded_qttest = false; |
| 243 |
|
| 244 |
do { |
| 245 |
QByteArray line = file.readLine(); |
| 246 |
if (line.isEmpty()) { |
| 247 |
break; |
| 248 |
} |
| 249 |
|
| 250 |
line = line.trimmed(); |
| 251 |
line.replace(' ', ""); |
| 252 |
|
| 253 |
if (line == "load(qttest_p4)") { |
| 254 |
loaded_qttest = true; |
| 255 |
} |
| 256 |
|
| 257 |
if (line.startsWith("TARGET=")) { |
| 258 |
*target = QString::fromLatin1(line.mid(sizeof("TARGET=")-1)); |
| 259 |
if (target->contains('/')) { |
| 260 |
*target = target->right(target->lastIndexOf('/')+1); |
| 261 |
} |
| 262 |
} |
| 263 |
|
| 264 |
if (loaded_qttest && !target->isEmpty()) { |
| 265 |
break; |
| 266 |
} |
| 267 |
} while(1); |
| 268 |
|
| 269 |
if (!loaded_qttest) { |
| 270 |
return false; |
| 271 |
} |
| 272 |
|
| 273 |
if (!target->isEmpty() && !target->startsWith("tst_")) { |
| 274 |
return false; |
| 275 |
} |
| 276 |
|
| 277 |
// If no target was set, default to tst_<dirname> |
| 278 |
if (target->isEmpty()) { |
| 279 |
*target = "tst_" + QFileInfo(pro_file).baseName(); |
| 280 |
} |
| 281 |
|
| 282 |
return true; |
| 283 |
} |
| 284 |
|
| 285 |
/* |
| 286 |
Returns true if a .pro file seems to be a subdirs project. |
| 287 |
Running qmake to figure this out takes too long. |
| 288 |
*/ |
| 289 |
bool looks_like_subdirs(QString const& pro_file) |
| 290 |
{ |
| 291 |
QFile file(pro_file); |
| 292 |
if (!file.open(QIODevice::ReadOnly)) { |
| 293 |
return false; |
| 294 |
} |
| 295 |
|
| 296 |
do { |
| 297 |
QByteArray line = file.readLine(); |
| 298 |
if (line.isEmpty()) { |
| 299 |
break; |
| 300 |
} |
| 301 |
|
| 302 |
line = line.trimmed(); |
| 303 |
line.replace(' ', ""); |
| 304 |
|
| 305 |
if (line == "TEMPLATE=subdirs") { |
| 306 |
return true; |
| 307 |
} |
| 308 |
} while(1); |
| 309 |
|
| 310 |
return false; |
| 311 |
} |
| 312 |
|
| 313 |
/* |
| 314 |
Returns a list of all subdirs in a given .pro file |
| 315 |
*/ |
| 316 |
QStringList tst_MakeTestSelfTest::find_subdirs(QString const& pro_file, FindSubdirsMode mode, QString const& prefix) |
| 317 |
{ |
| 318 |
QStringList out; |
| 319 |
|
| 320 |
QByteArray features = qgetenv("QMAKEFEATURES"); |
| 321 |
|
| 322 |
if (features.isEmpty()) { |
| 323 |
features = SRCDIR "/features"; |
| 324 |
} |
| 325 |
else { |
| 326 |
features.prepend(SRCDIR "/features" |
| 327 |
#ifdef Q_OS_WIN32 |
| 328 |
";" |
| 329 |
#else |
| 330 |
":" |
| 331 |
#endif |
| 332 |
); |
| 333 |
} |
| 334 |
|
| 335 |
QStringList args; |
| 336 |
args << pro_file << "-o" << SRCDIR "/dummy_output" << "CONFIG+=dump_subdirs"; |
| 337 |
|
| 338 |
/* Turn on every option there is, to ensure we process every single directory */ |
| 339 |
args |
| 340 |
<< "QT_CONFIG+=dbus" |
| 341 |
<< "QT_CONFIG+=declarative" |
| 342 |
<< "QT_CONFIG+=egl" |
| 343 |
<< "QT_CONFIG+=multimedia" |
| 344 |
<< "QT_CONFIG+=OdfWriter" |
| 345 |
<< "QT_CONFIG+=opengl" |
| 346 |
<< "QT_CONFIG+=openvg" |
| 347 |
<< "QT_CONFIG+=phonon" |
| 348 |
<< "QT_CONFIG+=private_tests" |
| 349 |
<< "QT_CONFIG+=pulseaudio" |
| 350 |
<< "QT_CONFIG+=qt3support" |
| 351 |
<< "QT_CONFIG+=script" |
| 352 |
<< "QT_CONFIG+=svg" |
| 353 |
<< "QT_CONFIG+=webkit" |
| 354 |
<< "QT_CONFIG+=xmlpatterns" |
| 355 |
<< "CONFIG+=mac" |
| 356 |
<< "CONFIG+=embedded" |
| 357 |
<< "CONFIG+=symbian" |
| 358 |
; |
| 359 |
|
| 360 |
|
| 361 |
|
| 362 |
QString cmd_with_args = QString("qmake %1").arg(args.join(" ")); |
| 363 |
|
| 364 |
QProcess proc; |
| 365 |
|
| 366 |
proc.setProcessChannelMode(QProcess::MergedChannels); |
| 367 |
|
| 368 |
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); |
| 369 |
env.insert("QMAKEFEATURES", features); |
| 370 |
proc.setProcessEnvironment(env); |
| 371 |
|
| 372 |
proc.start("qmake", args); |
| 373 |
if (!proc.waitForStarted(10000)) { |
| 374 |
QTest::qFail(qPrintable(QString("Failed to run qmake: %1\nCommand: %2") |
| 375 |
.arg(proc.errorString()) |
| 376 |
.arg(cmd_with_args)), |
| 377 |
__FILE__, __LINE__ |
| 378 |
); |
| 379 |
return out; |
| 380 |
} |
| 381 |
if (!proc.waitForFinished(30000)) { |
| 382 |
QTest::qFail(qPrintable(QString("qmake did not finish within 30 seconds\nCommand: %1\nOutput: %2") |
| 383 |
.arg(proc.errorString()) |
| 384 |
.arg(cmd_with_args) |
| 385 |
.arg(QString::fromLocal8Bit(proc.readAll()))), |
| 386 |
__FILE__, __LINE__ |
| 387 |
); |
| 388 |
return out; |
| 389 |
} |
| 390 |
|
| 391 |
if (proc.exitStatus() != QProcess::NormalExit) { |
| 392 |
QTest::qFail(qPrintable(QString("qmake crashed\nCommand: %1\nOutput: %2") |
| 393 |
.arg(cmd_with_args) |
| 394 |
.arg(QString::fromLocal8Bit(proc.readAll()))), |
| 395 |
__FILE__, __LINE__ |
| 396 |
); |
| 397 |
return out; |
| 398 |
} |
| 399 |
|
| 400 |
if (proc.exitCode() != 0) { |
| 401 |
QTest::qFail(qPrintable(QString("qmake exited with code %1\nCommand: %2\nOutput: %3") |
| 402 |
.arg(proc.exitCode()) |
| 403 |
.arg(cmd_with_args) |
| 404 |
.arg(QString::fromLocal8Bit(proc.readAll()))), |
| 405 |
__FILE__, __LINE__ |
| 406 |
); |
| 407 |
return out; |
| 408 |
} |
| 409 |
|
| 410 |
QList<QByteArray> lines = proc.readAll().split('\n'); |
| 411 |
if (!lines.count()) { |
| 412 |
QTest::qFail(qPrintable(QString("qmake seems to have not output anything\nCommand: %1\n") |
| 413 |
.arg(cmd_with_args)), |
| 414 |
__FILE__, __LINE__ |
| 415 |
); |
| 416 |
return out; |
| 417 |
} |
| 418 |
|
| 419 |
foreach (QByteArray const& line, lines) { |
| 420 |
static const QByteArray marker = "Project MESSAGE: subdir: "; |
| 421 |
if (line.startsWith(marker)) { |
| 422 |
QString subdir = QString::fromLocal8Bit(line.mid(marker.size()).trimmed()); |
| 423 |
out << prefix + subdir; |
| 424 |
|
| 425 |
if (mode == Flat) { |
| 426 |
continue; |
| 427 |
} |
| 428 |
|
| 429 |
// Need full path to subdir |
| 430 |
QString subdir_filepath = subdir; |
| 431 |
subdir_filepath.prepend(QFileInfo(pro_file).path() + "/"); |
| 432 |
|
| 433 |
// Add subdirs recursively |
| 434 |
if (subdir.endsWith(".pro") && looks_like_subdirs(subdir_filepath)) { |
| 435 |
// Need full path to .pro file |
| 436 |
out << find_subdirs(subdir_filepath, mode, prefix); |
| 437 |
} |
| 438 |
|
| 439 |
if (QFileInfo(subdir_filepath).isDir()) { |
| 440 |
subdir_filepath += "/" + subdir + ".pro"; |
| 441 |
if (looks_like_subdirs(subdir_filepath)) { |
| 442 |
out << find_subdirs(subdir_filepath, mode, prefix + subdir + "/"); |
| 443 |
} |
| 444 |
} |
| 445 |
} |
| 446 |
} |
| 447 |
|
| 448 |
return out; |
| 449 |
} |
| 450 |
|
| 451 |
void tst_MakeTestSelfTest::make_check() |
| 452 |
{ |
| 453 |
/* |
| 454 |
Run `make check' over the whole tests tree with a custom TESTRUNNER, |
| 455 |
to verify that the TESTRUNNER mechanism works right. |
| 456 |
*/ |
| 457 |
QString testsDir(SRCDIR "/.."); |
| 458 |
QString checktest(SRCDIR "/checktest/checktest"); |
| 459 |
|
| 460 |
#if defined(Q_OS_WIN32) || defined(Q_OS_MAC) |
| 461 |
if (qgetenv("RUN_SLOW_TESTS").isEmpty()) { |
| 462 |
QSKIP("This test is too slow to run by default on this OS. Set RUN_SLOW_TESTS=1 to run it.", SkipAll); |
| 463 |
} |
| 464 |
#endif |
| 465 |
|
| 466 |
#ifdef Q_OS_WIN32 |
| 467 |
checktest.replace("/", "\\"); |
| 468 |
checktest += ".exe"; |
| 469 |
#endif |
| 470 |
|
| 471 |
QProcess make; |
| 472 |
make.setWorkingDirectory(testsDir); |
| 473 |
|
| 474 |
QStringList arguments; |
| 475 |
arguments << "-k"; |
| 476 |
arguments << "check"; |
| 477 |
arguments << QString("TESTRUNNER=%1").arg(checktest); |
| 478 |
|
| 479 |
// find the right make; from externaltests.cpp |
| 480 |
static const char makes[] = |
| 481 |
"nmake.exe\0" |
| 482 |
"mingw32-make.exe\0" |
| 483 |
"gmake\0" |
| 484 |
"make\0" |
| 485 |
; |
| 486 |
|
| 487 |
bool ok = false; |
| 488 |
for (const char *p = makes; *p; p += strlen(p) + 1) { |
| 489 |
make.start(p, arguments); |
| 490 |
if (make.waitForStarted()) { |
| 491 |
ok = true; |
| 492 |
break; |
| 493 |
} |
| 494 |
} |
| 495 |
|
| 496 |
if (!ok) { |
| 497 |
QFAIL("Could not find the right make tool in PATH"); |
| 498 |
} |
| 499 |
|
| 500 |
QVERIFY(make.waitForFinished(1000 * 60 * 10)); |
| 501 |
QCOMPARE(make.exitStatus(), QProcess::NormalExit); |
| 502 |
|
| 503 |
int pass = 0; |
| 504 |
QList<QByteArray> out = make.readAllStandardOutput().split('\n'); |
| 505 |
QStringList fails; |
| 506 |
foreach (QByteArray line, out) { |
| 507 |
while (line.endsWith("\r")) { |
| 508 |
line.chop(1); |
| 509 |
} |
| 510 |
if (line.startsWith("CHECKTEST FAIL")) { |
| 511 |
fails << QString::fromLocal8Bit(line); |
| 512 |
} |
| 513 |
if (line.startsWith("CHECKTEST PASS")) { |
| 514 |
++pass; |
| 515 |
} |
| 516 |
} |
| 517 |
|
| 518 |
// We can't check that the exit code of make is 0, because some tests |
| 519 |
// may have failed to compile, but that doesn't mean `make check' is broken. |
| 520 |
// We do assume there are at least this many unbroken tests, though. |
| 521 |
QVERIFY2(fails.count() == 0, |
| 522 |
qPrintable(QString("`make check' doesn't work for %1 tests:\n%2") |
| 523 |
.arg(fails.count()).arg(fails.join("\n"))) |
| 524 |
); |
| 525 |
QVERIFY(pass > 50); |
| 526 |
} |
| 527 |
|
| 528 |
QStringList find_test_class(QString const& filename) |
| 529 |
{ |
| 530 |
QStringList out; |
| 531 |
|
| 532 |
QFile file(filename); |
| 533 |
if (!file.open(QIODevice::ReadOnly)) { |
| 534 |
return out; |
| 535 |
} |
| 536 |
|
| 537 |
static char const* klass_indicators[] = { |
| 538 |
"QTEST_MAIN(", |
| 539 |
"QTEST_APPLESS_MAIN(", |
| 540 |
"class", |
| 541 |
"staticconstcharklass[]=\"", /* hax0r tests which define their own metaobject */ |
| 542 |
0 |
| 543 |
}; |
| 544 |
|
| 545 |
do { |
| 546 |
QByteArray line = file.readLine(); |
| 547 |
if (line.isEmpty()) { |
| 548 |
break; |
| 549 |
} |
| 550 |
|
| 551 |
line = line.trimmed(); |
| 552 |
line.replace(' ', ""); |
| 553 |
|
| 554 |
for (int i = 0; klass_indicators[i]; ++i) { |
| 555 |
char const* prefix = klass_indicators[i]; |
| 556 |
if (!line.startsWith(prefix)) { |
| 557 |
continue; |
| 558 |
} |
| 559 |
QByteArray klass = line.mid(strlen(prefix)); |
| 560 |
if (!klass.startsWith("tst_")) { |
| 561 |
continue; |
| 562 |
} |
| 563 |
for (int j = 0; j < klass.size(); ++j) { |
| 564 |
char c = klass[j]; |
| 565 |
if (c == '_' |
| 566 |
|| (c >= '0' && c <= '9') |
| 567 |
|| (c >= 'A' && c <= 'Z') |
| 568 |
|| (c >= 'a' && c <= 'z')) { |
| 569 |
continue; |
| 570 |
} |
| 571 |
else { |
| 572 |
klass.truncate(j); |
| 573 |
break; |
| 574 |
} |
| 575 |
} |
| 576 |
QString klass_str = QString::fromLocal8Bit(klass).toLower(); |
| 577 |
if (!out.contains(klass_str)) |
| 578 |
out << klass_str; |
| 579 |
break; |
| 580 |
} |
| 581 |
} while(1); |
| 582 |
|
| 583 |
return out; |
| 584 |
} |
| 585 |
|
| 586 |
QTEST_MAIN(tst_MakeTestSelfTest) |
| 587 |
#include "tst_maketestselftest.moc" |