00001
00026 #include <stdlib.h>
00027 #include <sys/time.h>
00028 #include <sys/resource.h>
00029 #include <sys/types.h>
00030 #include <unistd.h>
00031 #include <pwd.h>
00032 #include <signal.h>
00033
00034 #include <kapplication.h>
00035 #include <kstandarddirs.h>
00036 #include <qimage.h>
00037 #include <qfile.h>
00038 #include "test_regression.h"
00039 #include <unistd.h>
00040 #include <stdio.h>
00041
00042 #include <kaction.h>
00043 #include <kcmdlineargs.h>
00044 #include "katefactory.h"
00045 #include <kio/job.h>
00046 #include <kmainwindow.h>
00047 #include <ksimpleconfig.h>
00048 #include <kglobalsettings.h>
00049
00050 #include <qcolor.h>
00051 #include <qcursor.h>
00052 #include <qdir.h>
00053 #include <qevent.h>
00054 #include <qobject.h>
00055 #include <qpushbutton.h>
00056 #include <qscrollview.h>
00057 #include <qstring.h>
00058 #include <qregexp.h>
00059 #include <qtextstream.h>
00060 #include <qvaluelist.h>
00061 #include <qwidget.h>
00062 #include <qfileinfo.h>
00063 #include <qtimer.h>
00064 #include <kstatusbar.h>
00065 #include <qfileinfo.h>
00066
00067 #include "katedocument.h"
00068 #include "kateview.h"
00069 #include <kparts/browserextension.h>
00070 #include "katejscript.h"
00071 #include "katedocumenthelpers.h"
00072 #include "kateconfig.h"
00073 #include "../interfaces/katecmd.h"
00074
00075 using namespace KJS;
00076
00077 #define BASE_DIR_CONFIG "/.testkateregression-3.5"
00078
00079
00080
00081 TestJScriptEnv::TestJScriptEnv(KateDocument *part) {
00082 ExecState *exec = m_interpreter->globalExec();
00083
00084 KJS::ObjectImp *wd = wrapDocument(m_interpreter->globalExec(), part);
00085 KateView *v = static_cast<KateView *>(part->widget());
00086 KJS::ObjectImp *wv = new KateViewObject(exec, v, wrapView(m_interpreter->globalExec(), v));
00087
00088 *m_view = KJS::Object(wv);
00089 *m_document = KJS::Object(wd);
00090 m_output = new OutputObject(exec, part, v);
00091 m_output->ref();
00092
00093
00094 m_interpreter->globalObject().put(exec, "document", *m_document);
00095 m_interpreter->globalObject().put(exec, "view", *m_view);
00096
00097 m_interpreter->globalObject().put(exec, "output", KJS::Object(m_output));
00098
00099 m_interpreter->globalObject().put(exec, "d", *m_document);
00100 m_interpreter->globalObject().put(exec, "v", *m_view);
00101 m_interpreter->globalObject().put(exec, "out", KJS::Object(m_output));
00102 m_interpreter->globalObject().put(exec, "o", KJS::Object(m_output));
00103 }
00104
00105 TestJScriptEnv::~TestJScriptEnv() {
00106 m_output->deref();
00107 }
00108
00109
00110
00111
00112
00113 KateViewObject::KateViewObject(ExecState *exec, KateView *v, ObjectImp *fallback)
00114 : view(v), fallback(fallback)
00115 {
00116
00117 #define PUT_FUNC(name, enumval) \
00118 putDirect(#name, new KateViewFunction(exec,v,KateViewFunction::enumval,1), DontEnum)
00119 fallback->ref();
00120
00121 PUT_FUNC(keyReturn, KeyReturn);
00122 PUT_FUNC(enter, KeyReturn);
00123 PUT_FUNC(type, Type);
00124 PUT_FUNC(keyDelete, KeyDelete);
00125 PUT_FUNC(deleteWordRight, DeleteWordRight);
00126 PUT_FUNC(transpose, Transpose);
00127 PUT_FUNC(cursorLeft, CursorLeft);
00128 PUT_FUNC(cursorPrev, CursorLeft);
00129 PUT_FUNC(left, CursorLeft);
00130 PUT_FUNC(prev, CursorLeft);
00131 PUT_FUNC(shiftCursorLeft, ShiftCursorLeft);
00132 PUT_FUNC(shiftCursorPrev, ShiftCursorLeft);
00133 PUT_FUNC(shiftLeft, ShiftCursorLeft);
00134 PUT_FUNC(shiftPrev, ShiftCursorLeft);
00135 PUT_FUNC(cursorRight, CursorRight);
00136 PUT_FUNC(cursorNext, CursorRight);
00137 PUT_FUNC(right, CursorRight);
00138 PUT_FUNC(next, CursorRight);
00139 PUT_FUNC(shiftCursorRight, ShiftCursorRight);
00140 PUT_FUNC(shiftCursorNext, ShiftCursorRight);
00141 PUT_FUNC(shiftRight, ShiftCursorRight);
00142 PUT_FUNC(shiftNext, ShiftCursorRight);
00143 PUT_FUNC(wordLeft, WordLeft);
00144 PUT_FUNC(wordPrev, WordLeft);
00145 PUT_FUNC(shiftWordLeft, ShiftWordLeft);
00146 PUT_FUNC(shiftWordPrev, ShiftWordLeft);
00147 PUT_FUNC(wordRight, WordRight);
00148 PUT_FUNC(wordNext, WordRight);
00149 PUT_FUNC(shiftWordRight, ShiftWordRight);
00150 PUT_FUNC(shiftWordNext, ShiftWordRight);
00151 PUT_FUNC(home, Home);
00152 PUT_FUNC(shiftHome, ShiftHome);
00153 PUT_FUNC(end, End);
00154 PUT_FUNC(shiftEnd, ShiftEnd);
00155 PUT_FUNC(up, Up);
00156 PUT_FUNC(shiftUp, ShiftUp);
00157 PUT_FUNC(down, Down);
00158 PUT_FUNC(shiftDown, ShiftDown);
00159 PUT_FUNC(scrollUp, ScrollUp);
00160 PUT_FUNC(scrollDown, ScrollDown);
00161 PUT_FUNC(topOfView, TopOfView);
00162 PUT_FUNC(shiftTopOfView, ShiftTopOfView);
00163 PUT_FUNC(bottomOfView, BottomOfView);
00164 PUT_FUNC(shiftBottomOfView, ShiftBottomOfView);
00165 PUT_FUNC(pageUp, PageUp);
00166 PUT_FUNC(shiftPageUp, ShiftPageUp);
00167 PUT_FUNC(pageDown, PageDown);
00168 PUT_FUNC(shiftPageDown, ShiftPageDown);
00169 PUT_FUNC(top, Top);
00170 PUT_FUNC(shiftTop, ShiftTop);
00171 PUT_FUNC(bottom, Bottom);
00172 PUT_FUNC(shiftBottom, ShiftBottom);
00173 PUT_FUNC(toMatchingBracket, ToMatchingBracket);
00174 PUT_FUNC(shiftToMatchingBracket, ShiftToMatchingBracket);
00175 #undef PUT_FUNC
00176 }
00177
00178 KateViewObject::~KateViewObject()
00179 {
00180 fallback->deref();
00181 }
00182
00183 const ClassInfo *KateViewObject::classInfo() const {
00184
00185 return fallback->classInfo();
00186 }
00187
00188 Value KateViewObject::get(ExecState *exec, const Identifier &propertyName) const
00189 {
00190 ValueImp *val = getDirect(propertyName);
00191 if (val)
00192 return Value(val);
00193
00194 return fallback->get(exec, propertyName);
00195 }
00196
00197
00198
00199
00200
00201 KateViewFunction::KateViewFunction(ExecState *, KateView *v, int _id, int length)
00202 {
00203 m_view = v;
00204 id = _id;
00205 putDirect("length",length);
00206 }
00207
00208 bool KateViewFunction::implementsCall() const
00209 {
00210 return true;
00211 }
00212
00213 Value KateViewFunction::call(ExecState *exec, Object &, const List &args)
00214 {
00215
00216
00217 #define REP_CALL(enumval, func) \
00218 case enumval: {\
00219 int cnt = 1;\
00220 if (args.size() > 0) cnt = args[0].toInt32(exec);\
00221 while (cnt-- > 0) { m_view->func(); }\
00222 return Undefined();\
00223 }
00224 switch (id) {
00225 REP_CALL(KeyReturn, keyReturn);
00226 REP_CALL(KeyDelete, keyDelete);
00227 REP_CALL(DeleteWordRight, deleteWordRight);
00228 REP_CALL(Transpose, transpose);
00229 REP_CALL(CursorLeft, cursorLeft);
00230 REP_CALL(ShiftCursorLeft, shiftCursorLeft);
00231 REP_CALL(CursorRight, cursorRight);
00232 REP_CALL(ShiftCursorRight, shiftCursorRight);
00233 REP_CALL(WordLeft, wordLeft);
00234 REP_CALL(ShiftWordLeft, shiftWordLeft);
00235 REP_CALL(WordRight, wordRight);
00236 REP_CALL(ShiftWordRight, shiftWordRight);
00237 REP_CALL(Home, home);
00238 REP_CALL(ShiftHome, shiftHome);
00239 REP_CALL(End, end);
00240 REP_CALL(ShiftEnd, shiftEnd);
00241 REP_CALL(Up, up);
00242 REP_CALL(ShiftUp, shiftUp);
00243 REP_CALL(Down, down);
00244 REP_CALL(ShiftDown, shiftDown);
00245 REP_CALL(ScrollUp, scrollUp);
00246 REP_CALL(ScrollDown, scrollDown);
00247 REP_CALL(TopOfView, topOfView);
00248 REP_CALL(ShiftTopOfView, shiftTopOfView);
00249 REP_CALL(BottomOfView, bottomOfView);
00250 REP_CALL(ShiftBottomOfView, shiftBottomOfView);
00251 REP_CALL(PageUp, pageUp);
00252 REP_CALL(ShiftPageUp, shiftPageUp);
00253 REP_CALL(PageDown, pageDown);
00254 REP_CALL(ShiftPageDown, shiftPageDown);
00255 REP_CALL(Top, top);
00256 REP_CALL(ShiftTop, shiftTop);
00257 REP_CALL(Bottom, bottom);
00258 REP_CALL(ShiftBottom, shiftBottom);
00259 REP_CALL(ToMatchingBracket, toMatchingBracket);
00260 REP_CALL(ShiftToMatchingBracket, shiftToMatchingBracket);
00261 case Type: {
00262 UString str = args[0].toString(exec);
00263 QString res = str.qstring();
00264 return Boolean(m_view->doc()->typeChars(m_view, res));
00265 }
00266 }
00267
00268 return Undefined();
00269 #undef REP_CALL
00270 }
00271
00272
00273
00274
00275
00276 OutputObject::OutputObject(KJS::ExecState *exec, KateDocument *d, KateView *v) : doc(d), view(v), changed(0), outstr(0) {
00277 putDirect("write", new OutputFunction(exec,this,OutputFunction::Write,-1), DontEnum);
00278 putDirect("print", new OutputFunction(exec,this,OutputFunction::Write,-1), DontEnum);
00279 putDirect("writeln", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
00280 putDirect("println", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
00281 putDirect("writeLn", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
00282 putDirect("printLn", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
00283
00284 putDirect("writeCursorPosition", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
00285 putDirect("cursorPosition", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
00286 putDirect("pos", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
00287 putDirect("writeCursorPositionln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
00288 putDirect("cursorPositionln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
00289 putDirect("posln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
00290
00291 }
00292
00293 OutputObject::~OutputObject() {
00294 }
00295
00296 KJS::UString OutputObject::className() const {
00297 return UString("OutputObject");
00298 }
00299
00300
00301
00302
00303
00304 OutputFunction::OutputFunction(KJS::ExecState *exec, OutputObject *output, int _id, int length)
00305 : o(output)
00306 {
00307 id = _id;
00308 if (length >= 0)
00309 putDirect("length",length);
00310 }
00311
00312 bool OutputFunction::implementsCall() const
00313 {
00314 return true;
00315 }
00316
00317 KJS::Value OutputFunction::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
00318 {
00319 if (!*o->changed) *o->outstr = QString();
00320
00321 switch (id) {
00322 case Write:
00323 case Writeln: {
00324
00325 QString res;
00326 for (int i = 0; i < args.size(); i++) {
00327 res += args[i].toString(exec).qstring();
00328 }
00329
00330 if (id == Writeln)
00331 res += "\n";
00332
00333 *o->outstr += res;
00334 break;
00335 }
00336 case WriteCursorPositionln:
00337 case WriteCursorPosition: {
00338
00339 QString res;
00340 for (int i = 0; i < args.size(); i++) {
00341 res += args[i].toString(exec).qstring();
00342 }
00343
00344
00345 uint l, c;
00346 o->view->cursorPosition(&l, &c);
00347 res += "(" + QString::number(l) + "," + QString::number(c) + ")";
00348
00349 if (id == WriteCursorPositionln)
00350 res += "\n";
00351
00352 *o->outstr += res;
00353 break;
00354 }
00355 }
00356
00357 *o->changed = true;
00358 return Undefined();
00359 }
00360
00361
00362
00363
00364
00365 const char failureSnapshotPrefix[] = "testkateregressionrc-FS.";
00366
00367 static QString findMostRecentFailureSnapshot() {
00368 QDir dir(kapp->dirs()->saveLocation("config"),
00369 QString(failureSnapshotPrefix)+"*",
00370 QDir::Time, QDir::Files);
00371 return dir[0].mid(sizeof failureSnapshotPrefix - 1);
00372 }
00373
00374 static KCmdLineOptions options[] =
00375 {
00376 { "b", 0, 0 },
00377 { "base <base_dir>", "Directory containing tests, basedir and output directories.", 0},
00378 { "cmp-failures <snapshot>", "Compare failures of this testrun against snapshot <snapshot>. Defaults to the most recently captured failure snapshot or none if none exists.", 0 },
00379 { "d", 0, 0 },
00380 { "debug", "Do not supress debug output", 0},
00381 { "g", 0, 0 } ,
00382 { "genoutput", "Regenerate baseline (instead of checking)", 0 } ,
00383 { "keep-output", "Keep output files even on success", 0 },
00384 { "save-failures <snapshot>", "Save failures of this testrun as failure snapshot <snapshot>", 0 },
00385 { "s", 0, 0 } ,
00386 { "show", "Show the window while running tests", 0 } ,
00387 { "t", 0, 0 } ,
00388 { "test <filename>", "Only run a single test. Multiple options allowed.", 0 } ,
00389 { "o", 0, 0 },
00390 { "output <directory>", "Put output in <directory> instead of <base_dir>/output", 0 } ,
00391 { "+[base_dir]", "Directory containing tests,basedir and output directories. Only regarded if -b is not specified.", 0 } ,
00392 { "+[testcases]", "Relative path to testcase, or directory of testcases to be run (equivalent to -t).", 0 } ,
00393 KCmdLineLastOption
00394 };
00395
00396 int main(int argc, char *argv[])
00397 {
00398
00399 passwd* pw = getpwuid( getuid() );
00400 if (!pw) {
00401 fprintf(stderr, "dang, I don't even know who I am.\n");
00402 exit(1);
00403 }
00404
00405 QString kh("/var/tmp/%1_kate_non_existent");
00406 kh = kh.arg( pw->pw_name );
00407 setenv( "KDEHOME", kh.latin1(), 1 );
00408 setenv( "LC_ALL", "C", 1 );
00409 setenv( "LANG", "C", 1 );
00410
00411
00412
00413 KCmdLineArgs::init(argc, argv, "testregression", "TestRegression",
00414 "Regression tester for kate", "1.0");
00415 KCmdLineArgs::addCmdLineOptions(options);
00416
00417 KCmdLineArgs *args = KCmdLineArgs::parsedArgs( );
00418
00419 QCString baseDir = args->getOption("base");
00420 QCString baseDirConfigFile(::getenv("HOME") + QCString(BASE_DIR_CONFIG));
00421 {
00422 QFile baseDirConfig(baseDirConfigFile);
00423 if (baseDirConfig.open(IO_ReadOnly)) {
00424 QTextStream bds(&baseDirConfig);
00425 baseDir = bds.readLine().latin1();
00426 }
00427 }
00428
00429 if ( args->count() < 1 && baseDir.isEmpty() ) {
00430 printf("For regression testing, make sure to have checked out the kate regression\n"
00431 "testsuite from svn:\n"
00432 "\tsvn co \"https://<user>@svn.kde.org:/home/kde/trunk/tests/katetests/regression\"\n"
00433 "Remember the root path into which you checked out the testsuite.\n"
00434 "\n");
00435 printf("%s needs the root path of the kate regression\n"
00436 "testsuite to function properly\n"
00437 "By default, the root path is looked up in the file\n"
00438 "\t%s\n"
00439 "If it doesn't exist yet, create it by invoking\n"
00440 "\techo \"<root-path>\" > %s\n"
00441 "You may override the location by specifying the root explicitly on the\n"
00442 "command line with option -b\n"
00443 "", KCmdLineArgs::appName(),
00444 (const char *)baseDirConfigFile,
00445 (const char *)baseDirConfigFile);
00446 ::exit( 1 );
00447 }
00448
00449 int testcase_index = 0;
00450 if (baseDir.isEmpty()) baseDir = args->arg(testcase_index++);
00451
00452 QFileInfo bdInfo(baseDir);
00453 baseDir = QFile::encodeName(bdInfo.absFilePath());
00454
00455 const char *subdirs[] = {"tests", "baseline", "output", "resources"};
00456 for ( int i = 0; i < 2; i++ ) {
00457 QFileInfo sourceDir(QFile::encodeName( baseDir ) + "/" + subdirs[i]);
00458 if ( !sourceDir.exists() || !sourceDir.isDir() ) {
00459 fprintf(stderr,"ERROR: Source directory \"%s/%s\": no such directory.\n", (const char *)baseDir, subdirs[i]);
00460 exit(1);
00461 }
00462 }
00463
00464 KApplication a;
00465 a.disableAutoDcopRegistration();
00466 a.setStyle("windows");
00467 KSimpleConfig cfg( "testkateregressionrc" );
00468 cfg.setGroup("Kate Document Defaults");
00469 cfg.writeEntry("Basic Config Flags",
00470 KateDocumentConfig::cfBackspaceIndents
00471
00472
00473 | KateDocumentConfig::cfWrapCursor
00474
00475
00476
00477 | KateDocumentConfig::cfKeepIndentProfile
00478 | KateDocumentConfig::cfKeepExtraSpaces
00479 | KateDocumentConfig::cfTabIndents
00480 | KateDocumentConfig::cfShowTabs
00481 | KateDocumentConfig::cfSpaceIndent
00482 | KateDocumentConfig::cfSmartHome
00483 | KateDocumentConfig::cfTabInsertsTab
00484
00485
00486 | KateDocumentConfig::cfDoxygenAutoTyping
00487
00488 | KateDocumentConfig::cfIndentPastedText
00489 );
00490 cfg.sync();
00491
00492 int rv = 1;
00493
00494 {
00495 KSimpleConfig dc( "kdebugrc" );
00496
00497 static int areas[] = { 1000, 13000, 13001, 13002, 13010,
00498 13020, 13025, 13030, 13033, 13035,
00499 13040, 13050, 13051, 7000, 7006, 170,
00500 171, 7101, 7002, 7019, 7027, 7014,
00501 7001, 7011, 6070, 6080, 6090, 0};
00502 int channel = args->isSet( "debug" ) ? 2 : 4;
00503 for ( int i = 0; areas[i]; ++i ) {
00504 dc.setGroup( QString::number( areas[i] ) );
00505 dc.writeEntry( "InfoOutput", channel );
00506 }
00507 dc.sync();
00508
00509 kdClearDebugConfig();
00510 }
00511
00512
00513 KateFactory *fac = KateFactory::self();
00514 KMainWindow *toplevel = new KMainWindow();
00515 KateDocument *part = new KateDocument(true,
00516 false,
00517 false,
00518 toplevel,
00519 "testkate");
00520 part->readConfig(&cfg);
00521
00522 toplevel->setCentralWidget( part->widget() );
00523
00524 Q_ASSERT(part->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping);
00525
00526 bool visual = false;
00527 if (args->isSet("show"))
00528 visual = true;
00529
00530 a.setTopWidget(part->widget());
00531 a.setMainWidget( toplevel );
00532 if ( visual )
00533 toplevel->show();
00534
00535
00536 toplevel->statusBar()->hide();
00537
00538 if (!getenv("KDE_DEBUG")) {
00539
00540 rlimit vmem_limit = { 256*1024*1024, RLIM_INFINITY };
00541 setrlimit(RLIMIT_AS, &vmem_limit);
00542 rlimit stack_limit = { 8*1024*1024, RLIM_INFINITY };
00543 setrlimit(RLIMIT_STACK, &stack_limit);
00544 }
00545
00546
00547 RegressionTest *regressionTest = new RegressionTest(part,
00548 &cfg,
00549 baseDir,
00550 args->getOption("output"),
00551 args->isSet("genoutput"));
00552 QObject::connect(part->browserExtension(), SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),
00553 regressionTest, SLOT(slotOpenURL(const KURL&, const KParts::URLArgs &)));
00554 QObject::connect(part->browserExtension(), SIGNAL(resizeTopLevelWidget( int, int )),
00555 regressionTest, SLOT(resizeTopLevelWidget( int, int )));
00556
00557 regressionTest->m_keepOutput = args->isSet("keep-output");
00558 regressionTest->m_showGui = args->isSet("show");
00559
00560 {
00561 QString failureSnapshot = args->getOption("cmp-failures");
00562 if (failureSnapshot.isEmpty())
00563 failureSnapshot = findMostRecentFailureSnapshot();
00564 if (!failureSnapshot.isEmpty())
00565 regressionTest->setFailureSnapshotConfig(
00566 new KSimpleConfig(failureSnapshotPrefix + failureSnapshot, true),
00567 failureSnapshot);
00568 }
00569
00570 if (args->isSet("save-failures")) {
00571 QString failureSaver = args->getOption("save-failures");
00572 regressionTest->setFailureSnapshotSaver(
00573 new KSimpleConfig(failureSnapshotPrefix + failureSaver, false),
00574 failureSaver);
00575 }
00576
00577 bool result = false;
00578 QCStringList tests = args->getOptionList("test");
00579
00580 for (; testcase_index < args->count(); testcase_index++)
00581 tests << args->arg(testcase_index);
00582 if (tests.count() > 0)
00583 for (QValueListConstIterator<QCString> it = tests.begin(); it != tests.end(); ++it) {
00584 result = regressionTest->runTests(*it,true);
00585 if (!result) break;
00586 }
00587 else
00588 result = regressionTest->runTests();
00589
00590 if (result) {
00591 if (args->isSet("genoutput")) {
00592 printf("\nOutput generation completed.\n");
00593 }
00594 else {
00595 printf("\nTests completed.\n");
00596 printf("Total: %d\n",
00597 regressionTest->m_passes_work+
00598 regressionTest->m_passes_fail+
00599 regressionTest->m_failures_work+
00600 regressionTest->m_failures_fail+
00601 regressionTest->m_errors);
00602 printf("Passes: %d",regressionTest->m_passes_work);
00603 if ( regressionTest->m_passes_fail )
00604 printf( " (%d unexpected passes)", regressionTest->m_passes_fail );
00605 if (regressionTest->m_passes_new)
00606 printf(" (%d new since %s)", regressionTest->m_passes_new, regressionTest->m_failureComp->group().latin1());
00607 printf( "\n" );
00608 printf("Failures: %d",regressionTest->m_failures_work);
00609 if ( regressionTest->m_failures_fail )
00610 printf( " (%d expected failures)", regressionTest->m_failures_fail );
00611 if ( regressionTest->m_failures_new )
00612 printf(" (%d new since %s)", regressionTest->m_failures_new, regressionTest->m_failureComp->group().latin1());
00613 printf( "\n" );
00614 if ( regressionTest->m_errors )
00615 printf("Errors: %d\n",regressionTest->m_errors);
00616
00617 QFile list( regressionTest->m_outputDir + "/links.html" );
00618 list.open( IO_WriteOnly|IO_Append );
00619 QString link, cl;
00620 link = QString( "<hr>%1 failures. (%2 expected failures)" )
00621 .arg(regressionTest->m_failures_work )
00622 .arg( regressionTest->m_failures_fail );
00623 if (regressionTest->m_failures_new)
00624 link += QString(" <span style=\"color:red;font-weight:bold\">(%1 new failures since %2)</span>")
00625 .arg(regressionTest->m_failures_new)
00626 .arg(regressionTest->m_failureComp->group());
00627 if (regressionTest->m_passes_new)
00628 link += QString(" <p style=\"color:green;font-weight:bold\">%1 new passes since %2</p>")
00629 .arg(regressionTest->m_passes_new)
00630 .arg(regressionTest->m_failureComp->group());
00631 list.writeBlock( link.latin1(), link.length() );
00632 list.close();
00633 }
00634 }
00635
00636
00637 if (regressionTest->m_failures_work == 0 && regressionTest->m_errors == 0)
00638 rv = 0;
00639
00640
00641 delete regressionTest;
00642 delete part;
00643 delete toplevel;
00644
00645
00646 return rv;
00647 }
00648
00649
00650
00651 RegressionTest *RegressionTest::curr = 0;
00652
00653 RegressionTest::RegressionTest(KateDocument *part, KConfig *baseConfig,
00654 const QString &baseDir,
00655 const QString &outputDir, bool _genOutput)
00656 : QObject(part)
00657 {
00658 m_part = part;
00659 m_view = static_cast<KateView *>(m_part->widget());
00660 m_baseConfig = baseConfig;
00661 m_baseDir = baseDir;
00662 m_baseDir = m_baseDir.replace( "//", "/" );
00663 if ( m_baseDir.endsWith( "/" ) )
00664 m_baseDir = m_baseDir.left( m_baseDir.length() - 1 );
00665 if (outputDir.isEmpty())
00666 m_outputDir = m_baseDir + "/output";
00667 else
00668 m_outputDir = outputDir;
00669 createMissingDirs(m_outputDir + "/");
00670 m_keepOutput = false;
00671 m_genOutput = _genOutput;
00672 m_failureComp = 0;
00673 m_failureSave = 0;
00674 m_showGui = false;
00675 m_passes_work = m_passes_fail = m_passes_new = 0;
00676 m_failures_work = m_failures_fail = m_failures_new = 0;
00677 m_errors = 0;
00678
00679 ::unlink( QFile::encodeName( m_outputDir + "/links.html" ) );
00680 QFile f( m_outputDir + "/empty.html" );
00681 QString s;
00682 f.open( IO_WriteOnly | IO_Truncate );
00683 s = "<html><body>Follow the white rabbit";
00684 f.writeBlock( s.latin1(), s.length() );
00685 f.close();
00686 f.setName( m_outputDir + "/index.html" );
00687 f.open( IO_WriteOnly | IO_Truncate );
00688 s = "<html><frameset cols=150,*><frame src=links.html><frame name=content src=empty.html>";
00689 f.writeBlock( s.latin1(), s.length() );
00690 f.close();
00691
00692 curr = this;
00693 }
00694
00695 #include <qobjectlist.h>
00696
00697 static QStringList readListFile( const QString &filename )
00698 {
00699
00700 QString ignoreFilename = filename;
00701 QFileInfo ignoreInfo(ignoreFilename);
00702 QStringList ignoreFiles;
00703 if (ignoreInfo.exists()) {
00704 QFile ignoreFile(ignoreFilename);
00705 if (!ignoreFile.open(IO_ReadOnly)) {
00706 fprintf(stderr,"Can't open %s\n",ignoreFilename.latin1());
00707 exit(1);
00708 }
00709 QTextStream ignoreStream(&ignoreFile);
00710 QString line;
00711 while (!(line = ignoreStream.readLine()).isNull())
00712 ignoreFiles.append(line);
00713 ignoreFile.close();
00714 }
00715 return ignoreFiles;
00716 }
00717
00718 RegressionTest::~RegressionTest()
00719 {
00720
00721
00722 delete m_failureComp;
00723 delete m_failureSave;
00724 }
00725
00726 void RegressionTest::setFailureSnapshotConfig(KConfig *cfg, const QString &sname)
00727 {
00728 Q_ASSERT(cfg);
00729 m_failureComp = cfg;
00730 m_failureComp->setGroup(sname);
00731 }
00732
00733 void RegressionTest::setFailureSnapshotSaver(KConfig *cfg, const QString &sname)
00734 {
00735 Q_ASSERT(cfg);
00736 m_failureSave = cfg;
00737 m_failureSave->setGroup(sname);
00738 }
00739
00740 QStringList RegressionTest::concatListFiles(const QString &relPath, const QString &filename)
00741 {
00742 QStringList cmds;
00743 int pos = relPath.findRev('/');
00744 if (pos >= 0)
00745 cmds += concatListFiles(relPath.left(pos), filename);
00746 cmds += readListFile(m_baseDir + "/tests/" + relPath + "/" + filename);
00747 return cmds;
00748 }
00749
00750 bool RegressionTest::runTests(QString relPath, bool mustExist, int known_failure)
00751 {
00752 m_currentOutput = QString::null;
00753
00754 if (!QFile(m_baseDir + "/tests/"+relPath).exists()) {
00755 fprintf(stderr,"%s: No such file or directory\n",relPath.latin1());
00756 return false;
00757 }
00758
00759 QString fullPath = m_baseDir + "/tests/"+relPath;
00760 QFileInfo info(fullPath);
00761
00762 if (!info.exists() && mustExist) {
00763 fprintf(stderr,"%s: No such file or directory\n",relPath.latin1());
00764 return false;
00765 }
00766
00767 if (!info.isReadable() && mustExist) {
00768 fprintf(stderr,"%s: Access denied\n",relPath.latin1());
00769 return false;
00770 }
00771
00772 if (info.isDir()) {
00773 QStringList ignoreFiles = readListFile( m_baseDir + "/tests/"+relPath+"/ignore" );
00774 QStringList failureFiles = readListFile( m_baseDir + "/tests/"+relPath+"/KNOWN_FAILURES" );
00775
00776
00777 QDir sourceDir(m_baseDir + "/tests/"+relPath);
00778 for (uint fileno = 0; fileno < sourceDir.count(); fileno++) {
00779 QString filename = sourceDir[fileno];
00780 QString relFilename = relPath.isEmpty() ? filename : relPath+"/"+filename;
00781
00782 if (filename.startsWith(".") || ignoreFiles.contains(filename) )
00783 continue;
00784 int failure_type = NoFailure;
00785 if ( failureFiles.contains( filename ) )
00786 failure_type |= AllFailure;
00787 if ( failureFiles.contains ( filename + "-result" ) )
00788 failure_type |= ResultFailure;
00789 runTests(relFilename, false, failure_type);
00790 }
00791 }
00792 else if (info.isFile()) {
00793
00794 QString relativeDir = QFileInfo(relPath).dirPath();
00795 QString filename = info.fileName();
00796 m_currentBase = m_baseDir + "/tests/"+relativeDir;
00797 m_currentCategory = relativeDir;
00798 m_currentTest = filename;
00799 m_known_failures = known_failure;
00800 m_outputCustomised = false;
00801
00802
00803 QStringList commands = concatListFiles(relPath, ".kateconfig-commands");
00804
00805 commands += readListFile(m_currentBase + "/" + filename + "-commands");
00806
00807 rereadConfig();
00808 if ( filename.endsWith(".txt") ) {
00809 #if 0
00810 if ( relPath.startsWith( "domts/" ) && !m_runJS )
00811 return true;
00812 if ( relPath.startsWith( "ecma/" ) && !m_runJS )
00813 return true;
00814 #endif
00815
00816 testStaticFile(relPath, commands);
00817 }
00818 else if (mustExist) {
00819 fprintf(stderr,"%s: Not a valid test file (must be .txt)\n",relPath.latin1());
00820 return false;
00821 }
00822 } else if (mustExist) {
00823 fprintf(stderr,"%s: Not a regular file\n",relPath.latin1());
00824 return false;
00825 }
00826
00827 return true;
00828 }
00829
00830 void RegressionTest::createLink( const QString& test, int failures )
00831 {
00832 createMissingDirs( m_outputDir + "/" + test + "-compare.html" );
00833
00834 QFile list( m_outputDir + "/links.html" );
00835 list.open( IO_WriteOnly|IO_Append );
00836 QString link;
00837 link = QString( "<a href=\"%1\" target=\"content\" title=\"%2\">" )
00838 .arg( test + "-compare.html" )
00839 .arg( test );
00840 link += m_currentTest;
00841 link += "</a> ";
00842 if (failures & NewFailure)
00843 link += "<span style=\"font-weight:bold;color:red\">";
00844 link += "[";
00845 if ( failures & ResultFailure )
00846 link += "R";
00847 link += "]";
00848 if (failures & NewFailure)
00849 link += "</span>";
00850 link += "<br>\n";
00851 list.writeBlock( link.latin1(), link.length() );
00852 list.close();
00853 }
00854
00861 static QString makeRelativePath(const QString &base, const QString &path)
00862 {
00863 QString absBase = QFileInfo(base).absFilePath();
00864 QString absPath = QFileInfo(path).absFilePath();
00865
00866
00867
00868
00869 int pos = 0;
00870 do {
00871 pos++;
00872 int newpos = absBase.find('/', pos);
00873 if (newpos == -1) newpos = absBase.length();
00874 QConstString cmpPathComp(absPath.unicode() + pos, newpos - pos);
00875 QConstString cmpBaseComp(absBase.unicode() + pos, newpos - pos);
00876
00877
00878
00879 if (cmpPathComp.string() != cmpBaseComp.string()) { pos--; break; }
00880 pos = newpos;
00881 } while (pos < (int)absBase.length() && pos < (int)absPath.length());
00882 int basepos = pos < (int)absBase.length() ? pos + 1 : pos;
00883 int pathpos = pos < (int)absPath.length() ? pos + 1 : pos;
00884
00885
00886
00887 QString rel;
00888 {
00889 QConstString relBase(absBase.unicode() + basepos, absBase.length() - basepos);
00890 QConstString relPath(absPath.unicode() + pathpos, absPath.length() - pathpos);
00891
00892 if (relBase.string().length() > 0) {
00893 for (int i = relBase.string().contains('/'); i > 0; --i)
00894 rel += "../";
00895 rel += "..";
00896 if (relPath.string().length() > 0) rel += "/";
00897 }
00898 rel += relPath.string();
00899 }
00900 return rel;
00901 }
00902
00904 static void pause(int msec)
00905 {
00906 QTime t;
00907 t.start();
00908 do {
00909 kapp->processEvents();
00910 } while (t.elapsed() < msec);
00911 }
00912
00913 void RegressionTest::doFailureReport( const QString& test, int failures )
00914 {
00915 if ( failures == NoFailure ) {
00916 ::unlink( QFile::encodeName( m_outputDir + "/" + test + "-compare.html" ) );
00917 return;
00918 }
00919
00920 createLink( test, failures );
00921
00922 QFile compare( m_outputDir + "/" + test + "-compare.html" );
00923
00924 QString testFile = QFileInfo(test).fileName();
00925
00926 QString renderDiff;
00927 QString domDiff;
00928
00929 QString relOutputDir = makeRelativePath(m_baseDir, m_outputDir);
00930
00931
00932 char pwd[PATH_MAX];
00933 (void) getcwd( pwd, PATH_MAX );
00934 chdir( QFile::encodeName( m_baseDir ) );
00935
00936 if ( failures & ResultFailure ) {
00937 domDiff += "<pre>";
00938 FILE *pipe = popen( QString::fromLatin1( "diff -u baseline/%1-result %3/%2-result" )
00939 .arg ( test, test, relOutputDir ).latin1(), "r" );
00940 QTextIStream *is = new QTextIStream( pipe );
00941 for ( int line = 0; line < 100 && !is->eof(); ++line ) {
00942 QString line = is->readLine();
00943 line = line.replace( '<', "<" );
00944 line = line.replace( '>', ">" );
00945 domDiff += line + "\n";
00946 }
00947 delete is;
00948 pclose( pipe );
00949 domDiff += "</pre>";
00950 }
00951
00952 chdir( pwd );
00953
00954
00955 QString relpath = makeRelativePath(m_outputDir + "/"
00956 + QFileInfo(test).dirPath(), m_baseDir);
00957
00958 compare.open( IO_WriteOnly|IO_Truncate );
00959 QString cl;
00960 cl = QString( "<html><head><title>%1</title>" ).arg( test );
00961 cl += QString( "<script>\n"
00962 "var pics = new Array();\n"
00963 "pics[0]=new Image();\n"
00964 "pics[0].src = '%1';\n"
00965 "pics[1]=new Image();\n"
00966 "pics[1].src = '%2';\n"
00967 "var doflicker = 1;\n"
00968 "var t = 1;\n"
00969 "var lastb=0;\n" )
00970 .arg( relpath+"/baseline/"+test+"-dump.png" )
00971 .arg( testFile+"-dump.png" );
00972 cl += QString( "function toggleVisible(visible) {\n"
00973 " document.getElementById('render').style.visibility= visible == 'render' ? 'visible' : 'hidden';\n"
00974 " document.getElementById('image').style.visibility= visible == 'image' ? 'visible' : 'hidden';\n"
00975 " document.getElementById('dom').style.visibility= visible == 'dom' ? 'visible' : 'hidden';\n"
00976 "}\n"
00977 "function show() { document.getElementById('image').src = pics[t].src; "
00978 "document.getElementById('image').style.borderColor = t && !doflicker ? 'red' : 'gray';\n"
00979 "toggleVisible('image');\n"
00980 "}" );
00981 cl += QString ( "function runSlideShow(){\n"
00982 " document.getElementById('image').src = pics[t].src;\n"
00983 " if (doflicker)\n"
00984 " t = 1 - t;\n"
00985 " setTimeout('runSlideShow()', 200);\n"
00986 "}\n"
00987 "function m(b) { if (b == lastb) return; document.getElementById('b'+b).className='buttondown';\n"
00988 " var e = document.getElementById('b'+lastb);\n"
00989 " if(e) e.className='button';\n"
00990 " lastb = b;\n"
00991 "}\n"
00992 "function showRender() { doflicker=0;toggleVisible('render')\n"
00993 "}\n"
00994 "function showDom() { doflicker=0;toggleVisible('dom')\n"
00995 "}\n"
00996 "</script>\n");
00997
00998 cl += QString ("<style>\n"
00999 ".buttondown { cursor: pointer; padding: 0px 20px; color: white; background-color: blue; border: inset blue 2px;}\n"
01000 ".button { cursor: pointer; padding: 0px 20px; color: black; background-color: white; border: outset blue 2px;}\n"
01001 ".diff { position: absolute; left: 10px; top: 100px; visibility: hidden; border: 1px black solid; background-color: white; color: black; /* width: 800; height: 600; overflow: scroll; */ }\n"
01002 "</style>\n" );
01003
01004 cl += QString( "<body onload=\"m(5); toggleVisible('dom');\"" );
01005 cl += QString(" text=black bgcolor=gray>\n<h1>%3</h1>\n" ).arg( test );
01006 if ( renderDiff.length() )
01007 cl += "<span id='b4' class='button' onclick='showRender();m(4)'>R-DIFF</span> \n";
01008 if ( domDiff.length() )
01009 cl += "<span id='b5' class='button' onclick='showDom();m(5);'>D-DIFF</span> \n";
01010
01011 if ( QFile::exists( m_baseDir + "/tests/"+ test ) )
01012 cl += QString( "<a class=button href=\"%1\">HTML</a> " )
01013 .arg( relpath+"/tests/"+test );
01014
01015 cl += QString( "<hr>"
01016 "<img style='border: solid 5px gray' src=\"%1\" id='image'>" )
01017 .arg( relpath+"/baseline/"+test+"-dump.png" );
01018
01019 cl += "<div id='render' class='diff'>" + renderDiff + "</div>";
01020 cl += "<div id='dom' class='diff'>" + domDiff + "</div>";
01021
01022 cl += "</body></html>";
01023 compare.writeBlock( cl.latin1(), cl.length() );
01024 compare.close();
01025 }
01026
01027 void RegressionTest::testStaticFile(const QString & filename, const QStringList &commands)
01028 {
01029 qApp->mainWidget()->resize( 800, 600);
01030
01031
01032 KParts::URLArgs args;
01033 if (filename.endsWith(".txt")) args.serviceType = "text/plain";
01034 m_part->browserExtension()->setURLArgs(args);
01035
01036 KURL url;
01037 url.setProtocol("file");
01038 url.setPath(QFileInfo(m_baseDir + "/tests/"+filename).absFilePath());
01039 m_part->openURL(url);
01040
01041
01042 for (QStringList::ConstIterator cit = commands.begin(); cit != commands.end(); ++cit) {
01043 QString str = (*cit).stripWhiteSpace();
01044 if (str.isEmpty() || str.startsWith("#")) continue;
01045 Kate::Command *cmd = KateCmd::self()->queryCommand(str);
01046 if (cmd) {
01047 QString msg;
01048 if (!cmd->exec(m_view, str, msg))
01049 fprintf(stderr, "ERROR executing command '%s': %s\n", str.latin1(), msg.latin1());
01050 }
01051 }
01052
01053 pause(200);
01054
01055 Q_ASSERT(m_part->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping);
01056
01057 bool script_error = false;
01058 {
01059
01060 TestJScriptEnv jsenv(m_part);
01061 jsenv.output()->setChangedFlag(&m_outputCustomised);
01062 jsenv.output()->setOutputString(&m_outputString);
01063 script_error = evalJS(jsenv.interpreter(), m_baseDir + "/tests/"+QFileInfo(filename).dirPath()+"/.kateconfig-script", true)
01064 && evalJS(jsenv.interpreter(), m_baseDir + "/tests/"+filename+"-script");
01065 }
01066
01067 int back_known_failures = m_known_failures;
01068
01069 if (!script_error) goto bail_out;
01070
01071 if (m_showGui) kapp->processEvents();
01072
01073 if ( m_genOutput ) {
01074 reportResult(checkOutput(filename+"-result"), "result");
01075 } else {
01076 int failures = NoFailure;
01077
01078
01079 if ( m_known_failures & ResultFailure)
01080 m_known_failures = AllFailure;
01081 bool newfail;
01082 if ( !reportResult( checkOutput(filename+"-result"), "result", &newfail ) )
01083 failures |= ResultFailure;
01084 if (newfail)
01085 failures |= NewFailure;
01086
01087 doFailureReport(filename, failures );
01088 }
01089
01090 bail_out:
01091 m_known_failures = back_known_failures;
01092 m_part->setModified(false);
01093 m_part->closeURL();
01094 }
01095
01096 bool RegressionTest::evalJS(Interpreter &interp, const QString &filename, bool ignore_nonexistent)
01097 {
01098 QString fullSourceName = filename;
01099 QFile sourceFile(fullSourceName);
01100
01101 if (!sourceFile.open(IO_ReadOnly)) {
01102 if (!ignore_nonexistent) {
01103 fprintf(stderr,"ERROR reading file %s\n",fullSourceName.latin1());
01104 m_errors++;
01105 }
01106 return ignore_nonexistent;
01107 }
01108
01109 QTextStream stream ( &sourceFile );
01110 stream.setEncoding( QTextStream::UnicodeUTF8 );
01111 QString code = stream.read();
01112 sourceFile.close();
01113
01114 saw_failure = false;
01115 ignore_errors = false;
01116 Completion c = interp.evaluate(UString( code ) );
01117
01118 if ( !ignore_errors) {
01119 if (c.complType() == Throw) {
01120 QString errmsg = c.value().toString(interp.globalExec()).qstring();
01121 printf( "ERROR: %s (%s)\n",filename.latin1(), errmsg.latin1());
01122 m_errors++;
01123 return false;
01124 }
01125 }
01126 return true;
01127 }
01128
01129 class GlobalImp : public ObjectImp {
01130 public:
01131 virtual UString className() const { return "global"; }
01132 };
01133
01134 RegressionTest::CheckResult RegressionTest::checkOutput(const QString &againstFilename)
01135 {
01136 QString absFilename = QFileInfo(m_baseDir + "/baseline/" + againstFilename).absFilePath();
01137 if ( svnIgnored( absFilename ) ) {
01138 m_known_failures = NoFailure;
01139 return Ignored;
01140 }
01141
01142 CheckResult result = Success;
01143
01144
01145 QString outputFilename = QFileInfo(m_outputDir + "/" + againstFilename).absFilePath();
01146 bool kf = false;
01147 if ( m_known_failures & AllFailure )
01148 kf = true;
01149 if ( kf )
01150 outputFilename += "-KF";
01151
01152 if ( m_genOutput )
01153 outputFilename = absFilename;
01154
01155
01156 QString data;
01157 if (m_outputCustomised) {
01158 data = m_outputString;
01159 } else {
01160 data = m_part->text();
01161 }
01162
01163 QFile file(absFilename);
01164 if (file.open(IO_ReadOnly)) {
01165 QTextStream stream ( &file );
01166 stream.setEncoding( QTextStream::UnicodeUTF8 );
01167
01168 QString fileData = stream.read();
01169
01170 result = ( fileData == data ) ? Success : Failure;
01171 if ( !m_genOutput && result == Success && !m_keepOutput ) {
01172 ::unlink( QFile::encodeName( outputFilename ) );
01173 return Success;
01174 }
01175 } else if (!m_genOutput) {
01176 fprintf(stderr, "Error reading file %s\n", absFilename.latin1());
01177 result = Failure;
01178 }
01179
01180
01181 createMissingDirs( outputFilename );
01182 QFile file2(outputFilename);
01183 if (!file2.open(IO_WriteOnly)) {
01184 fprintf(stderr,"Error writing to file %s\n",outputFilename.latin1());
01185 exit(1);
01186 }
01187
01188 QTextStream stream2(&file2);
01189 stream2.setEncoding( QTextStream::UnicodeUTF8 );
01190 stream2 << data;
01191 if ( m_genOutput )
01192 printf("Generated %s\n", outputFilename.latin1());
01193
01194 return result;
01195 }
01196
01197 void RegressionTest::rereadConfig()
01198 {
01199 m_baseConfig->setGroup("Kate Document Defaults");
01200 m_part->config()->readConfig(m_baseConfig);
01201 m_baseConfig->setGroup("Kate View Defaults");
01202 m_view->config()->readConfig(m_baseConfig);
01203 }
01204
01205 bool RegressionTest::reportResult(CheckResult result, const QString & description, bool *newfail)
01206 {
01207 if ( result == Ignored ) {
01208
01209
01210 return true;
01211 } else
01212 return reportResult( result == Success, description, newfail );
01213 }
01214
01215 bool RegressionTest::reportResult(bool passed, const QString & description, bool *newfail)
01216 {
01217 if (newfail) *newfail = false;
01218
01219 if (m_genOutput)
01220 return true;
01221
01222 QString filename(m_currentTest + "-" + description);
01223 if (!m_currentCategory.isEmpty())
01224 filename = m_currentCategory + "/" + filename;
01225
01226 const bool oldfailed = m_failureComp && m_failureComp->readNumEntry(filename);
01227 if (passed) {
01228 if ( m_known_failures & AllFailure ) {
01229 printf("PASS (unexpected!)");
01230 m_passes_fail++;
01231 } else {
01232 printf("PASS");
01233 m_passes_work++;
01234 }
01235 if (oldfailed) {
01236 printf(" (new)");
01237 m_passes_new++;
01238 }
01239 if (m_failureSave)
01240 m_failureSave->deleteEntry(filename);
01241 }
01242 else {
01243 if ( m_known_failures & AllFailure ) {
01244 printf("FAIL (known)");
01245 m_failures_fail++;
01246 passed = true;
01247 } else {
01248 printf("FAIL");
01249 m_failures_work++;
01250 }
01251 if (!oldfailed && m_failureComp) {
01252 printf(" (new)");
01253 m_failures_new++;
01254 if (newfail) *newfail = true;
01255 }
01256 if (m_failureSave)
01257 m_failureSave->writeEntry(filename, 1);
01258 }
01259 printf(": ");
01260
01261 printDescription( description );
01262 return passed;
01263 }
01264
01265 void RegressionTest::printDescription(const QString& description)
01266 {
01267 if (!m_currentCategory.isEmpty())
01268 printf("%s/", m_currentCategory.latin1());
01269
01270 printf("%s", m_currentTest.latin1());
01271
01272 if (!description.isEmpty()) {
01273 QString desc = description;
01274 desc.replace( '\n', ' ' );
01275 printf(" [%s]", desc.latin1());
01276 }
01277
01278 printf("\n");
01279 fflush(stdout);
01280 }
01281
01282 void RegressionTest::createMissingDirs(const QString & filename)
01283 {
01284 QFileInfo dif(filename);
01285 QFileInfo dirInfo( dif.dirPath() );
01286 if (dirInfo.exists())
01287 return;
01288
01289 QStringList pathComponents;
01290 QFileInfo parentDir = dirInfo;
01291 pathComponents.prepend(parentDir.absFilePath());
01292 while (!parentDir.exists()) {
01293 QString parentPath = parentDir.absFilePath();
01294 int slashPos = parentPath.findRev('/');
01295 if (slashPos < 0)
01296 break;
01297 parentPath = parentPath.left(slashPos);
01298 pathComponents.prepend(parentPath);
01299 parentDir = QFileInfo(parentPath);
01300 }
01301 for (uint pathno = 1; pathno < pathComponents.count(); pathno++) {
01302 if (!QFileInfo(pathComponents[pathno]).exists() &&
01303 !QDir(pathComponents[pathno-1]).mkdir(pathComponents[pathno])) {
01304 fprintf(stderr,"Error creating directory %s\n",pathComponents[pathno].latin1());
01305 exit(1);
01306 }
01307 }
01308 }
01309
01310 void RegressionTest::slotOpenURL(const KURL &url, const KParts::URLArgs &args)
01311 {
01312 m_part->browserExtension()->setURLArgs( args );
01313
01314 m_part->openURL(url);
01315 }
01316
01317 bool RegressionTest::svnIgnored( const QString &filename )
01318 {
01319 QFileInfo fi( filename );
01320 QString ignoreFilename = fi.dirPath() + "/svnignore";
01321 QFile ignoreFile(ignoreFilename);
01322 if (!ignoreFile.open(IO_ReadOnly))
01323 return false;
01324
01325 QTextStream ignoreStream(&ignoreFile);
01326 QString line;
01327 while (!(line = ignoreStream.readLine()).isNull()) {
01328 if ( line == fi.fileName() )
01329 return true;
01330 }
01331 ignoreFile.close();
01332 return false;
01333 }
01334
01335 void RegressionTest::resizeTopLevelWidget( int w, int h )
01336 {
01337 qApp->mainWidget()->resize( w, h );
01338
01339 QApplication::sendPostedEvents( 0, QEvent::Resize );
01340 }
01341
01342 #include "test_regression.moc"
01343
01344