test_regression.cpp
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 //BEGIN TestJScriptEnv 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 // recreate properties 00094 m_interpreter->globalObject().put(exec, "document", *m_document); 00095 m_interpreter->globalObject().put(exec, "view", *m_view); 00096 // create new properties 00097 m_interpreter->globalObject().put(exec, "output", KJS::Object(m_output)); 00098 // add convenience shortcuts 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 //END TestJScriptEnv 00110 00111 //BEGIN KateViewObject 00112 00113 KateViewObject::KateViewObject(ExecState *exec, KateView *v, ObjectImp *fallback) 00114 : view(v), fallback(fallback) 00115 { 00116 // put a function 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 // evil hack II: disguise as fallback, otherwise we can't fall back 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 //END KateViewObject 00198 00199 //BEGIN KateViewFunction 00200 00201 KateViewFunction::KateViewFunction(ExecState */*exec*/, 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 &/*thisObj*/, const List &args) 00214 { 00215 // calls a function repeatedly as specified by its first parameter (once 00216 // if not specified). 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 //END KateViewFunction 00273 00274 //BEGIN OutputObject 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 //END OutputObject 00301 00302 //BEGIN OutputFunction 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 // Gather all parameters and concatenate to string 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 // Gather all parameters and concatenate to string 00339 QString res; 00340 for (int i = 0; i < args.size(); i++) { 00341 res += args[i].toString(exec).qstring(); 00342 } 00343 00344 // Append cursor position 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 //END OutputFunction 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 // forget about any settings 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 // signal( SIGALRM, signal_handler ); 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 // | KateDocumentConfig::cfWordWrap 00472 // | KateDocumentConfig::cfRemoveSpaces 00473 | KateDocumentConfig::cfWrapCursor 00474 // | KateDocumentConfig::cfAutoBrackets 00475 // | KateDocumentConfig::cfTabIndentsMode 00476 // | KateDocumentConfig::cfOvr 00477 | KateDocumentConfig::cfKeepIndentProfile 00478 | KateDocumentConfig::cfKeepExtraSpaces 00479 | KateDocumentConfig::cfTabIndents 00480 | KateDocumentConfig::cfShowTabs 00481 | KateDocumentConfig::cfSpaceIndent 00482 | KateDocumentConfig::cfSmartHome 00483 | KateDocumentConfig::cfTabInsertsTab 00484 // | KateDocumentConfig::cfReplaceTabsDyn 00485 // | KateDocumentConfig::cfRemoveTrailingDyn 00486 | KateDocumentConfig::cfDoxygenAutoTyping 00487 // | KateDocumentConfig::cfMixedIndent 00488 | KateDocumentConfig::cfIndentPastedText 00489 ); 00490 cfg.sync(); 00491 00492 int rv = 1; 00493 00494 { 00495 KSimpleConfig dc( "kdebugrc" ); 00496 // FIXME adapt to kate 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 // create widgets 00513 KateFactory *fac = KateFactory::self(); 00514 KMainWindow *toplevel = new KMainWindow(); 00515 KateDocument *part = new KateDocument(/*bSingleViewMode*/true, 00516 /*bBrowserView*/false, 00517 /*bReadOnly*/false, 00518 /*parentWidget*/toplevel, 00519 /*widgetName*/"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 // we're not interested 00536 toplevel->statusBar()->hide(); 00537 00538 if (!getenv("KDE_DEBUG")) { 00539 // set ulimits 00540 rlimit vmem_limit = { 256*1024*1024, RLIM_INFINITY }; // 256Mb Memory should suffice 00541 setrlimit(RLIMIT_AS, &vmem_limit); 00542 rlimit stack_limit = { 8*1024*1024, RLIM_INFINITY }; // 8Mb Memory should suffice 00543 setrlimit(RLIMIT_STACK, &stack_limit); 00544 } 00545 00546 // run the tests 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 // merge testcases specified on command line 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 // Only return a 0 exit code if all tests were successful 00637 if (regressionTest->m_failures_work == 0 && regressionTest->m_errors == 0) 00638 rv = 0; 00639 00640 // cleanup 00641 delete regressionTest; 00642 delete part; 00643 delete toplevel; 00644 // delete fac; 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 // Read ignore file for this directory 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 // Important! Delete comparison config *first* as saver config 00721 // might point to the same physical file. 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 // Run each test in this directory, recusively 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 // gather commands 00802 // directory-specific commands 00803 QStringList commands = concatListFiles(relPath, ".kateconfig-commands"); 00804 // testcase-specific commands 00805 commands += readListFile(m_currentBase + "/" + filename + "-commands"); 00806 00807 rereadConfig(); // reset options to default 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 // if ( m_runHTML ) 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 // kdDebug() << "absPath: \"" << absPath << "\"" << endl; 00866 // kdDebug() << "absBase: \"" << absBase << "\"" << endl; 00867 00868 // walk up to common ancestor directory 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 // kdDebug() << "cmpPathComp: \"" << cmpPathComp.string() << "\"" << endl; 00877 // kdDebug() << "cmpBaseComp: \"" << cmpBaseComp.string() << "\"" << endl; 00878 // kdDebug() << "pos: " << pos << " newpos: " << newpos << endl; 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 // kdDebug() << "basepos " << basepos << " pathpos " << pathpos << endl; 00886 00887 QString rel; 00888 { 00889 QConstString relBase(absBase.unicode() + basepos, absBase.length() - basepos); 00890 QConstString relPath(absPath.unicode() + pathpos, absPath.length() - pathpos); 00891 // generate as many .. as there are path elements in relBase 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 // are blocking reads possible with KProcess? 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 // create a relative path so that it works via web as well. ugly 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 // The test file always exists - except for checkOutput called from *.js files 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); // restore size 01030 01031 // Set arguments 01032 KParts::URLArgs args; 01033 if (filename.endsWith(".txt")) args.serviceType = "text/plain"; 01034 m_part->browserExtension()->setURLArgs(args); 01035 // load page 01036 KURL url; 01037 url.setProtocol("file"); 01038 url.setPath(QFileInfo(m_baseDir + "/tests/"+filename).absFilePath()); 01039 m_part->openURL(url); 01040 01041 // inject commands 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 // Execute script 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 // compare with output file 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 ( /*report_result &&*/ !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 // compare result to existing file 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 // get existing content 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 // generate result file 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 //printf("IGNORED: "); 01209 //printDescription( description ); 01210 return true; // no error 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; // we knew about it 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 // Since we're not visible, this doesn't have an immediate effect, QWidget posts the event 01339 QApplication::sendPostedEvents( 0, QEvent::Resize ); 01340 } 01341 01342 #include "test_regression.moc" 01343 01344 // kate: indent-width 4