• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Kate

test_regression.cpp

Go to the documentation of this file.
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( '<', "&lt;" );
00944             line = line.replace( '>', "&gt;" );
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>&nbsp;\n";
01008     if ( domDiff.length() )
01009         cl += "<span id='b5' class='button' onclick='showDom();m(5);'>D-DIFF</span>&nbsp;\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>&nbsp;" )
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

Kate

Skip menu "Kate"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal