TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_regression.cpp 46KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344
  1. /**
  2. * This file is part of the KDE project
  3. *
  4. * Copyright (C) 2001,2003 Peter Kelly (pmk@post.com)
  5. * Copyright (C) 2003,2004 Stephan Kulow (coolo@kde.org)
  6. * Copyright (C) 2004 Dirk Mueller ( mueller@kde.org )
  7. * Copyright 2006 Leo Savernik (l.savernik@aon.at)
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Library General Public
  11. * License as published by the Free Software Foundation; either
  12. * version 2 of the License, or (at your option) any later version.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Library General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Library General Public License
  20. * along with this library; see the file COPYING.LIB. If not, write to
  21. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  22. * Boston, MA 02110-1301, USA.
  23. *
  24. */
  25. #include <stdlib.h>
  26. #include <sys/time.h>
  27. #include <sys/resource.h>
  28. #include <sys/types.h>
  29. #include <unistd.h>
  30. #include <pwd.h>
  31. #include <signal.h>
  32. #include <tdeapplication.h>
  33. #include <kstandarddirs.h>
  34. #include <tqimage.h>
  35. #include <tqfile.h>
  36. #include "test_regression.h"
  37. #include <unistd.h>
  38. #include <stdio.h>
  39. #include <tdeaction.h>
  40. #include <tdecmdlineargs.h>
  41. #include "katefactory.h"
  42. #include <tdeio/job.h>
  43. #include <tdemainwindow.h>
  44. #include <ksimpleconfig.h>
  45. #include <tdeglobalsettings.h>
  46. #include <tqcolor.h>
  47. #include <tqcursor.h>
  48. #include <tqdir.h>
  49. #include <tqevent.h>
  50. #include <tqobject.h>
  51. #include <tqpushbutton.h>
  52. #include <tqscrollview.h>
  53. #include <tqstring.h>
  54. #include <tqregexp.h>
  55. #include <tqtextstream.h>
  56. #include <tqvaluelist.h>
  57. #include <tqwidget.h>
  58. #include <tqfileinfo.h>
  59. #include <tqtimer.h>
  60. #include <kstatusbar.h>
  61. #include <tqfileinfo.h>
  62. #include "katedocument.h"
  63. #include "kateview.h"
  64. #include <tdeparts/browserextension.h>
  65. #include "katejscript.h"
  66. #include "katedocumenthelpers.h"
  67. #include "kateconfig.h"
  68. #include "../interfaces/katecmd.h"
  69. using namespace KJS;
  70. #define BASE_DIR_CONFIG "/.testkateregression-3.5"
  71. //BEGIN TestJScriptEnv
  72. TestJScriptEnv::TestJScriptEnv(KateDocument *part) {
  73. ExecState *exec = m_interpreter->globalExec();
  74. KJS::ObjectImp *wd = wrapDocument(m_interpreter->globalExec(), part);
  75. KateView *v = static_cast<KateView *>(part->widget());
  76. KJS::ObjectImp *wv = new KateViewObject(exec, v, wrapView(m_interpreter->globalExec(), v));
  77. *m_view = KJS::Object(wv);
  78. *m_document = KJS::Object(wd);
  79. m_output = new OutputObject(exec, part, v);
  80. m_output->ref();
  81. // recreate properties
  82. m_interpreter->globalObject().put(exec, "document", *m_document);
  83. m_interpreter->globalObject().put(exec, "view", *m_view);
  84. // create new properties
  85. m_interpreter->globalObject().put(exec, "output", KJS::Object(m_output));
  86. // add convenience shortcuts
  87. m_interpreter->globalObject().put(exec, "d", *m_document);
  88. m_interpreter->globalObject().put(exec, "v", *m_view);
  89. m_interpreter->globalObject().put(exec, "out", KJS::Object(m_output));
  90. m_interpreter->globalObject().put(exec, "o", KJS::Object(m_output));
  91. }
  92. TestJScriptEnv::~TestJScriptEnv() {
  93. m_output->deref();
  94. }
  95. //END TestJScriptEnv
  96. //BEGIN KateViewObject
  97. KateViewObject::KateViewObject(ExecState *exec, KateView *v, ObjectImp *fallback)
  98. : view(v), fallback(fallback)
  99. {
  100. // put a function
  101. #define PUT_FUNC(name, enumval) \
  102. putDirect(#name, new KateViewFunction(exec,v,KateViewFunction::enumval,1), DontEnum)
  103. fallback->ref();
  104. PUT_FUNC(keyReturn, KeyReturn);
  105. PUT_FUNC(enter, KeyReturn);
  106. PUT_FUNC(type, Type);
  107. PUT_FUNC(keyDelete, KeyDelete);
  108. PUT_FUNC(deleteWordRight, DeleteWordRight);
  109. PUT_FUNC(transpose, Transpose);
  110. PUT_FUNC(cursorLeft, CursorLeft);
  111. PUT_FUNC(cursorPrev, CursorLeft);
  112. PUT_FUNC(left, CursorLeft);
  113. PUT_FUNC(prev, CursorLeft);
  114. PUT_FUNC(shiftCursorLeft, ShiftCursorLeft);
  115. PUT_FUNC(shiftCursorPrev, ShiftCursorLeft);
  116. PUT_FUNC(shiftLeft, ShiftCursorLeft);
  117. PUT_FUNC(shiftPrev, ShiftCursorLeft);
  118. PUT_FUNC(cursorRight, CursorRight);
  119. PUT_FUNC(cursorNext, CursorRight);
  120. PUT_FUNC(right, CursorRight);
  121. PUT_FUNC(next, CursorRight);
  122. PUT_FUNC(shiftCursorRight, ShiftCursorRight);
  123. PUT_FUNC(shiftCursorNext, ShiftCursorRight);
  124. PUT_FUNC(shiftRight, ShiftCursorRight);
  125. PUT_FUNC(shiftNext, ShiftCursorRight);
  126. PUT_FUNC(wordLeft, WordLeft);
  127. PUT_FUNC(wordPrev, WordLeft);
  128. PUT_FUNC(shiftWordLeft, ShiftWordLeft);
  129. PUT_FUNC(shiftWordPrev, ShiftWordLeft);
  130. PUT_FUNC(wordRight, WordRight);
  131. PUT_FUNC(wordNext, WordRight);
  132. PUT_FUNC(shiftWordRight, ShiftWordRight);
  133. PUT_FUNC(shiftWordNext, ShiftWordRight);
  134. PUT_FUNC(home, Home);
  135. PUT_FUNC(shiftHome, ShiftHome);
  136. PUT_FUNC(end, End);
  137. PUT_FUNC(shiftEnd, ShiftEnd);
  138. PUT_FUNC(up, Up);
  139. PUT_FUNC(shiftUp, ShiftUp);
  140. PUT_FUNC(down, Down);
  141. PUT_FUNC(shiftDown, ShiftDown);
  142. PUT_FUNC(scrollUp, ScrollUp);
  143. PUT_FUNC(scrollDown, ScrollDown);
  144. PUT_FUNC(topOfView, TopOfView);
  145. PUT_FUNC(shiftTopOfView, ShiftTopOfView);
  146. PUT_FUNC(bottomOfView, BottomOfView);
  147. PUT_FUNC(shiftBottomOfView, ShiftBottomOfView);
  148. PUT_FUNC(pageUp, PageUp);
  149. PUT_FUNC(shiftPageUp, ShiftPageUp);
  150. PUT_FUNC(pageDown, PageDown);
  151. PUT_FUNC(shiftPageDown, ShiftPageDown);
  152. PUT_FUNC(top, Top);
  153. PUT_FUNC(shiftTop, ShiftTop);
  154. PUT_FUNC(bottom, Bottom);
  155. PUT_FUNC(shiftBottom, ShiftBottom);
  156. PUT_FUNC(toMatchingBracket, ToMatchingBracket);
  157. PUT_FUNC(shiftToMatchingBracket, ShiftToMatchingBracket);
  158. #undef PUT_FUNC
  159. }
  160. KateViewObject::~KateViewObject()
  161. {
  162. fallback->deref();
  163. }
  164. const ClassInfo *KateViewObject::classInfo() const {
  165. // evil hack II: disguise as fallback, otherwise we can't fall back
  166. return fallback->classInfo();
  167. }
  168. Value KateViewObject::get(ExecState *exec, const Identifier &propertyName) const
  169. {
  170. ValueImp *val = getDirect(propertyName);
  171. if (val)
  172. return Value(val);
  173. return fallback->get(exec, propertyName);
  174. }
  175. //END KateViewObject
  176. //BEGIN KateViewFunction
  177. KateViewFunction::KateViewFunction(ExecState */*exec*/, KateView *v, int _id, int length)
  178. {
  179. m_view = v;
  180. id = _id;
  181. putDirect("length",length);
  182. }
  183. bool KateViewFunction::implementsCall() const
  184. {
  185. return true;
  186. }
  187. Value KateViewFunction::call(ExecState *exec, Object &/*thisObj*/, const List &args)
  188. {
  189. // calls a function repeatedly as specified by its first parameter (once
  190. // if not specified).
  191. #define REP_CALL(enumval, func) \
  192. case enumval: {\
  193. int cnt = 1;\
  194. if (args.size() > 0) cnt = args[0].toInt32(exec);\
  195. while (cnt-- > 0) { m_view->func(); }\
  196. return Undefined();\
  197. }
  198. switch (id) {
  199. REP_CALL(KeyReturn, keyReturn);
  200. REP_CALL(KeyDelete, keyDelete);
  201. REP_CALL(DeleteWordRight, deleteWordRight);
  202. REP_CALL(Transpose, transpose);
  203. REP_CALL(CursorLeft, cursorLeft);
  204. REP_CALL(ShiftCursorLeft, shiftCursorLeft);
  205. REP_CALL(CursorRight, cursorRight);
  206. REP_CALL(ShiftCursorRight, shiftCursorRight);
  207. REP_CALL(WordLeft, wordLeft);
  208. REP_CALL(ShiftWordLeft, shiftWordLeft);
  209. REP_CALL(WordRight, wordRight);
  210. REP_CALL(ShiftWordRight, shiftWordRight);
  211. REP_CALL(Home, home);
  212. REP_CALL(ShiftHome, shiftHome);
  213. REP_CALL(End, end);
  214. REP_CALL(ShiftEnd, shiftEnd);
  215. REP_CALL(Up, up);
  216. REP_CALL(ShiftUp, shiftUp);
  217. REP_CALL(Down, down);
  218. REP_CALL(ShiftDown, shiftDown);
  219. REP_CALL(ScrollUp, scrollUp);
  220. REP_CALL(ScrollDown, scrollDown);
  221. REP_CALL(TopOfView, topOfView);
  222. REP_CALL(ShiftTopOfView, shiftTopOfView);
  223. REP_CALL(BottomOfView, bottomOfView);
  224. REP_CALL(ShiftBottomOfView, shiftBottomOfView);
  225. REP_CALL(PageUp, pageUp);
  226. REP_CALL(ShiftPageUp, shiftPageUp);
  227. REP_CALL(PageDown, pageDown);
  228. REP_CALL(ShiftPageDown, shiftPageDown);
  229. REP_CALL(Top, top);
  230. REP_CALL(ShiftTop, shiftTop);
  231. REP_CALL(Bottom, bottom);
  232. REP_CALL(ShiftBottom, shiftBottom);
  233. REP_CALL(ToMatchingBracket, toMatchingBracket);
  234. REP_CALL(ShiftToMatchingBracket, shiftToMatchingBracket);
  235. case Type: {
  236. UString str = args[0].toString(exec);
  237. TQString res = str.qstring();
  238. return Boolean(m_view->doc()->typeChars(m_view, res));
  239. }
  240. }
  241. return Undefined();
  242. #undef REP_CALL
  243. }
  244. //END KateViewFunction
  245. //BEGIN OutputObject
  246. OutputObject::OutputObject(KJS::ExecState *exec, KateDocument *d, KateView *v) : doc(d), view(v), changed(0), outstr(0) {
  247. putDirect("write", new OutputFunction(exec,this,OutputFunction::Write,-1), DontEnum);
  248. putDirect("print", new OutputFunction(exec,this,OutputFunction::Write,-1), DontEnum);
  249. putDirect("writeln", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
  250. putDirect("println", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
  251. putDirect("writeLn", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
  252. putDirect("printLn", new OutputFunction(exec,this,OutputFunction::Writeln,-1), DontEnum);
  253. putDirect("writeCursorPosition", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
  254. putDirect("cursorPosition", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
  255. putDirect("pos", new OutputFunction(exec,this,OutputFunction::WriteCursorPosition,-1), DontEnum);
  256. putDirect("writeCursorPositionln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
  257. putDirect("cursorPositionln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
  258. putDirect("posln", new OutputFunction(exec,this,OutputFunction::WriteCursorPositionln,-1), DontEnum);
  259. }
  260. OutputObject::~OutputObject() {
  261. }
  262. KJS::UString OutputObject::className() const {
  263. return UString("OutputObject");
  264. }
  265. //END OutputObject
  266. //BEGIN OutputFunction
  267. OutputFunction::OutputFunction(KJS::ExecState *exec, OutputObject *output, int _id, int length)
  268. : o(output)
  269. {
  270. id = _id;
  271. if (length >= 0)
  272. putDirect("length",length);
  273. }
  274. bool OutputFunction::implementsCall() const
  275. {
  276. return true;
  277. }
  278. KJS::Value OutputFunction::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args)
  279. {
  280. if (!*o->changed) *o->outstr = TQString();
  281. switch (id) {
  282. case Write:
  283. case Writeln: {
  284. // Gather all parameters and concatenate to string
  285. TQString res;
  286. for (int i = 0; i < args.size(); i++) {
  287. res += args[i].toString(exec).qstring();
  288. }
  289. if (id == Writeln)
  290. res += "\n";
  291. *o->outstr += res;
  292. break;
  293. }
  294. case WriteCursorPositionln:
  295. case WriteCursorPosition: {
  296. // Gather all parameters and concatenate to string
  297. TQString res;
  298. for (int i = 0; i < args.size(); i++) {
  299. res += args[i].toString(exec).qstring();
  300. }
  301. // Append cursor position
  302. uint l, c;
  303. o->view->cursorPosition(&l, &c);
  304. res += "(" + TQString::number(l) + "," + TQString::number(c) + ")";
  305. if (id == WriteCursorPositionln)
  306. res += "\n";
  307. *o->outstr += res;
  308. break;
  309. }
  310. }
  311. *o->changed = true;
  312. return Undefined();
  313. }
  314. //END OutputFunction
  315. // -------------------------------------------------------------------------
  316. const char failureSnapshotPrefix[] = "testkateregressionrc-FS.";
  317. static TQString findMostRecentFailureSnapshot() {
  318. TQDir dir(kapp->dirs()->saveLocation("config"),
  319. TQString(failureSnapshotPrefix)+"*",
  320. TQDir::Time, TQDir::Files);
  321. return dir[0].mid(sizeof failureSnapshotPrefix - 1);
  322. }
  323. static TDECmdLineOptions options[] =
  324. {
  325. { "b", 0, 0 },
  326. { "base <base_dir>", "Directory containing tests, basedir and output directories.", 0},
  327. { "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 },
  328. { "d", 0, 0 },
  329. { "debug", "Do not supress debug output", 0},
  330. { "g", 0, 0 } ,
  331. { "genoutput", "Regenerate baseline (instead of checking)", 0 } ,
  332. { "keep-output", "Keep output files even on success", 0 },
  333. { "save-failures <snapshot>", "Save failures of this testrun as failure snapshot <snapshot>", 0 },
  334. { "s", 0, 0 } ,
  335. { "show", "Show the window while running tests", 0 } ,
  336. { "t", 0, 0 } ,
  337. { "test <filename>", "Only run a single test. Multiple options allowed.", 0 } ,
  338. { "o", 0, 0 },
  339. { "output <directory>", "Put output in <directory> instead of <base_dir>/output", 0 } ,
  340. { "+[base_dir]", "Directory containing tests,basedir and output directories. Only regarded if -b is not specified.", 0 } ,
  341. { "+[testcases]", "Relative path to testcase, or directory of testcases to be run (equivalent to -t).", 0 } ,
  342. TDECmdLineLastOption
  343. };
  344. int main(int argc, char *argv[])
  345. {
  346. // forget about any settings
  347. passwd* pw = getpwuid( getuid() );
  348. if (!pw) {
  349. fprintf(stderr, "dang, I don't even know who I am.\n");
  350. exit(1);
  351. }
  352. TQString kh("/var/tmp/%1_kate_non_existent");
  353. kh = kh.arg( pw->pw_name );
  354. setenv( "TDEHOME", kh.latin1(), 1 );
  355. setenv( "LC_ALL", "C", 1 );
  356. setenv( "LANG", "C", 1 );
  357. // signal( SIGALRM, signal_handler );
  358. TDECmdLineArgs::init(argc, argv, "testregression", "TestRegression",
  359. "Regression tester for kate", "1.0");
  360. TDECmdLineArgs::addCmdLineOptions(options);
  361. TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs( );
  362. TQCString baseDir = args->getOption("base");
  363. TQCString baseDirConfigFile(::getenv("HOME") + TQCString(BASE_DIR_CONFIG));
  364. {
  365. TQFile baseDirConfig(baseDirConfigFile);
  366. if (baseDirConfig.open(IO_ReadOnly)) {
  367. TQTextStream bds(&baseDirConfig);
  368. baseDir = bds.readLine().latin1();
  369. }
  370. }
  371. if ( args->count() < 1 && baseDir.isEmpty() ) {
  372. printf("For regression testing, make sure to have checked out the kate regression\n"
  373. "testsuite from svn:\n"
  374. "\tsvn co \"https://<user>@svn.kde.org:/home/kde/trunk/tests/katetests/regression\"\n"
  375. "Remember the root path into which you checked out the testsuite.\n"
  376. "\n");
  377. printf("%s needs the root path of the kate regression\n"
  378. "testsuite to function properly\n"
  379. "By default, the root path is looked up in the file\n"
  380. "\t%s\n"
  381. "If it doesn't exist yet, create it by invoking\n"
  382. "\techo \"<root-path>\" > %s\n"
  383. "You may override the location by specifying the root explicitly on the\n"
  384. "command line with option -b\n"
  385. "", TDECmdLineArgs::appName(),
  386. (const char *)baseDirConfigFile,
  387. (const char *)baseDirConfigFile);
  388. ::exit( 1 );
  389. }
  390. int testcase_index = 0;
  391. if (baseDir.isEmpty()) baseDir = args->arg(testcase_index++);
  392. TQFileInfo bdInfo(baseDir);
  393. baseDir = TQFile::encodeName(bdInfo.absFilePath());
  394. const char *subdirs[] = {"tests", "baseline", "output", "resources"};
  395. for ( int i = 0; i < 2; i++ ) {
  396. TQFileInfo sourceDir(TQFile::encodeName( baseDir ) + "/" + subdirs[i]);
  397. if ( !sourceDir.exists() || !sourceDir.isDir() ) {
  398. fprintf(stderr,"ERROR: Source directory \"%s/%s\": no such directory.\n", (const char *)baseDir, subdirs[i]);
  399. exit(1);
  400. }
  401. }
  402. TDEApplication a;
  403. a.disableAutoDcopRegistration();
  404. a.setStyle("windows");
  405. KSimpleConfig cfg( "testkateregressionrc" );
  406. cfg.setGroup("Kate Document Defaults");
  407. cfg.writeEntry("Basic Config Flags",
  408. KateDocumentConfig::cfBackspaceIndents
  409. // | KateDocumentConfig::cfWordWrap
  410. // | KateDocumentConfig::cfRemoveSpaces
  411. | KateDocumentConfig::cfWrapCursor
  412. // | KateDocumentConfig::cfAutoBrackets
  413. // | KateDocumentConfig::cfTabIndentsMode
  414. // | KateDocumentConfig::cfOvr
  415. | KateDocumentConfig::cfKeepIndentProfile
  416. | KateDocumentConfig::cfKeepExtraSpaces
  417. | KateDocumentConfig::cfTabIndents
  418. | KateDocumentConfig::cfShowTabs
  419. | KateDocumentConfig::cfSpaceIndent
  420. | KateDocumentConfig::cfSmartHome
  421. | KateDocumentConfig::cfTabInsertsTab
  422. // | KateDocumentConfig::cfReplaceTabsDyn
  423. // | KateDocumentConfig::cfRemoveTrailingDyn
  424. | KateDocumentConfig::cfDoxygenAutoTyping
  425. // | KateDocumentConfig::cfMixedIndent
  426. | KateDocumentConfig::cfIndentPastedText
  427. );
  428. cfg.sync();
  429. int rv = 1;
  430. {
  431. KSimpleConfig dc( "kdebugrc" );
  432. // FIXME adapt to kate
  433. static int areas[] = { 1000, 13000, 13001, 13002, 13010,
  434. 13020, 13025, 13030, 13033, 13035,
  435. 13040, 13050, 13051, 7000, 7006, 170,
  436. 171, 7101, 7002, 7019, 7027, 7014,
  437. 7001, 7011, 6070, 6080, 6090, 0};
  438. int channel = args->isSet( "debug" ) ? 2 : 4;
  439. for ( int i = 0; areas[i]; ++i ) {
  440. dc.setGroup( TQString::number( areas[i] ) );
  441. dc.writeEntry( "InfoOutput", channel );
  442. }
  443. dc.sync();
  444. kdClearDebugConfig();
  445. }
  446. // create widgets
  447. KateFactory *fac = KateFactory::self();
  448. TDEMainWindow *toplevel = new TDEMainWindow();
  449. KateDocument *part = new KateDocument(/*bSingleViewMode*/true,
  450. /*bBrowserView*/false,
  451. /*bReadOnly*/false,
  452. /*parentWidget*/toplevel,
  453. /*widgetName*/"testkate");
  454. part->readConfig(&cfg);
  455. toplevel->setCentralWidget( part->widget() );
  456. Q_ASSERT(part->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping);
  457. bool visual = false;
  458. if (args->isSet("show"))
  459. visual = true;
  460. a.setTopWidget(part->widget());
  461. a.setMainWidget( toplevel );
  462. if ( visual )
  463. toplevel->show();
  464. // we're not interested
  465. toplevel->statusBar()->hide();
  466. if (!getenv("TDE_DEBUG")) {
  467. // set ulimits
  468. rlimit vmem_limit = { 256*1024*1024, RLIM_INFINITY }; // 256Mb Memory should suffice
  469. setrlimit(RLIMIT_AS, &vmem_limit);
  470. rlimit stack_limit = { 8*1024*1024, RLIM_INFINITY }; // 8Mb Memory should suffice
  471. setrlimit(RLIMIT_STACK, &stack_limit);
  472. }
  473. // run the tests
  474. RegressionTest *regressionTest = new RegressionTest(part,
  475. &cfg,
  476. baseDir,
  477. args->getOption("output"),
  478. args->isSet("genoutput"));
  479. TQObject::connect(part->browserExtension(), TQT_SIGNAL(openURLRequest(const KURL &, const KParts::URLArgs &)),
  480. regressionTest, TQT_SLOT(slotOpenURL(const KURL&, const KParts::URLArgs &)));
  481. TQObject::connect(part->browserExtension(), TQT_SIGNAL(resizeTopLevelWidget( int, int )),
  482. regressionTest, TQT_SLOT(resizeTopLevelWidget( int, int )));
  483. regressionTest->m_keepOutput = args->isSet("keep-output");
  484. regressionTest->m_showGui = args->isSet("show");
  485. {
  486. TQString failureSnapshot = args->getOption("cmp-failures");
  487. if (failureSnapshot.isEmpty())
  488. failureSnapshot = findMostRecentFailureSnapshot();
  489. if (!failureSnapshot.isEmpty())
  490. regressionTest->setFailureSnapshotConfig(
  491. new KSimpleConfig(failureSnapshotPrefix + failureSnapshot, true),
  492. failureSnapshot);
  493. }
  494. if (args->isSet("save-failures")) {
  495. TQString failureSaver = args->getOption("save-failures");
  496. regressionTest->setFailureSnapshotSaver(
  497. new KSimpleConfig(failureSnapshotPrefix + failureSaver, false),
  498. failureSaver);
  499. }
  500. bool result = false;
  501. QCStringList tests = args->getOptionList("test");
  502. // merge testcases specified on command line
  503. for (; testcase_index < args->count(); testcase_index++)
  504. tests << args->arg(testcase_index);
  505. if (tests.count() > 0)
  506. for (TQValueListConstIterator<TQCString> it = tests.begin(); it != tests.end(); ++it) {
  507. result = regressionTest->runTests(*it,true);
  508. if (!result) break;
  509. }
  510. else
  511. result = regressionTest->runTests();
  512. if (result) {
  513. if (args->isSet("genoutput")) {
  514. printf("\nOutput generation completed.\n");
  515. }
  516. else {
  517. printf("\nTests completed.\n");
  518. printf("Total: %d\n",
  519. regressionTest->m_passes_work+
  520. regressionTest->m_passes_fail+
  521. regressionTest->m_failures_work+
  522. regressionTest->m_failures_fail+
  523. regressionTest->m_errors);
  524. printf("Passes: %d",regressionTest->m_passes_work);
  525. if ( regressionTest->m_passes_fail )
  526. printf( " (%d unexpected passes)", regressionTest->m_passes_fail );
  527. if (regressionTest->m_passes_new)
  528. printf(" (%d new since %s)", regressionTest->m_passes_new, regressionTest->m_failureComp->group().latin1());
  529. printf( "\n" );
  530. printf("Failures: %d",regressionTest->m_failures_work);
  531. if ( regressionTest->m_failures_fail )
  532. printf( " (%d expected failures)", regressionTest->m_failures_fail );
  533. if ( regressionTest->m_failures_new )
  534. printf(" (%d new since %s)", regressionTest->m_failures_new, regressionTest->m_failureComp->group().latin1());
  535. printf( "\n" );
  536. if ( regressionTest->m_errors )
  537. printf("Errors: %d\n",regressionTest->m_errors);
  538. TQFile list( regressionTest->m_outputDir + "/links.html" );
  539. list.open( IO_WriteOnly|IO_Append );
  540. TQString link, cl;
  541. link = TQString( "<hr>%1 failures. (%2 expected failures)" )
  542. .arg(regressionTest->m_failures_work )
  543. .arg( regressionTest->m_failures_fail );
  544. if (regressionTest->m_failures_new)
  545. link += TQString(" <span style=\"color:red;font-weight:bold\">(%1 new failures since %2)</span>")
  546. .arg(regressionTest->m_failures_new)
  547. .arg(regressionTest->m_failureComp->group());
  548. if (regressionTest->m_passes_new)
  549. link += TQString(" <p style=\"color:green;font-weight:bold\">%1 new passes since %2</p>")
  550. .arg(regressionTest->m_passes_new)
  551. .arg(regressionTest->m_failureComp->group());
  552. list.tqwriteBlock( link.latin1(), link.length() );
  553. list.close();
  554. }
  555. }
  556. // Only return a 0 exit code if all tests were successful
  557. if (regressionTest->m_failures_work == 0 && regressionTest->m_errors == 0)
  558. rv = 0;
  559. // cleanup
  560. delete regressionTest;
  561. delete part;
  562. delete toplevel;
  563. // delete fac;
  564. return rv;
  565. }
  566. // -------------------------------------------------------------------------
  567. RegressionTest *RegressionTest::curr = 0;
  568. RegressionTest::RegressionTest(KateDocument *part, TDEConfig *baseConfig,
  569. const TQString &baseDir,
  570. const TQString &outputDir, bool _genOutput)
  571. : TQObject(part)
  572. {
  573. m_part = part;
  574. m_view = static_cast<KateView *>(m_part->widget());
  575. m_baseConfig = baseConfig;
  576. m_baseDir = baseDir;
  577. m_baseDir = m_baseDir.replace( "//", "/" );
  578. if ( m_baseDir.endsWith( "/" ) )
  579. m_baseDir = m_baseDir.left( m_baseDir.length() - 1 );
  580. if (outputDir.isEmpty())
  581. m_outputDir = m_baseDir + "/output";
  582. else
  583. m_outputDir = outputDir;
  584. createMissingDirs(m_outputDir + "/");
  585. m_keepOutput = false;
  586. m_genOutput = _genOutput;
  587. m_failureComp = 0;
  588. m_failureSave = 0;
  589. m_showGui = false;
  590. m_passes_work = m_passes_fail = m_passes_new = 0;
  591. m_failures_work = m_failures_fail = m_failures_new = 0;
  592. m_errors = 0;
  593. ::unlink( TQFile::encodeName( m_outputDir + "/links.html" ) );
  594. TQFile f( m_outputDir + "/empty.html" );
  595. TQString s;
  596. f.open( IO_WriteOnly | IO_Truncate );
  597. s = "<html><body>Follow the white rabbit";
  598. f.tqwriteBlock( s.latin1(), s.length() );
  599. f.close();
  600. f.setName( m_outputDir + "/index.html" );
  601. f.open( IO_WriteOnly | IO_Truncate );
  602. s = "<html><frameset cols=150,*><frame src=links.html><frame name=content src=empty.html>";
  603. f.tqwriteBlock( s.latin1(), s.length() );
  604. f.close();
  605. curr = this;
  606. }
  607. #include <tqobjectlist.h>
  608. static TQStringList readListFile( const TQString &filename )
  609. {
  610. // Read ignore file for this directory
  611. TQString ignoreFilename = filename;
  612. TQFileInfo ignoreInfo(ignoreFilename);
  613. TQStringList ignoreFiles;
  614. if (ignoreInfo.exists()) {
  615. TQFile ignoreFile(ignoreFilename);
  616. if (!ignoreFile.open(IO_ReadOnly)) {
  617. fprintf(stderr,"Can't open %s\n",ignoreFilename.latin1());
  618. exit(1);
  619. }
  620. TQTextStream ignoreStream(&ignoreFile);
  621. TQString line;
  622. while (!(line = ignoreStream.readLine()).isNull())
  623. ignoreFiles.append(line);
  624. ignoreFile.close();
  625. }
  626. return ignoreFiles;
  627. }
  628. RegressionTest::~RegressionTest()
  629. {
  630. // Important! Delete comparison config *first* as saver config
  631. // might point to the same physical file.
  632. delete m_failureComp;
  633. delete m_failureSave;
  634. }
  635. void RegressionTest::setFailureSnapshotConfig(TDEConfig *cfg, const TQString &sname)
  636. {
  637. Q_ASSERT(cfg);
  638. m_failureComp = cfg;
  639. m_failureComp->setGroup(sname);
  640. }
  641. void RegressionTest::setFailureSnapshotSaver(TDEConfig *cfg, const TQString &sname)
  642. {
  643. Q_ASSERT(cfg);
  644. m_failureSave = cfg;
  645. m_failureSave->setGroup(sname);
  646. }
  647. TQStringList RegressionTest::concatListFiles(const TQString &relPath, const TQString &filename)
  648. {
  649. TQStringList cmds;
  650. int pos = relPath.findRev('/');
  651. if (pos >= 0)
  652. cmds += concatListFiles(relPath.left(pos), filename);
  653. cmds += readListFile(m_baseDir + "/tests/" + relPath + "/" + filename);
  654. return cmds;
  655. }
  656. bool RegressionTest::runTests(TQString relPath, bool mustExist, int known_failure)
  657. {
  658. m_currentOutput = TQString::null;
  659. if (!TQFile(m_baseDir + "/tests/"+relPath).exists()) {
  660. fprintf(stderr,"%s: No such file or directory\n",relPath.latin1());
  661. return false;
  662. }
  663. TQString fullPath = m_baseDir + "/tests/"+relPath;
  664. TQFileInfo info(fullPath);
  665. if (!info.exists() && mustExist) {
  666. fprintf(stderr,"%s: No such file or directory\n",relPath.latin1());
  667. return false;
  668. }
  669. if (!info.isReadable() && mustExist) {
  670. fprintf(stderr,"%s: Access denied\n",relPath.latin1());
  671. return false;
  672. }
  673. if (info.isDir()) {
  674. TQStringList ignoreFiles = readListFile( m_baseDir + "/tests/"+relPath+"/ignore" );
  675. TQStringList failureFiles = readListFile( m_baseDir + "/tests/"+relPath+"/KNOWN_FAILURES" );
  676. // Run each test in this directory, recusively
  677. TQDir sourceDir(m_baseDir + "/tests/"+relPath);
  678. for (uint fileno = 0; fileno < sourceDir.count(); fileno++) {
  679. TQString filename = sourceDir[fileno];
  680. TQString relFilename = relPath.isEmpty() ? filename : relPath+"/"+filename;
  681. if (filename.startsWith(".") || ignoreFiles.contains(filename) )
  682. continue;
  683. int failure_type = NoFailure;
  684. if ( failureFiles.contains( filename ) )
  685. failure_type |= AllFailure;
  686. if ( failureFiles.contains ( filename + "-result" ) )
  687. failure_type |= ResultFailure;
  688. runTests(relFilename, false, failure_type);
  689. }
  690. }
  691. else if (info.isFile()) {
  692. TQString relativeDir = TQFileInfo(relPath).dirPath();
  693. TQString filename = info.fileName();
  694. m_currentBase = m_baseDir + "/tests/"+relativeDir;
  695. m_currentCategory = relativeDir;
  696. m_currentTest = filename;
  697. m_known_failures = known_failure;
  698. m_outputCustomised = false;
  699. // gather commands
  700. // directory-specific commands
  701. TQStringList commands = concatListFiles(relPath, ".kateconfig-commands");
  702. // testcase-specific commands
  703. commands += readListFile(m_currentBase + "/" + filename + "-commands");
  704. rereadConfig(); // reset options to default
  705. if ( filename.endsWith(".txt") ) {
  706. #if 0
  707. if ( relPath.startsWith( "domts/" ) && !m_runJS )
  708. return true;
  709. if ( relPath.startsWith( "ecma/" ) && !m_runJS )
  710. return true;
  711. #endif
  712. // if ( m_runHTML )
  713. testStaticFile(relPath, commands);
  714. }
  715. else if (mustExist) {
  716. fprintf(stderr,"%s: Not a valid test file (must be .txt)\n",relPath.latin1());
  717. return false;
  718. }
  719. } else if (mustExist) {
  720. fprintf(stderr,"%s: Not a regular file\n",relPath.latin1());
  721. return false;
  722. }
  723. return true;
  724. }
  725. void RegressionTest::createLink( const TQString& test, int failures )
  726. {
  727. createMissingDirs( m_outputDir + "/" + test + "-compare.html" );
  728. TQFile list( m_outputDir + "/links.html" );
  729. list.open( IO_WriteOnly|IO_Append );
  730. TQString link;
  731. link = TQString( "<a href=\"%1\" target=\"content\" title=\"%2\">" )
  732. .arg( test + "-compare.html" )
  733. .arg( test );
  734. link += m_currentTest;
  735. link += "</a> ";
  736. if (failures & NewFailure)
  737. link += "<span style=\"font-weight:bold;color:red\">";
  738. link += "[";
  739. if ( failures & ResultFailure )
  740. link += "R";
  741. link += "]";
  742. if (failures & NewFailure)
  743. link += "</span>";
  744. link += "<br>\n";
  745. list.tqwriteBlock( link.latin1(), link.length() );
  746. list.close();
  747. }
  748. /** returns the path in a way that is relatively reachable from base.
  749. * @param base base directory (must not include trailing slash)
  750. * @param path directory/file to be relatively reached by base
  751. * @return path with all elements replaced by .. and concerning path elements
  752. * to be relatively reachable from base.
  753. */
  754. static TQString makeRelativePath(const TQString &base, const TQString &path)
  755. {
  756. TQString absBase = TQFileInfo(base).absFilePath();
  757. TQString absPath = TQFileInfo(path).absFilePath();
  758. // kdDebug() << "absPath: \"" << absPath << "\"" << endl;
  759. // kdDebug() << "absBase: \"" << absBase << "\"" << endl;
  760. // walk up to common ancestor directory
  761. int pos = 0;
  762. do {
  763. pos++;
  764. int newpos = absBase.find('/', pos);
  765. if (newpos == -1) newpos = absBase.length();
  766. TQConstString cmpPathComp(absPath.unicode() + pos, newpos - pos);
  767. TQConstString cmpBaseComp(absBase.unicode() + pos, newpos - pos);
  768. // kdDebug() << "cmpPathComp: \"" << cmpPathComp.string() << "\"" << endl;
  769. // kdDebug() << "cmpBaseComp: \"" << cmpBaseComp.string() << "\"" << endl;
  770. // kdDebug() << "pos: " << pos << " newpos: " << newpos << endl;
  771. if (cmpPathComp.string() != cmpBaseComp.string()) { pos--; break; }
  772. pos = newpos;
  773. } while (pos < (int)absBase.length() && pos < (int)absPath.length());
  774. int basepos = pos < (int)absBase.length() ? pos + 1 : pos;
  775. int pathpos = pos < (int)absPath.length() ? pos + 1 : pos;
  776. // kdDebug() << "basepos " << basepos << " pathpos " << pathpos << endl;
  777. TQString rel;
  778. {
  779. TQConstString relBase(absBase.unicode() + basepos, absBase.length() - basepos);
  780. TQConstString relPath(absPath.unicode() + pathpos, absPath.length() - pathpos);
  781. // generate as many .. as there are path elements in relBase
  782. if (relBase.string().length() > 0) {
  783. for (int i = relBase.string().contains('/'); i > 0; --i)
  784. rel += "../";
  785. rel += "..";
  786. if (relPath.string().length() > 0) rel += "/";
  787. }
  788. rel += relPath.string();
  789. }
  790. return rel;
  791. }
  792. /** processes events for at least \c msec milliseconds */
  793. static void pause(int msec)
  794. {
  795. TQTime t;
  796. t.start();
  797. do {
  798. kapp->processEvents();
  799. } while (t.elapsed() < msec);
  800. }
  801. void RegressionTest::doFailureReport( const TQString& test, int failures )
  802. {
  803. if ( failures == NoFailure ) {
  804. ::unlink( TQFile::encodeName( m_outputDir + "/" + test + "-compare.html" ) );
  805. return;
  806. }
  807. createLink( test, failures );
  808. TQFile compare( m_outputDir + "/" + test + "-compare.html" );
  809. TQString testFile = TQFileInfo(test).fileName();
  810. TQString renderDiff;
  811. TQString domDiff;
  812. TQString relOutputDir = makeRelativePath(m_baseDir, m_outputDir);
  813. // are blocking reads possible with TDEProcess?
  814. char pwd[PATH_MAX];
  815. (void) getcwd( pwd, PATH_MAX );
  816. chdir( TQFile::encodeName( m_baseDir ) );
  817. if ( failures & ResultFailure ) {
  818. domDiff += "<pre>";
  819. FILE *pipe = popen( TQString::fromLatin1( "diff -u baseline/%1-result %3/%2-result" )
  820. .arg ( test, test, relOutputDir ).latin1(), "r" );
  821. TQTextIStream *is = new TQTextIStream( pipe );
  822. for ( int line = 0; line < 100 && !is->eof(); ++line ) {
  823. TQString line = is->readLine();
  824. line = line.replace( '<', "&lt;" );
  825. line = line.replace( '>', "&gt;" );
  826. domDiff += line + "\n";
  827. }
  828. delete is;
  829. pclose( pipe );
  830. domDiff += "</pre>";
  831. }
  832. chdir( pwd );
  833. // create a relative path so that it works via web as well. ugly
  834. TQString relpath = makeRelativePath(m_outputDir + "/"
  835. + TQFileInfo(test).dirPath(), m_baseDir);
  836. compare.open( IO_WriteOnly|IO_Truncate );
  837. TQString cl;
  838. cl = TQString( "<html><head><title>%1</title>" ).arg( test );
  839. cl += TQString( "<script>\n"
  840. "var pics = new Array();\n"
  841. "pics[0]=new Image();\n"
  842. "pics[0].src = '%1';\n"
  843. "pics[1]=new Image();\n"
  844. "pics[1].src = '%2';\n"
  845. "var doflicker = 1;\n"
  846. "var t = 1;\n"
  847. "var lastb=0;\n" )
  848. .arg( relpath+"/baseline/"+test+"-dump.png" )
  849. .arg( testFile+"-dump.png" );
  850. cl += TQString( "function toggleVisible(visible) {\n"
  851. " document.getElementById('render').style.visibility= visible == 'render' ? 'visible' : 'hidden';\n"
  852. " document.getElementById('image').style.visibility= visible == 'image' ? 'visible' : 'hidden';\n"
  853. " document.getElementById('dom').style.visibility= visible == 'dom' ? 'visible' : 'hidden';\n"
  854. "}\n"
  855. "function show() { document.getElementById('image').src = pics[t].src; "
  856. "document.getElementById('image').style.borderColor = t && !doflicker ? 'red' : 'gray';\n"
  857. "toggleVisible('image');\n"
  858. "}" );
  859. cl += TQString ( "function runSlideShow(){\n"
  860. " document.getElementById('image').src = pics[t].src;\n"
  861. " if (doflicker)\n"
  862. " t = 1 - t;\n"
  863. " setTimeout('runSlideShow()', 200);\n"
  864. "}\n"
  865. "function m(b) { if (b == lastb) return; document.getElementById('b'+b).className='buttondown';\n"
  866. " var e = document.getElementById('b'+lastb);\n"
  867. " if(e) e.className='button';\n"
  868. " lastb = b;\n"
  869. "}\n"
  870. "function showRender() { doflicker=0;toggleVisible('render')\n"
  871. "}\n"
  872. "function showDom() { doflicker=0;toggleVisible('dom')\n"
  873. "}\n"
  874. "</script>\n");
  875. cl += TQString ("<style>\n"
  876. ".buttondown { cursor: pointer; padding: 0px 20px; color: white; background-color: blue; border: inset blue 2px;}\n"
  877. ".button { cursor: pointer; padding: 0px 20px; color: black; background-color: white; border: outset blue 2px;}\n"
  878. ".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"
  879. "</style>\n" );
  880. cl += TQString( "<body onload=\"m(5); toggleVisible('dom');\"" );
  881. cl += TQString(" text=black bgcolor=gray>\n<h1>%3</h1>\n" ).arg( test );
  882. if ( renderDiff.length() )
  883. cl += "<span id='b4' class='button' onclick='showRender();m(4)'>R-DIFF</span>&nbsp;\n";
  884. if ( domDiff.length() )
  885. cl += "<span id='b5' class='button' onclick='showDom();m(5);'>D-DIFF</span>&nbsp;\n";
  886. // The test file always exists - except for checkOutput called from *.js files
  887. if ( TQFile::exists( m_baseDir + "/tests/"+ test ) )
  888. cl += TQString( "<a class=button href=\"%1\">HTML</a>&nbsp;" )
  889. .arg( relpath+"/tests/"+test );
  890. cl += TQString( "<hr>"
  891. "<img style='border: solid 5px gray' src=\"%1\" id='image'>" )
  892. .arg( relpath+"/baseline/"+test+"-dump.png" );
  893. cl += "<div id='render' class='diff'>" + renderDiff + "</div>";
  894. cl += "<div id='dom' class='diff'>" + domDiff + "</div>";
  895. cl += "</body></html>";
  896. compare.tqwriteBlock( cl.latin1(), cl.length() );
  897. compare.close();
  898. }
  899. void RegressionTest::testStaticFile(const TQString & filename, const TQStringList &commands)
  900. {
  901. tqApp->mainWidget()->resize( 800, 600); // restore size
  902. // Set arguments
  903. KParts::URLArgs args;
  904. if (filename.endsWith(".txt")) args.serviceType = "text/plain";
  905. m_part->browserExtension()->setURLArgs(args);
  906. // load page
  907. KURL url;
  908. url.setProtocol("file");
  909. url.setPath(TQFileInfo(m_baseDir + "/tests/"+filename).absFilePath());
  910. m_part->openURL(url);
  911. // inject commands
  912. for (TQStringList::ConstIterator cit = commands.begin(); cit != commands.end(); ++cit) {
  913. TQString str = (*cit).stripWhiteSpace();
  914. if (str.isEmpty() || str.startsWith("#")) continue;
  915. Kate::Command *cmd = KateCmd::self()->queryCommand(str);
  916. if (cmd) {
  917. TQString msg;
  918. if (!cmd->exec(m_view, str, msg))
  919. fprintf(stderr, "ERROR executing command '%s': %s\n", str.latin1(), msg.latin1());
  920. }
  921. }
  922. pause(200);
  923. Q_ASSERT(m_part->config()->configFlags() & KateDocumentConfig::cfDoxygenAutoTyping);
  924. bool script_error = false;
  925. {
  926. // Execute script
  927. TestJScriptEnv jsenv(m_part);
  928. jsenv.output()->setChangedFlag(&m_outputCustomised);
  929. jsenv.output()->setOutputString(&m_outputString);
  930. script_error = evalJS(jsenv.interpreter(), m_baseDir + "/tests/"+TQFileInfo(filename).dirPath()+"/.kateconfig-script", true)
  931. && evalJS(jsenv.interpreter(), m_baseDir + "/tests/"+filename+"-script");
  932. }
  933. int back_known_failures = m_known_failures;
  934. if (!script_error) goto bail_out;
  935. if (m_showGui) kapp->processEvents();
  936. if ( m_genOutput ) {
  937. reportResult(checkOutput(filename+"-result"), "result");
  938. } else {
  939. int failures = NoFailure;
  940. // compare with output file
  941. if ( m_known_failures & ResultFailure)
  942. m_known_failures = AllFailure;
  943. bool newfail;
  944. if ( !reportResult( checkOutput(filename+"-result"), "result", &newfail ) )
  945. failures |= ResultFailure;
  946. if (newfail)
  947. failures |= NewFailure;
  948. doFailureReport(filename, failures );
  949. }
  950. bail_out:
  951. m_known_failures = back_known_failures;
  952. m_part->setModified(false);
  953. m_part->closeURL();
  954. }
  955. bool RegressionTest::evalJS(Interpreter &interp, const TQString &filename, bool ignore_nonexistent)
  956. {
  957. TQString fullSourceName = filename;
  958. TQFile sourceFile(fullSourceName);
  959. if (!sourceFile.open(IO_ReadOnly)) {
  960. if (!ignore_nonexistent) {
  961. fprintf(stderr,"ERROR reading file %s\n",fullSourceName.latin1());
  962. m_errors++;
  963. }
  964. return ignore_nonexistent;
  965. }
  966. TQTextStream stream ( &sourceFile );
  967. stream.setEncoding( TQTextStream::UnicodeUTF8 );
  968. TQString code = stream.read();
  969. sourceFile.close();
  970. saw_failure = false;
  971. ignore_errors = false;
  972. Completion c = interp.evaluate(UString( code ) );
  973. if ( /*report_result &&*/ !ignore_errors) {
  974. if (c.complType() == Throw) {
  975. TQString errmsg = c.value().toString(interp.globalExec()).qstring();
  976. printf( "ERROR: %s (%s)\n",filename.latin1(), errmsg.latin1());
  977. m_errors++;
  978. return false;
  979. }
  980. }
  981. return true;
  982. }
  983. class GlobalImp : public ObjectImp {
  984. public:
  985. virtual UString className() const { return "global"; }
  986. };
  987. RegressionTest::CheckResult RegressionTest::checkOutput(const TQString &againstFilename)
  988. {
  989. TQString absFilename = TQFileInfo(m_baseDir + "/baseline/" + againstFilename).absFilePath();
  990. if ( svnIgnored( absFilename ) ) {
  991. m_known_failures = NoFailure;
  992. return Ignored;
  993. }
  994. CheckResult result = Success;
  995. // compare result to existing file
  996. TQString outputFilename = TQFileInfo(m_outputDir + "/" + againstFilename).absFilePath();
  997. bool kf = false;
  998. if ( m_known_failures & AllFailure )
  999. kf = true;
  1000. if ( kf )
  1001. outputFilename += "-KF";
  1002. if ( m_genOutput )
  1003. outputFilename = absFilename;
  1004. // get existing content
  1005. TQString data;
  1006. if (m_outputCustomised) {
  1007. data = m_outputString;
  1008. } else {
  1009. data = m_part->text();
  1010. }
  1011. TQFile file(absFilename);
  1012. if (file.open(IO_ReadOnly)) {
  1013. TQTextStream stream ( &file );
  1014. stream.setEncoding( TQTextStream::UnicodeUTF8 );
  1015. TQString fileData = stream.read();
  1016. result = ( fileData == data ) ? Success : Failure;
  1017. if ( !m_genOutput && result == Success && !m_keepOutput ) {
  1018. ::unlink( TQFile::encodeName( outputFilename ) );
  1019. return Success;
  1020. }
  1021. } else if (!m_genOutput) {
  1022. fprintf(stderr, "Error reading file %s\n", absFilename.latin1());
  1023. result = Failure;
  1024. }
  1025. // generate result file
  1026. createMissingDirs( outputFilename );
  1027. TQFile file2(outputFilename);
  1028. if (!file2.open(IO_WriteOnly)) {
  1029. fprintf(stderr,"Error writing to file %s\n",outputFilename.latin1());
  1030. exit(1);
  1031. }
  1032. TQTextStream stream2(&file2);
  1033. stream2.setEncoding( TQTextStream::UnicodeUTF8 );
  1034. stream2 << data;
  1035. if ( m_genOutput )
  1036. printf("Generated %s\n", outputFilename.latin1());
  1037. return result;
  1038. }
  1039. void RegressionTest::rereadConfig()
  1040. {
  1041. m_baseConfig->setGroup("Kate Document Defaults");
  1042. m_part->config()->readConfig(m_baseConfig);
  1043. m_baseConfig->setGroup("Kate View Defaults");
  1044. m_view->config()->readConfig(m_baseConfig);
  1045. }
  1046. bool RegressionTest::reportResult(CheckResult result, const TQString & description, bool *newfail)
  1047. {
  1048. if ( result == Ignored ) {
  1049. //printf("IGNORED: ");
  1050. //printDescription( description );
  1051. return true; // no error
  1052. } else
  1053. return reportResult( result == Success, description, newfail );
  1054. }
  1055. bool RegressionTest::reportResult(bool passed, const TQString & description, bool *newfail)
  1056. {
  1057. if (newfail) *newfail = false;
  1058. if (m_genOutput)
  1059. return true;
  1060. TQString filename(m_currentTest + "-" + description);
  1061. if (!m_currentCategory.isEmpty())
  1062. filename = m_currentCategory + "/" + filename;
  1063. const bool oldfailed = m_failureComp && m_failureComp->readNumEntry(filename);
  1064. if (passed) {
  1065. if ( m_known_failures & AllFailure ) {
  1066. printf("PASS (unexpected!)");
  1067. m_passes_fail++;
  1068. } else {
  1069. printf("PASS");
  1070. m_passes_work++;
  1071. }
  1072. if (oldfailed) {
  1073. printf(" (new)");
  1074. m_passes_new++;
  1075. }
  1076. if (m_failureSave)
  1077. m_failureSave->deleteEntry(filename);
  1078. }
  1079. else {
  1080. if ( m_known_failures & AllFailure ) {
  1081. printf("FAIL (known)");
  1082. m_failures_fail++;
  1083. passed = true; // we knew about it
  1084. } else {
  1085. printf("FAIL");
  1086. m_failures_work++;
  1087. }
  1088. if (!oldfailed && m_failureComp) {
  1089. printf(" (new)");
  1090. m_failures_new++;
  1091. if (newfail) *newfail = true;
  1092. }
  1093. if (m_failureSave)
  1094. m_failureSave->writeEntry(filename, 1);
  1095. }
  1096. printf(": ");
  1097. printDescription( description );
  1098. return passed;
  1099. }
  1100. void RegressionTest::printDescription(const TQString& description)
  1101. {
  1102. if (!m_currentCategory.isEmpty())
  1103. printf("%s/", m_currentCategory.latin1());
  1104. printf("%s", m_currentTest.latin1());
  1105. if (!description.isEmpty()) {
  1106. TQString desc = description;
  1107. desc.replace( '\n', ' ' );
  1108. printf(" [%s]", desc.latin1());
  1109. }
  1110. printf("\n");
  1111. fflush(stdout);
  1112. }
  1113. void RegressionTest::createMissingDirs(const TQString & filename)
  1114. {
  1115. TQFileInfo dif(filename);
  1116. TQFileInfo dirInfo( dif.dirPath() );
  1117. if (dirInfo.exists())
  1118. return;
  1119. TQStringList pathComponents;
  1120. TQFileInfo parentDir = dirInfo;
  1121. pathComponents.prepend(parentDir.absFilePath());
  1122. while (!parentDir.exists()) {
  1123. TQString parentPath = parentDir.absFilePath();
  1124. int slashPos = parentPath.findRev('/');
  1125. if (slashPos < 0)
  1126. break;
  1127. parentPath = parentPath.left(slashPos);
  1128. pathComponents.prepend(parentPath);
  1129. parentDir = TQFileInfo(parentPath);
  1130. }
  1131. for (uint pathno = 1; pathno < pathComponents.count(); pathno++) {
  1132. if (!TQFileInfo(pathComponents[pathno]).exists() &&
  1133. !TQDir(pathComponents[pathno-1]).mkdir(pathComponents[pathno])) {
  1134. fprintf(stderr,"Error creating directory %s\n",pathComponents[pathno].latin1());
  1135. exit(1);
  1136. }
  1137. }
  1138. }
  1139. void RegressionTest::slotOpenURL(const KURL &url, const KParts::URLArgs &args)
  1140. {
  1141. m_part->browserExtension()->setURLArgs( args );
  1142. m_part->openURL(url);
  1143. }
  1144. bool RegressionTest::svnIgnored( const TQString &filename )
  1145. {
  1146. TQFileInfo fi( filename );
  1147. TQString ignoreFilename = fi.dirPath() + "/svnignore";
  1148. TQFile ignoreFile(ignoreFilename);
  1149. if (!ignoreFile.open(IO_ReadOnly))
  1150. return false;
  1151. TQTextStream ignoreStream(&ignoreFile);
  1152. TQString line;
  1153. while (!(line = ignoreStream.readLine()).isNull()) {
  1154. if ( line == fi.fileName() )
  1155. return true;
  1156. }
  1157. ignoreFile.close();
  1158. return false;
  1159. }
  1160. void RegressionTest::resizeTopLevelWidget( int w, int h )
  1161. {
  1162. tqApp->mainWidget()->resize( w, h );
  1163. // Since we're not visible, this doesn't have an immediate effect, TQWidget posts the event
  1164. TQApplication::sendPostedEvents( 0, TQEvent::Resize );
  1165. }
  1166. #include "test_regression.moc"
  1167. // kate: indent-width 4