From 44db2be195b38316e238eddad80b816ab96b6058 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 7 Oct 2014 15:51:02 +0200 Subject: [PATCH] Provide basic test runner and output pane --- plugins/autotest/autotest.pro | 16 +- plugins/autotest/autotest.qrc | 11 + plugins/autotest/autotestplugin.cpp | 5 + plugins/autotest/images/debug.png | Bin 0 -> 621 bytes plugins/autotest/images/fail.png | Bin 0 -> 600 bytes plugins/autotest/images/fatal.png | Bin 0 -> 597 bytes plugins/autotest/images/leafsort.png | Bin 0 -> 415 bytes plugins/autotest/images/pass.png | Bin 0 -> 595 bytes plugins/autotest/images/run.png | Bin 0 -> 509 bytes plugins/autotest/images/runselected.png | Bin 0 -> 709 bytes plugins/autotest/images/skip.png | Bin 0 -> 368 bytes plugins/autotest/images/warn.png | Bin 0 -> 621 bytes plugins/autotest/images/xfail.png | Bin 0 -> 547 bytes plugins/autotest/images/xpass.png | Bin 0 -> 599 bytes plugins/autotest/testcodeparser.cpp | 5 +- plugins/autotest/testcodeparser.h | 2 +- plugins/autotest/testconfiguration.cpp | 71 +++++ plugins/autotest/testconfiguration.h | 78 +++++ plugins/autotest/testinfo.cpp | 5 + plugins/autotest/testinfo.h | 1 + plugins/autotest/testresult.cpp | 101 +++++++ plugins/autotest/testresult.h | 79 +++++ plugins/autotest/testresultdelegate.cpp | 244 ++++++++++++++++ plugins/autotest/testresultdelegate.h | 110 +++++++ plugins/autotest/testresultmodel.cpp | 163 +++++++++++ plugins/autotest/testresultmodel.h | 66 +++++ plugins/autotest/testresultspane.cpp | 216 ++++++++++++++ plugins/autotest/testresultspane.h | 91 ++++++ plugins/autotest/testrunner.cpp | 371 ++++++++++++++++++++++++ plugins/autotest/testrunner.h | 74 +++++ plugins/autotest/testtreemodel.cpp | 134 ++++++++- plugins/autotest/testtreemodel.h | 4 + plugins/autotest/testtreeview.cpp | 52 +++- plugins/autotest/testtreeview.h | 4 + 34 files changed, 1877 insertions(+), 26 deletions(-) create mode 100644 plugins/autotest/images/debug.png create mode 100644 plugins/autotest/images/fail.png create mode 100644 plugins/autotest/images/fatal.png create mode 100644 plugins/autotest/images/leafsort.png create mode 100644 plugins/autotest/images/pass.png create mode 100644 plugins/autotest/images/run.png create mode 100644 plugins/autotest/images/runselected.png create mode 100644 plugins/autotest/images/skip.png create mode 100644 plugins/autotest/images/warn.png create mode 100644 plugins/autotest/images/xfail.png create mode 100644 plugins/autotest/images/xpass.png create mode 100644 plugins/autotest/testconfiguration.cpp create mode 100644 plugins/autotest/testconfiguration.h create mode 100644 plugins/autotest/testresult.cpp create mode 100644 plugins/autotest/testresult.h create mode 100644 plugins/autotest/testresultdelegate.cpp create mode 100644 plugins/autotest/testresultdelegate.h create mode 100644 plugins/autotest/testresultmodel.cpp create mode 100644 plugins/autotest/testresultmodel.h create mode 100644 plugins/autotest/testresultspane.cpp create mode 100644 plugins/autotest/testresultspane.h create mode 100644 plugins/autotest/testrunner.cpp create mode 100644 plugins/autotest/testrunner.h diff --git a/plugins/autotest/autotest.pro b/plugins/autotest/autotest.pro index 19df6bcb2eb..566c6131d12 100644 --- a/plugins/autotest/autotest.pro +++ b/plugins/autotest/autotest.pro @@ -15,7 +15,13 @@ SOURCES += \ testvisitor.cpp \ testinfo.cpp \ testcodeparser.cpp \ - autotestplugin.cpp + autotestplugin.cpp \ + testrunner.cpp \ + testconfiguration.cpp \ + testresult.cpp \ + testresultspane.cpp \ + testresultmodel.cpp \ + testresultdelegate.cpp HEADERS += \ testtreeview.h \ @@ -26,7 +32,13 @@ HEADERS += \ testcodeparser.h \ autotestplugin.h \ autotest_global.h \ - autotestconstants.h + autotestconstants.h \ + testrunner.h \ + testconfiguration.h \ + testresult.h \ + testresultspane.h \ + testresultmodel.h \ + testresultdelegate.h RESOURCES += \ autotest.qrc diff --git a/plugins/autotest/autotest.qrc b/plugins/autotest/autotest.qrc index 53dee42e310..b4b47f100f6 100644 --- a/plugins/autotest/autotest.qrc +++ b/plugins/autotest/autotest.qrc @@ -5,5 +5,16 @@ images/expand.png images/collapse.png images/sort.png + images/leafsort.png + images/debug.png + images/fail.png + images/fatal.png + images/pass.png + images/skip.png + images/warn.png + images/xfail.png + images/xpass.png + images/run.png + images/runselected.png diff --git a/plugins/autotest/autotestplugin.cpp b/plugins/autotest/autotestplugin.cpp index b0da2c1cac8..7dcb029d905 100644 --- a/plugins/autotest/autotestplugin.cpp +++ b/plugins/autotest/autotestplugin.cpp @@ -18,8 +18,10 @@ #include "autotestplugin.h" #include "autotestconstants.h" +#include "testrunner.h" #include "testtreeview.h" #include "testtreemodel.h" +#include "testresultspane.h" #include #include @@ -48,6 +50,8 @@ AutotestPlugin::~AutotestPlugin() // Delete members TestTreeModel *model = TestTreeModel::instance(); delete model; + TestRunner *runner = TestRunner::instance(); + delete runner; } bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorString) @@ -74,6 +78,7 @@ bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorStri Core::ActionManager::actionContainer(Core::Constants::M_TOOLS)->addMenu(menu); addAutoReleasedObject(new TestViewFactory); + addAutoReleasedObject(TestResultsPane::instance()); return true; } diff --git a/plugins/autotest/images/debug.png b/plugins/autotest/images/debug.png new file mode 100644 index 0000000000000000000000000000000000000000..e18bd157e4a53187cb30ea2ec9b3a948c8445ec0 GIT binary patch literal 621 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4rT@h1`S>QUm z*zlT}=>-N#xw%Prc!=BC$+)^og@ouvM#@J;DW#@b6c#EcCCNob$|onQ=HytHmZ}#Q ztCyE+R8(k`mb$jLs}&Y%R#k;`cB&T^#!Q~9T~%e-+^kVq2@zK>E7Pv6)vB-8s;$+m zt##{Ybc=__s6)VD5u8dy2I&s~)r1k4lHf+e)v?*)z=A6x& zbGK~C+q$)2+qRBWmHFJE4F?b_CxH+S5=z3cYv-FNQnyLa!_unIyQw9tS3~N1I977}|Sr0n;9dck`IB>JfJMut) zu)oyflmF|F>Q@*F2`hF)YIUv|VVy;AjiPR7f4^RsE!^M5V> z6uWg#&f1_`%kKUC=$>qUp)B_L(S5Zyvc9a?Iwi=fHZGfIM)A4FZ_7?w2OgcWSkLG4 z+AhEH9kap`Pt}WGj9~t1er&^&$}6W{v!u=wZ(rQj&to8$5qYjqHela9aVCjhiI%c-SRB7Bd2n#H`qyid!yWfCpKWfuXv(AdL$_xM2%eYS zk#*ptL-p^E+ak`a`u#V)?&i1O$5&o?9s7E=wuO380*l(@f`w~0Prh(~LDtrGi|b+w zr9U3a<9*Jby<5F?+qAo(2X}ZpvZ?8Cj4|nT$k$((GAW_nepjNxtYe+~_GLdT`0-+8 z#+NTX8zXKo@*Ga#dBpHworE}T;`C?Ft(n(%{Ste<*3A|~)9_-ulW#OI;4!YA1OYe|FDkU*11u*V}tc2hw-UZe41hd+xyZ@(&5s@0c!o>F#gb{MlgZ_3uRve_PUn z4Zel0yi^wV?N_g`Qg~J366QY3gz&^NPCL8sSGlim**4a9-cQTP_wuAA`@Vnrba?ugeXp;$|6&fYxotb=^TRvwPMSymyA;$raV$K0NLO~5 z^28H6Gpz0^%KXbH-JL&Av|FG5%6C5l#UmRZ8{{9D`md3(XQSbyn>v3_s&E|K9`y3% zqfejyevgix$}Aispk&XP|9HY>CdQz{hb3B>ninrV!pp2HpkQlM6n*{c<;f?X9Mzwe z`i`;hP>oCx!^M!U9f=dpJeANA@p%3@s9aC_n5w+TfspqHH8ejiae4Pf!CJP?l27+^ z((%%XclBlkMXAZm`?S4)<=i)>yv2^Uw`E?*`V$j>{Iks#2ZrTA2Umt=Mv5t3w|&Yi zbs}y-?E2*VJfXg$3>jClID8l9-kz~ksBEXtVG~>V7l+gnwu#@2i)LP0Alq&i>(0@5 zJLge|R*rbL$ceCIvtl)IjqSU$<$o-=@{8+C&Y|wZQnmrnAD(ERsS7`K z?`-$sy2o1t%!3Q;SkgsGtT%+Tx3Vscd%xaRz@qLo9liPTuRy>?m;b ze#(0#!y`%xTy-L8t0zS1T<+|RSR%->HfPOSrlzE|y;E7D|1)n4;@&Oe(RHFwI!MAZ zd!mVZ-hAIC@y^bRjk;y`XW!>D?wN3;UdeE2yy@(-t@eyPhNm3`Y}^7vW{FKOI5vf0 zPsa(XhN(MV{F(CfcipkVImwgQ88$JTV%XBL#&6Aniy0Y9L*_m)J2}~aM||tG=?wQg z^nBMGh^kgr@{f95%Mkd-S&;L1*n`S>+Dg(&MT`nr+i!0+tn7PoJiMX_wU#j-{ ztqt=o-yN&Z(R6p;9yO)+$&5d1_AxJ5IOUet^2^s5p2c3@yZZX;NQT)A?;ih~Xe%%= zc#`NOz6R~=T*a9_^Oy~G#^iAr6mcnix^-QrK{@-a^ZrWfzuWcSCy3YFKd-Tri@~J9 zm|^?cu+=d-VsF1MnaGfQMy{c&!^G1rdo91A&7q5qLMnl4!(u0O9{juA{on3$^Pm6P WcC~k^VmAW=1B0ilpUXO@geCw!YOzuP literal 0 HcmV?d00001 diff --git a/plugins/autotest/images/pass.png b/plugins/autotest/images/pass.png new file mode 100644 index 0000000000000000000000000000000000000000..33dbe44649ee7fd1fb99c57d79882cd52d16f877 GIT binary patch literal 595 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!toq&!_5Lo9liPCo4&QYdoV zKK{sY0xVp`EZIc3%V~5-g zy_05oS_10F*^R}Y-K}^(x4M39$uSWwo8840&ktADx@sOx)cH~F;KFpKpk3}<>*0r+ z3vJ)MnD_7K-J<)c^V06UJDva5Vcm(qa|YgB*|AmP&a%#5Tz_?JJucAlrs&(zPrsl3 zj$XgL=(p8`)sr3>$#+b?;Ii2MlK(9a;|!jpHXoCV9(}6Idu9|WmD!ao+!3JjM)sk} z<|CdAC9m(!NSy4V;&{tra@R5rwx%`LuYK(Qd3^2bYsm?03JeODizYMXz1iRt)!|{_ z_T*G~GOfy+Iw|v6PvuXPk7Lf>rlgvv}`A;P;-Q%=xp872@SAkb?bDLrt*yN;Z?tS+7_FIyqIqh@E#`=1|d3-&ju{B!4I5OCU(zCKpt z3xCOcYdwXX;WPKwTedIJh}&yw5k2Q_gY4b|rRL^|**Ry}^ z3##%pvg1|IkVe)?R8#LF26qn=uN&Sqek#IvR=R`#9i zgtO0f@iZ#@YrT*+VN1P#!vT$BZikGyF6OO|d>uVIf@j&rn7Il&?@BQ;F1EN*w11<> z)eraL9Rls1@_${s^)I`C)ZIVJvke12*gpz>t7;#@_4}kQgM$gf>Z?N6R^@LDsdT&~ zlaj~S@MD$Av!=?Lu>PN_cWlnwD43|g`Q%pE#Nu_=-@Dr`+IP;0gTe~DWM4f{&?&L literal 0 HcmV?d00001 diff --git a/plugins/autotest/images/runselected.png b/plugins/autotest/images/runselected.png new file mode 100644 index 0000000000000000000000000000000000000000..328974062effb73ad838265bb8baaf0839525033 GIT binary patch literal 709 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!todOTemLo9liPLB7O94K*Y z{$m;4%}c+ee?FfQ5y;ZzGGUS+XRn)Y(<0qho4WI6U2whIBfzrQ(X(l~Xs1h^N0Q)G zjCgF9EDXnH z?0lQ@*|^TKI9u_P`mWGK1%VmcZsts7IPiIoqd?1w%P;rzn6g?|x7&Wa_dGml?({wz zrT|};8+)EVe||JZ&)wVGJ7xRrmHYSGd%3W(GHjW%?$ScN>c6eRTkplXXUm>VpC#vR zuVbg8B+Jm&*0!znaN?09!!2939C%v9$q=xS?ZVGJKAfh_qNf%e%(cE#wf^e<|IZG- zob_#1H^YUych|N!DMqXfE0AGde9=R-VTn?p(@BMeX%hleK7>|=mEOITxa+b>(w=SK zOSRNDUA!3R&6?op;m3T?41o*xi3()gb#_{&%k3{^f=%PdzY|^bB?Ln(RHliGk$^D@(Y$f#x@% ziD8TkE-p=KuQ^?#q!okj@i1sHHtl2b{O#y+D~(@a1*`CMQBKKN&DVDgS1*X50p5C|@Jw4Yv6Zm&6Cmf*ciw^!9Z**H6MrK#e!si|4)C(kbl zrG^bUhV#HPfgCnyLUgIp8s!Z ae#;D7rPd$vXBZe57(8A5T-G@yGywq9U!=GI literal 0 HcmV?d00001 diff --git a/plugins/autotest/images/warn.png b/plugins/autotest/images/warn.png new file mode 100644 index 0000000000000000000000000000000000000000..a813c1dc9a6478e754aa3f62c40e14bf0f7c814c GIT binary patch literal 621 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!toOgvp2Lo9liPWJYg5-4)K z{(JQqZ)sggmv+VYr;8T|O*(Lb>voGk)0YKiiY$Vn8Z4@7Z;1%*SQW-9rg24WQV)+) z*7Q`bwN(==BUc*C>N39n_g(qzC&zy@bp0z3`(ALa|ML#_jI}@IBTvgZaXfzI%BS9S zQ?j$=HP7w?g>xR9P6@KJy=(ZZz_q4~^?3A=w%Y0!cS26qU5@is2x*hmI=1E0N1KBu zZa=xZZ(sRa^IiM=*Q|K(L@=;u#mscq)+4P38=E;>?-vi_UxKe?3B{$s6D6Ee@Btfsjn~-oI16wDr>L#=8G3ujW}DNu4(1sZ)={WS>bYo>MeV1y9*OWxn|I z?(*&P^KLT>uVB|YoEzqu{PM*TkBQ3e({$9-b}gH1H*sH{#4(>QQD1Vtx^Dec-cmWu zBbl+sxUaLb)wNGX(ujp)!$G!tiSIi$zp!BvV>Dsdx=7FP@E`eW9Lg;mf@(dTp7U&i zeI_b6ws2h2e7)@F4V&xC6OzyEpK&gKYv$!eu5+zISD9o!dZfhe!Y#*p?yGW(#XI&l z8{K=Q`1oG0dHitU;^gGbhyBE-W%zw(?CO)>a9S@q$MBd010SE1lxfx!&CMs(L|0bS zIyA38P-&miYs#|FPnMavb&(^p^lTwjtFq+`4IKx##6M0*WwpO-v;UHfkGBkQIAXLXD0a7tlNP%3EGS-nS2>iE&e%%|@t zy!*Z)@%oB+D_NIWb{;fjnXbj!RWl{}Q`v&@{m=h!H%IX?tu&5(#=yY9;OXk;vd$@? F2>^vX?UMii literal 0 HcmV?d00001 diff --git a/plugins/autotest/images/xpass.png b/plugins/autotest/images/xpass.png new file mode 100644 index 0000000000000000000000000000000000000000..964bb4a181e56ac6bb3c5b60fdfaab16755f9f3f GIT binary patch literal 599 zcmeAS@N?(olHy`uVBq!ia0y~yU=RRd4mJh`2Kmqb6B!to{$Gm-I&?lq=ic2r=kwz`{mN0<|J2v4l~zi!Y51IJ zA=T@~ADNrGF8g-;bDQ^%-{yRKyY0_e*%*LdUEWSQEBwyY#@qWLeOSnLK6X=1Gw|xVd>)4({VV`pEf#Rs&-}{F66%7MmL- zCvvz23#Szu^Y%F}S>ONSfdcQ_w-KEz&MU7>mF_FHVYy@wQ*P|FaH`X9yT;dxR)vP_ z>{)m9!o2pSTMZL#AATrUb}%6J?<=iUK?n843ZFk$dK2dbufW+e59*5;BpJH2B-G084>`pARe0Z(+9l7G)_*@> z`A)Y2e7%-X_8+>2>C~`&>^LMjriXTE={|qm9B;E7gB#!Hgh)p$(autoTestRootIndex.internalPointer()); autoTestRootItem->removeChildren(); m_model->endResetModel(); - ProjectExplorer::SessionManager *session = static_cast( - ProjectExplorer::SessionManager::instance()); + ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance(); if (!session || !session->hasProjects()) return; @@ -196,10 +195,10 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr doc) m_cppDocMap.insert(file, new TestInfo(tc, privSlots.keys(), doc->revision(), doc->editorRevision())); + delete info; break; } } - delete info; delete ttItem; } else { m_model->beginInsertRows(autoTestRootIndex, autoTestRootItem->childCount(), autoTestRootItem->childCount()); diff --git a/plugins/autotest/testcodeparser.h b/plugins/autotest/testcodeparser.h index 0ef4eedc333..1eaed0b063a 100644 --- a/plugins/autotest/testcodeparser.h +++ b/plugins/autotest/testcodeparser.h @@ -38,7 +38,7 @@ class TestCodeParser : public QObject { Q_OBJECT public: - explicit TestCodeParser(/*QObject*/TestTreeModel *parent = 0); + explicit TestCodeParser(TestTreeModel *parent = 0); signals: diff --git a/plugins/autotest/testconfiguration.cpp b/plugins/autotest/testconfiguration.cpp new file mode 100644 index 00000000000..22037989233 --- /dev/null +++ b/plugins/autotest/testconfiguration.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "testconfiguration.h" + +#include + +namespace Autotest { +namespace Internal { + +TestConfiguration::TestConfiguration(const QString &testClass, const QStringList &testCases, + QObject *parent) + : QObject(parent), + m_testClass(testClass), + m_testCases(testCases), + m_project(0) +{ +} + +TestConfiguration::~TestConfiguration() +{ + m_testCases.clear(); +} + +void TestConfiguration::setTargetFile(const QString &targetFile) +{ + m_targetFile = targetFile; +} + +void TestConfiguration::setTargetName(const QString &targetName) +{ + m_targetName = targetName; +} + +void TestConfiguration::setProFile(const QString &proFile) +{ + m_proFile = proFile; +} + +void TestConfiguration::setWorkingDirectory(const QString &workingDirectory) +{ + m_workingDir = workingDirectory; +} + +void TestConfiguration::setEnvironment(const Utils::Environment &env) +{ + m_environment = env; +} + +void TestConfiguration::setProject(ProjectExplorer::Project *project) +{ + m_project = project; +} + +} // namespace Internal +} // namespace Autotest diff --git a/plugins/autotest/testconfiguration.h b/plugins/autotest/testconfiguration.h new file mode 100644 index 00000000000..fa9545157b4 --- /dev/null +++ b/plugins/autotest/testconfiguration.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef TESTCONFIGURATION_H +#define TESTCONFIGURATION_H + +#include + +#include +#include + +namespace ProjectExplorer { +class Project; +} + +namespace Autotest { +namespace Internal { + +class TestConfiguration : public QObject + +{ + Q_OBJECT +public: + explicit TestConfiguration(const QString &testClass, const QStringList &testCases, + QObject *parent = 0); + ~TestConfiguration(); + + void setTargetFile(const QString &targetFile); + void setTargetName(const QString &targetName); + void setProFile(const QString &proFile); + void setWorkingDirectory(const QString &workingDirectory); + void setEnvironment(const Utils::Environment &env); + void setProject(ProjectExplorer::Project *project); + + QString testClass() const { return m_testClass; } + QStringList testCases() const { return m_testCases; } + QString proFile() const { return m_proFile; } + QString targetFile() const { return m_targetFile; } + QString targetName() const { return m_targetName; } + QString workingDirectory() const { return m_workingDir; } + Utils::Environment environment() const { return m_environment; } + ProjectExplorer::Project *project() const { return m_project; } + + +signals: + +public slots: + +private: + QString m_testClass; + QStringList m_testCases; + QString m_proFile; + QString m_targetFile; + QString m_targetName; + QString m_workingDir; + Utils::Environment m_environment; + ProjectExplorer::Project *m_project; +}; + +} // namespace Internal +} // namespace Autotest + +#endif // TESTCONFIGURATION_H diff --git a/plugins/autotest/testinfo.cpp b/plugins/autotest/testinfo.cpp index 8e5850c84be..ad904e9a0f1 100644 --- a/plugins/autotest/testinfo.cpp +++ b/plugins/autotest/testinfo.cpp @@ -30,5 +30,10 @@ TestInfo::TestInfo(const QString &className, const QStringList &functions, unsig { } +TestInfo::~TestInfo() +{ + m_functions.clear(); +} + } // namespace Internal } // namespace Autotest diff --git a/plugins/autotest/testinfo.h b/plugins/autotest/testinfo.h index 378afcab589..bfb7ef33c5e 100644 --- a/plugins/autotest/testinfo.h +++ b/plugins/autotest/testinfo.h @@ -30,6 +30,7 @@ public: explicit TestInfo(const QString &className, const QStringList &functions = QStringList(), unsigned revision = 0, unsigned editorRevision = 0); + ~TestInfo(); const QString testClass() const { return m_className; } void setTestClass(const QString &className) { m_className = className; } const QStringList testFunctions() const { return m_functions; } diff --git a/plugins/autotest/testresult.cpp b/plugins/autotest/testresult.cpp new file mode 100644 index 00000000000..c75ba31ac6f --- /dev/null +++ b/plugins/autotest/testresult.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "testresult.h" + +namespace Autotest { +namespace Internal { + +TestResult::TestResult(const QString &className, const QString &testCase, const QString &dataTag, + ResultType result, const QString &description) + : m_class(className), + m_case(testCase), + m_dataTag(dataTag), + m_result(result), + m_description(description), + m_line(0) +{ +} + +ResultType TestResult::resultFromString(const QString &resultString) +{ + if (resultString == QLatin1String("pass")) + return PASS; + if (resultString == QLatin1String("fail")) + return FAIL; + if (resultString == QLatin1String("xfail")) + return EXPECTED_FAIL; + if (resultString == QLatin1String("xpass")) + return UNEXPECTED_PASS; + if (resultString == QLatin1String("skip")) + return SKIP; + if (resultString == QLatin1String("qdebug")) + return MESSAGE_DEBUG; + if (resultString == QLatin1String("warn")) + return MESSAGE_WARN; + if (resultString == QLatin1String("qfatal")) + return MESSAGE_FATAL; + qDebug(" unexpected testresult..."); + qDebug(resultString.toLatin1()); + return UNKNOWN; +} + +QString TestResult::resultToString(const ResultType type) +{ + switch(type) { + case PASS: return QLatin1String("PASS"); + case FAIL: return QLatin1String("FAIL"); + case EXPECTED_FAIL: return QLatin1String("XFAIL"); + case UNEXPECTED_PASS: return QLatin1String("XPASS"); + case SKIP: return QLatin1String("SKIP"); + case MESSAGE_DEBUG: return QLatin1String("DEBUG"); + case MESSAGE_WARN: return QLatin1String("WARN"); + case MESSAGE_FATAL: return QLatin1String("FATAL"); + case MESSAGE_INTERNAL: return QString(); + default: + return QLatin1String("UNKNOWN"); + } +} + +QColor TestResult::colorForType(const ResultType type) +{ + switch(type) { + case PASS: return QColor("#009900"); + case FAIL: return QColor("#a00000"); + case EXPECTED_FAIL: return QColor("#00ff00"); + case UNEXPECTED_PASS: return QColor("#ff0000"); + case SKIP: return QColor("#787878"); + case MESSAGE_DEBUG: return QColor("#329696"); + case MESSAGE_WARN: return QColor("#d0bb00"); + case MESSAGE_FATAL: return QColor("#640000"); + case MESSAGE_INTERNAL: return QColor("transparent"); + default: + return QColor("#000000"); + } +} + +bool operator==(const TestResult &t1, const TestResult &t2) +{ + return t1.className() == t2.className() + && t1.testCase() == t2.testCase() + && t1.dataTag() == t2.dataTag() + && t1.result() == t2.result(); +} + +} // namespace Internal +} // namespace Autotest diff --git a/plugins/autotest/testresult.h b/plugins/autotest/testresult.h new file mode 100644 index 00000000000..dc476916645 --- /dev/null +++ b/plugins/autotest/testresult.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef TESTRESULT_H +#define TESTRESULT_H + +#include +#include + +namespace Autotest { +namespace Internal { + +enum ResultType { + PASS, + FAIL, + EXPECTED_FAIL, + UNEXPECTED_PASS, + SKIP, + MESSAGE_DEBUG, + MESSAGE_WARN, + MESSAGE_FATAL, + MESSAGE_INTERNAL, + UNKNOWN // ??? +}; + +class TestResult +{ +public: + + TestResult(const QString &className, const QString &testCase, const QString &dataTag = QString(), + ResultType result = UNKNOWN, const QString &description = QString()); + + QString className() const { return m_class; } + QString testCase() const { return m_case; } + QString dataTag() const { return m_dataTag; } + ResultType result() const { return m_result; } + QString description() const { return m_description; } + QString fileName() const { return m_file; } + int line() const { return m_line; } + + void setFileName(const QString &fileName) { m_file = fileName; } + void setLine(int line) { m_line = line; } + + static ResultType resultFromString(const QString &resultString); + static QString resultToString(const ResultType type); + static QColor colorForType(const ResultType type); + +private: + QString m_class; + QString m_case; + QString m_dataTag; + ResultType m_result; + QString m_description; + QString m_file; + int m_line; + // environment? +}; + +bool operator==(const TestResult &t1, const TestResult &t2); + +} // namespace Internal +} // namespace Autotest + +#endif // TESTRESULT_H diff --git a/plugins/autotest/testresultdelegate.cpp b/plugins/autotest/testresultdelegate.cpp new file mode 100644 index 00000000000..1f423d2e3a1 --- /dev/null +++ b/plugins/autotest/testresultdelegate.cpp @@ -0,0 +1,244 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "testresultdelegate.h" +#include "testresultmodel.h" + +#include +#include +#include +#include + +namespace Autotest { +namespace Internal { + +TestResultDelegate::TestResultDelegate(QObject *parent) + : QStyledItemDelegate(parent) +{ +} + +TestResultDelegate::~TestResultDelegate() +{ + +} + +void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + painter->save(); + + QFontMetrics fm(opt.font); + QColor background; + QColor foreground; + + const QAbstractItemView *view = qobject_cast(opt.widget); + const bool selected = view->selectionModel()->currentIndex() == index; + + if (selected) { + painter->setBrush(opt.palette.highlight().color()); + background = opt.palette.highlight().color(); + foreground = opt.palette.highlightedText().color(); + } else { + painter->setBrush(opt.palette.background().color()); + background = opt.palette.background().color(); + foreground = opt.palette.text().color(); + } + + painter->setPen(Qt::NoPen); + painter->drawRect(opt.rect); + + painter->setPen(foreground); + TestResultModel *resultModel = static_cast(view->model()); + LayoutPositions positions(opt, resultModel); + TestResult testResult = resultModel->testResult(index); + ResultType type = testResult.result(); + + QIcon icon = index.data(Qt::DecorationRole).value(); + if (!icon.isNull()) + painter->drawPixmap(positions.left(), positions.top(), + icon.pixmap(positions.iconSize(), positions.iconSize())); + + QString typeStr = TestResult::resultToString(testResult.result()); + if (selected) { + painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr); + } else { + QPen tmp = painter->pen(); + painter->setPen(TestResult::colorForType(testResult.result())); + painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr); + painter->setPen(tmp); + } + + QString output; + switch (type) { + case ResultType::PASS: + case ResultType::FAIL: + case ResultType::EXPECTED_FAIL: + case ResultType::UNEXPECTED_PASS: + output = testResult.className() + QLatin1String("::") + testResult.testCase(); + if (!testResult.dataTag().isEmpty()) + output.append(QString::fromLatin1(" (%1)").arg(testResult.dataTag())); + if (selected && !testResult.description().isEmpty()) { + output.append(QLatin1Char('\n')); + output.append(testResult.description()); + } + break; + default: + output = testResult.description(); + if (!selected) + output = output.split(QLatin1Char('\n')).first(); + } + + QColor mix; + mix.setRgb( static_cast(0.7 * foreground.red() + 0.3 * background.red()), + static_cast(0.7 * foreground.green() + 0.3 * background.green()), + static_cast(0.7 * foreground.blue() + 0.3 * background.blue())); + + if (selected) { + int height = 0; + int leading = fm.leading(); + int firstLineBreak = output.indexOf(QLatin1Char('\n')); + output.replace(QLatin1Char('\n'), QChar::LineSeparator); + QTextLayout tl(output); + if (firstLineBreak != -1) { + QTextLayout::FormatRange fr; + fr.start = firstLineBreak; + fr.length = output.length() - firstLineBreak; + fr.format.setFontStyleHint(QFont::Monospace); + fr.format.setForeground(mix); + tl.setAdditionalFormats(QList() << fr); + } + tl.beginLayout(); + while (true) { + QTextLine tLine = tl.createLine(); + if (!tLine.isValid()) + break; + tLine.setLineWidth(positions.textAreaWidth()); + height += leading; + tLine.setPosition(QPoint(0, height)); + height += fm.ascent() + fm.descent(); + } + tl.endLayout(); + tl.draw(painter, QPoint(positions.textAreaLeft(), positions.top())); + + painter->setPen(mix); + + int bottomLine = positions.top() + fm.ascent() + height + leading; + painter->drawText(positions.textAreaLeft(), bottomLine, testResult.fileName()); + } else { + painter->setClipRect(positions.textArea()); + painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(), output); + } + + QString file = testResult.fileName(); + const int pos = file.lastIndexOf(QLatin1Char('/')); + if (pos != -1) + file = file.mid(pos + 1); + + painter->setClipRect(positions.fileArea()); + painter->drawText(positions.fileAreaLeft(), positions.top() + fm.ascent(), file); + + + if (testResult.line()) { + QString line = QString::number(testResult.line()); + painter->setClipRect(positions.lineArea()); + painter->drawText(positions.lineAreaLeft(), positions.top() + fm.ascent(), line); + } + + painter->setClipRect(opt.rect); + painter->setPen(QColor::fromRgb(150, 150, 150)); + painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); + painter->restore(); +} + +QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV4 opt = option; + initStyleOption(&opt, index); + + const QAbstractItemView *view = qobject_cast(opt.widget); + const bool selected = view->selectionModel()->currentIndex() == index; + + QFontMetrics fm(opt.font); + int fontHeight = fm.height(); + TestResultModel *resultModel = static_cast(view->model()); + LayoutPositions positions(opt, resultModel); + QSize s; + s.setWidth(opt.rect.width()); + + if (selected) { + TestResult testResult = resultModel->testResult(index); + + QString output; + switch (testResult.result()) { + case ResultType::PASS: + case ResultType::FAIL: + case ResultType::EXPECTED_FAIL: + case ResultType::UNEXPECTED_PASS: + output = testResult.className() + QLatin1String("::") + testResult.testCase(); + if (!testResult.dataTag().isEmpty()) + output.append(QString::fromLatin1(" (%1)").arg(testResult.dataTag())); + if (!testResult.description().isEmpty()) { + output.append(QLatin1Char('\n')); + output.append(testResult.description()); + } + break; + default: + output = testResult.description(); + } + + output.replace(QLatin1Char('\n'), QChar::LineSeparator); + + int height = 0; + int leading = fm.leading(); + QTextLayout tl(output); + tl.beginLayout(); + while (true) { + QTextLine line = tl.createLine(); + if (!line.isValid()) + break; + height += leading; + line.setPosition(QPoint(0, height)); + height += fm.ascent() + fm.descent(); + } + tl.endLayout(); + + s.setHeight(height + leading + 3 + (testResult.fileName().isEmpty() ? 0 : fontHeight)); + } else { + s.setHeight(fontHeight + 3); + } + + if (s.height() < positions.minimumHeight()) + s.setHeight(positions.minimumHeight()); + + return s; +} + +void TestResultDelegate::emitSizeHintChanged(const QModelIndex &index) +{ + emit sizeHintChanged(index); +} + +void TestResultDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + emit sizeHintChanged(current); + emit sizeHintChanged(previous); +} + +} // namespace Internal +} // namespace Autotest diff --git a/plugins/autotest/testresultdelegate.h b/plugins/autotest/testresultdelegate.h new file mode 100644 index 00000000000..2b85ae1076d --- /dev/null +++ b/plugins/autotest/testresultdelegate.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef TESTRESULTDELEGATE_H +#define TESTRESULTDELEGATE_H + +#include "testresultmodel.h" + +#include + +namespace Autotest { +namespace Internal { + +class TestResultDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + explicit TestResultDelegate(QObject *parent = 0); + ~TestResultDelegate(); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void emitSizeHintChanged(const QModelIndex &index); + +signals: + +public slots: + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +private: + class LayoutPositions + { + public: + LayoutPositions(QStyleOptionViewItemV4 &options, TestResultModel *model) + : m_totalWidth(options.rect.width()), + m_maxFileLength(model->maxWidthOfFileName(options.font)), + m_maxLineLength(model->maxWidthOfLineNumber(options.font)), + m_realFileLength(m_maxFileLength), + m_top(options.rect.top()), + m_bottom(options.rect.bottom()) + { + int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING; + if (m_maxFileLength > flexibleArea / 2) + m_realFileLength = flexibleArea / 2; + m_fontHeight = QFontMetrics(options.font).height(); + m_typeAreaWidth = QFontMetrics(options.font).width(QLatin1String("XXXXXXXX")); + } + + int top() const { return m_top + ITEM_MARGIN; } + int left() const { return ITEM_MARGIN; } + int right() const { return m_totalWidth - ITEM_MARGIN; } + int bottom() const { return m_bottom; } + int minimumHeight() const { return ICON_SIZE + 2 * ITEM_MARGIN; } + + int iconSize() const { return ICON_SIZE; } + int fontHeight() const { return m_fontHeight; } + int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; } + int typeAreaWidth() const { return m_typeAreaWidth; } + int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING; } + int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); } + int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; } + int lineAreaLeft() const { return right() - m_maxLineLength; } + + QRect typeArea() const { return QRect(typeAreaLeft(), top(), + typeAreaWidth(), m_fontHeight); } + QRect textArea() const { return QRect(textAreaLeft(), top(), + fileAreaLeft() - ITEM_SPACING, m_fontHeight); } + QRect fileArea() const { return QRect(fileAreaLeft(), top(), + lineAreaLeft() - ITEM_SPACING, m_fontHeight); } + + QRect lineArea() const { return QRect(lineAreaLeft(), top(), + m_maxLineLength, m_fontHeight); } + + private: + int m_totalWidth; + int m_maxFileLength; + int m_maxLineLength; + int m_realFileLength; + int m_top; + int m_bottom; + int m_fontHeight; + int m_typeAreaWidth; + + static const int ICON_SIZE = 16; + static const int ITEM_MARGIN = 2; + static const int ITEM_SPACING = 4; + + }; +}; + +} // namespace Internal +} // namespace Autotest + +#endif // TESTRESULTDELEGATE_H diff --git a/plugins/autotest/testresultmodel.cpp b/plugins/autotest/testresultmodel.cpp new file mode 100644 index 00000000000..1867987aa1f --- /dev/null +++ b/plugins/autotest/testresultmodel.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "testresultmodel.h" + +#include +#include +#include + +namespace Autotest { +namespace Internal { + +TestResultModel::TestResultModel(QObject *parent) : + QAbstractItemModel(parent), + m_widthOfLineNumber(0), + m_maxWidthOfFileName(0), + m_lastMaxWidthIndex(0) +{ +} + +TestResultModel::~TestResultModel() +{ + m_testResults.clear(); +} + +QModelIndex TestResultModel::index(int row, int column, const QModelIndex &parent) const +{ + if (parent.isValid()) + return QModelIndex(); + return createIndex(row, column); +} + +QModelIndex TestResultModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +int TestResultModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_testResults.size(); +} + +int TestResultModel::columnCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : 1; +} + +static QIcon testResultIcon(ResultType result) { + static QIcon icons[8] = { + QIcon(QLatin1String(":/images/pass.png")), + QIcon(QLatin1String(":/images/fail.png")), + QIcon(QLatin1String(":/images/xfail.png")), + QIcon(QLatin1String(":/images/xpass.png")), + QIcon(QLatin1String(":/images/skip.png")), + QIcon(QLatin1String(":/images/debug.png")), + QIcon(QLatin1String(":/images/warn.png")), + QIcon(QLatin1String(":/images/fatal.png")), + }; + + if (result < 0 || result >= MESSAGE_INTERNAL) + return QIcon(); + return icons[result]; +} + +QVariant TestResultModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || index.row() >= m_testResults.count() || index.column() != 0) + return QVariant(); + if (role == Qt::DisplayRole) { + const TestResult &tr = m_testResults.at(index.row()); + switch (tr.result()) { + case ResultType::PASS: + case ResultType::FAIL: + case ResultType::EXPECTED_FAIL: + case ResultType::UNEXPECTED_PASS: + case ResultType::SKIP: + return QString::fromLatin1("%1::%2 (%3) - %4").arg(tr.className(), tr.testCase(), + tr.dataTag(), tr.fileName()); + default: + return tr.description(); + } + } + if (role == Qt::DecorationRole) { + const TestResult &tr = m_testResults.at(index.row()); + return testResultIcon(tr.result()); + } + + return QVariant(); +} + +void TestResultModel::addTestResult(const TestResult &testResult) +{ + beginInsertRows(QModelIndex(), m_testResults.size(), m_testResults.size()); + m_testResults.append(testResult); + endInsertRows(); +} + +void TestResultModel::clearTestResults() +{ + if (m_testResults.size() == 0) + return; + beginRemoveRows(QModelIndex(), 0, m_testResults.size() - 1); + m_testResults.clear(); + endRemoveRows(); +} + +TestResult TestResultModel::testResult(const QModelIndex &index) const +{ + if (!index.isValid()) + return TestResult(QString(), QString()); + return m_testResults.at(index.row()); +} + +int TestResultModel::maxWidthOfFileName(const QFont &font) +{ + int count = m_testResults.size(); + if (count == 0) + return 0; + if (m_maxWidthOfFileName > 0 && font == m_measurementFont && m_lastMaxWidthIndex == count - 1) + return m_maxWidthOfFileName; + + QFontMetrics fm(font); + m_measurementFont = font; + + for (int i = m_lastMaxWidthIndex; i < count; ++i) { + QString filename = m_testResults.at(i).fileName(); + const int pos = filename.lastIndexOf(QLatin1Char('/')); + if (pos != -1) + filename = filename.mid(pos +1); + + m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(filename)); + } + m_lastMaxWidthIndex = count - 1; + return m_maxWidthOfFileName; +} + +int TestResultModel::maxWidthOfLineNumber(const QFont &font) +{ + if (m_widthOfLineNumber == 0 || font != m_measurementFont) { + QFontMetrics fm(font); + m_measurementFont = font; + m_widthOfLineNumber = fm.width(QLatin1String("88888")); + } + return m_widthOfLineNumber; +} + +} // namespace Internal +} // namespace Autotest diff --git a/plugins/autotest/testresultmodel.h b/plugins/autotest/testresultmodel.h new file mode 100644 index 00000000000..fa358205546 --- /dev/null +++ b/plugins/autotest/testresultmodel.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef TESTRESULTMODEL_H +#define TESTRESULTMODEL_H + +#include "testresult.h" + +#include +#include + +namespace Autotest { +namespace Internal { + +class TestResultModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit TestResultModel(QObject *parent = 0); + ~TestResultModel(); + QModelIndex index(int row, int column, const QModelIndex &parent) const; + QModelIndex parent(const QModelIndex &) const; + int rowCount(const QModelIndex &parent) const; + int columnCount(const QModelIndex &parent) const; + QVariant data(const QModelIndex &index, int role) const; + + void addTestResult(const TestResult &testResult); + void clearTestResults(); + + bool hasResults() const { return m_testResults.size() > 0; } + TestResult testResult(const QModelIndex &index) const; + + int maxWidthOfFileName(const QFont &font); + int maxWidthOfLineNumber(const QFont &font); + +signals: + +public slots: + +private: + QList m_testResults; + int m_widthOfLineNumber; + int m_maxWidthOfFileName; + int m_lastMaxWidthIndex; + QFont m_measurementFont; +}; + +} // namespace Internal +} // namespace Autotest + +#endif // TESTRESULTMODEL_H diff --git a/plugins/autotest/testresultspane.cpp b/plugins/autotest/testresultspane.cpp new file mode 100644 index 00000000000..100de73e58b --- /dev/null +++ b/plugins/autotest/testresultspane.cpp @@ -0,0 +1,216 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "testresultspane.h" +#include "testresultmodel.h" +#include "testresultdelegate.h" +#include "testrunner.h" +#include "testtreemodel.h" + +#include + +#include + +#include + +#include + +namespace Autotest { +namespace Internal { + +TestResultsPane::TestResultsPane(QObject *parent) : + Core::IOutputPane(parent), + m_context(new Core::IContext(this)) +{ + m_listView = new Utils::ListView; + m_model = new TestResultModel(this); + m_listView->setModel(m_model); + TestResultDelegate *trd = new TestResultDelegate(this); + m_listView->setItemDelegate(trd); + + createToolButtons(); + + connect(m_listView, &Utils::ListView::activated, this, &TestResultsPane::onItemActivated); + connect(m_listView->selectionModel(), &QItemSelectionModel::currentChanged, + trd, &TestResultDelegate::currentChanged); +} + +void TestResultsPane::createToolButtons() +{ + m_runAll = new QToolButton(m_listView); + m_runAll->setIcon(QIcon(QLatin1String(":/images/run.png"))); + m_runAll->setToolTip(tr("Run All Tests")); + connect(m_runAll, &QToolButton::clicked, this, &TestResultsPane::onRunAllTriggered); + + m_runSelected = new QToolButton(m_listView); + m_runSelected->setIcon(QIcon(QLatin1String(":/images/runselected.png"))); + m_runSelected->setToolTip(tr("Run Selected Tests")); + connect(m_runSelected, &QToolButton::clicked, this, &TestResultsPane::onRunSelectedTriggered); +} + +static TestResultsPane *m_instance = 0; + +TestResultsPane *TestResultsPane::instance() +{ + if (!m_instance) + m_instance = new TestResultsPane; + return m_instance; +} + +TestResultsPane::~TestResultsPane() +{ + delete m_listView; + m_instance = 0; +} + +void TestResultsPane::addTestResult(const TestResult &result) +{ + m_model->addTestResult(result); + if (!m_listView->isVisible()) + popup(Core::IOutputPane::NoModeSwitch); + flash(); + navigateStateChanged(); +} + +QWidget *TestResultsPane::outputWidget(QWidget *parent) +{ + if (m_listView) { + m_listView->setParent(parent); + } else { + m_listView = new Utils::ListView(parent); + } + return m_listView; +} + +QList TestResultsPane::toolBarWidgets() const +{ + return QList() << m_runAll << m_runSelected; // add filter as well +} + +QString TestResultsPane::displayName() const +{ + return tr("Test Results"); +} + +int TestResultsPane::priorityInStatusBar() const +{ + return -666; +} + +void TestResultsPane::clearContents() +{ + m_model->clearTestResults(); + navigateStateChanged(); +} + +void TestResultsPane::visibilityChanged(bool) +{ +} + +void TestResultsPane::setFocus() +{ +} + +bool TestResultsPane::hasFocus() const +{ + return m_listView->hasFocus(); +} + +bool TestResultsPane::canFocus() const +{ + return true; +} + +bool TestResultsPane::canNavigate() const +{ + return true; +} + +bool TestResultsPane::canNext() const +{ + return m_model->hasResults(); +} + +bool TestResultsPane::canPrevious() const +{ + return m_model->hasResults(); +} + +void TestResultsPane::goToNext() +{ + if (!canNext()) + return; + + QModelIndex currentIndex = m_listView->currentIndex(); + if (currentIndex.isValid()) { + int row = currentIndex.row() + 1; + if (row == m_model->rowCount(QModelIndex())) + row = 0; + currentIndex = m_model->index(row, 0, QModelIndex()); + } else { + currentIndex = m_model->index(0, 0, QModelIndex()); + } + m_listView->setCurrentIndex(currentIndex); + onItemActivated(currentIndex); +} + +void TestResultsPane::goToPrev() +{ + if (!canPrevious()) + return; + + QModelIndex currentIndex = m_listView->currentIndex(); + if (currentIndex.isValid()) { + int row = currentIndex.row() - 1; + if (row < 0) + row = m_model->rowCount(QModelIndex()) - 1; + currentIndex = m_model->index(row, 0, QModelIndex()); + } else { + currentIndex = m_model->index(m_model->rowCount(QModelIndex()) - 1, 0, QModelIndex()); + } + m_listView->setCurrentIndex(currentIndex); + onItemActivated(currentIndex); +} + +void TestResultsPane::onItemActivated(const QModelIndex &index) +{ + if (!index.isValid()) + return; + + TestResult tr = m_model->testResult(index); + if (!tr.fileName().isEmpty()) + Core::EditorManager::openEditorAt(tr.fileName(), tr.line(), 0); +} + +void TestResultsPane::onRunAllTriggered() +{ + TestRunner *runner = TestRunner::instance(); + runner->setSelectedTests(TestTreeModel::instance()->getAllTestCases()); + runner->runTests(); +} + +void TestResultsPane::onRunSelectedTriggered() +{ + TestRunner *runner = TestRunner::instance(); + runner->setSelectedTests(TestTreeModel::instance()->getSelectedTests()); + runner->runTests(); + +} + +} // namespace Internal +} // namespace Autotest diff --git a/plugins/autotest/testresultspane.h b/plugins/autotest/testresultspane.h new file mode 100644 index 00000000000..28aff09103d --- /dev/null +++ b/plugins/autotest/testresultspane.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef TESTRESULTSPANE_H +#define TESTRESULTSPANE_H + +#include + +QT_BEGIN_NAMESPACE +class QModelIndex; +class QToolButton; +QT_END_NAMESPACE + +namespace Core { +class IContext; +} + +namespace Utils { +class ListView; +} + +namespace Autotest { +namespace Internal { + +class TestResult; +class TestResultModel; + +class TestResultsPane : public Core::IOutputPane +{ + Q_OBJECT +public: + virtual ~TestResultsPane(); + static TestResultsPane *instance(); + + void addTestResult(const TestResult &result); + + // IOutputPane interface + QWidget *outputWidget(QWidget *parent); + QList toolBarWidgets() const; + QString displayName() const; + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool); + void setFocus(); + bool hasFocus() const; + bool canFocus() const; + bool canNavigate() const; + bool canNext() const; + bool canPrevious() const; + void goToNext(); + void goToPrev(); + +signals: + +public slots: + +private slots: + void onItemActivated(const QModelIndex &index); + void onRunAllTriggered(); + void onRunSelectedTriggered(); + +private: + explicit TestResultsPane(QObject *parent = 0); + void createToolButtons(); + + Utils::ListView *m_listView; + TestResultModel *m_model; + Core::IContext *m_context; + QToolButton *m_runAll; + QToolButton *m_runSelected; +}; + +} // namespace Internal +} // namespace Autotest + +#endif // TESTRESULTSPANE_H diff --git a/plugins/autotest/testrunner.cpp b/plugins/autotest/testrunner.cpp new file mode 100644 index 00000000000..5a1f41baefb --- /dev/null +++ b/plugins/autotest/testrunner.cpp @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#include "testresultspane.h" +#include "testrunner.h" + +#include // REMOVE + +#include +#include +#include +#include + +#include + +namespace Autotest { +namespace Internal { + +static TestRunner* m_instance = 0; + +TestRunner *TestRunner::instance() +{ + if (!m_instance) + m_instance = new TestRunner; + return m_instance; +} + +TestRunner::TestRunner(QObject *parent) : + QObject(parent), + m_building(false) +{ + m_runner.setReadChannelMode(QProcess::MergedChannels); + m_runner.setReadChannel(QProcess::StandardOutput); + + connect(&m_runner, &QProcess::readyReadStandardOutput, + this, &TestRunner::processOutput, Qt::DirectConnection); + connect(&m_runner, SIGNAL(finished(int,QProcess::ExitStatus)), + this, SLOT(onRunnerFinished(int,QProcess::ExitStatus)), Qt::DirectConnection); +} + +TestRunner::~TestRunner() +{ + qDeleteAll(m_selectedTests); + m_selectedTests.clear(); + m_instance = 0; +} + +void TestRunner::setSelectedTests(const QList &selected) +{ + qDeleteAll(m_selectedTests); + m_selectedTests.clear(); + m_selectedTests = selected; +} + +void TestRunner::runTests() +{ + if (m_selectedTests.empty()) { + TestResultsPane::instance()->addTestResult( + TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL, + tr("*** No tests selected - canceling Test Run ***"))); + return; + } + + ProjectExplorer::Project *project = m_selectedTests.at(0)->project(); + + if (!project) {// add a warning or info to output? possible at all? + return; + } + + ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance(); + ProjectExplorer::Internal::ProjectExplorerSettings pes = pep->projectExplorerSettings(); + if (pes.buildBeforeDeploy) { + if (!project->hasActiveBuildSettings()) { + qDebug() << "no active build settings...???"; // let it configure? + return; + } + buildProject(project); + while (m_building) { + qApp->processEvents(); + } + + if (!m_buildSucceeded) { + TestResultsPane::instance()->addTestResult( + TestResult(QString(), QString(), QString(), ResultType::MESSAGE_FATAL, + tr("*** Build failed - canceling Test Run ***"))); + return; + } + } + + // clear old log and output pane + m_xmlLog.clear(); + TestResultsPane::instance()->clearContents(); + + foreach (TestConfiguration *tc, m_selectedTests) { + QString cmd = tc->targetFile(); + QString workDir = tc->workingDirectory(); + QStringList args; + Utils::Environment env = tc->environment(); + + args << QLatin1String("-xml"); + if (tc->testCases().count()) + args << tc->testCases(); + + exec(cmd, args, workDir, env); + } + qDebug("test run finished"); +} + +void TestRunner::stopTestRun() +{ + +} + +/******************** XML line parser helper ********************/ + +static bool xmlStartsWith(const QString &code, const QString &start, QString &result) +{ + if (code.startsWith(start)) { + result = code.mid(start.length()); + result = result.left(result.indexOf(QLatin1Char('"'))); + result = result.left(result.indexOf(QLatin1String(""), index) - index); + return !result.isEmpty(); + } + return false; +} + +static bool xmlExtractTypeFileLine(const QString &code, const QString &tagStart, + ResultType &result, QString &file, int &line) +{ + if (code.startsWith(tagStart)) { + int start = code.indexOf(QLatin1String(" type=\"")) + 7; + result = TestResult::resultFromString( + code.mid(start, code.indexOf(QLatin1Char('"'), start) - start)); + start = code.indexOf(QLatin1String(" file=\"")) + 7; + file = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start); + start = code.indexOf(QLatin1String(" line=\"")) + 7; + line = code.mid(start, code.indexOf(QLatin1Char('"'), start) - start).toInt(); + return true; + } + return false; +} + +/****************** XML line parser helper end ******************/ + +void TestRunner::processOutput() +{ + static QString className; + static QString testCase; + static QString dataTag; + static ResultType result = ResultType::UNKNOWN; + static QString description; + static QString file; + static int lineNumber = 0; + static QString duration; + static bool readingDescription = false; + static QString qtVersion; + static QString qtestVersion; + + while (m_runner.canReadLine()) { + // TODO Qt5 uses UTF-8 - while Qt4 uses ISO-8859-1 - could this be a problem? + QString line = QString::fromUtf8(m_runner.readLine()); + line = line.trimmed(); + m_xmlLog.append(line); + if (line.isEmpty() || line.startsWith(QLatin1String(""), dataTag)) + continue; + if (xmlCData(line, QLatin1String(""), description)) { + if (!line.endsWith(QLatin1String(""))) + readingDescription = true; + continue; + } + if (xmlExtractTypeFileLine(line, QLatin1String(""))) { + TestResult testResult(className, testCase, dataTag, result, description); + if (!file.isEmpty()) + file = QFileInfo(m_runner.workingDirectory(), file).canonicalFilePath(); + testResult.setFileName(file); + testResult.setLine(lineNumber); + TestResultsPane::instance()->addTestResult(testResult); + } + continue; + } + if (line == QLatin1String("") || line == QLatin1String("")) { + TestResult testResult(className, testCase, dataTag, result, description); + if (!file.isEmpty()) + file = QFileInfo(m_runner.workingDirectory(), file).canonicalFilePath(); + testResult.setFileName(file); + testResult.setLine(lineNumber); + TestResultsPane::instance()->addTestResult(testResult); + description = QString(); + } else if (line == QLatin1String("") && !duration.isEmpty()) { + TestResult testResult(className, testCase, QString(), ResultType::MESSAGE_INTERNAL, + tr("execution took %1ms").arg(duration)); + TestResultsPane::instance()->addTestResult(testResult); + } else if (line == QLatin1String("") && !duration.isEmpty()) { + TestResult testResult(className, QString(), QString(), ResultType::MESSAGE_INTERNAL, + tr("Test execution took %1ms").arg(duration)); + TestResultsPane::instance()->addTestResult(testResult); + } else if (readingDescription) { + if (line.endsWith(QLatin1String("]]>"))) { + description.append(QLatin1Char('\n')); + description.append(line.left(line.indexOf(QLatin1String("]]>")))); + readingDescription = false; + } else { + description.append(QLatin1Char('\n')); + description.append(line); + } + } else if (xmlStartsWith(line, QLatin1String(""), qtVersion)) { + TestResultsPane::instance()->addTestResult( + TestResult(QString(), QString(), QString(), ResultType::MESSAGE_INTERNAL, + tr("Qt Version: %1").arg(qtVersion))); + } else if (xmlStartsWith(line, QLatin1String(""), qtestVersion)) { + TestResultsPane::instance()->addTestResult( + TestResult(QString(), QString(), QString(), ResultType::MESSAGE_INTERNAL, + tr("QTest Version: %1").arg(qtestVersion))); + } else { +// qDebug() << "Unhandled line:" << line; // TODO remove + } + } +} + +void TestRunner::onRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + qDebug("runnerFinished"); +} + +void TestRunner::buildProject(ProjectExplorer::Project *project) +{ + m_building = true; + m_buildSucceeded = false; + ProjectExplorer::BuildManager *mgr = static_cast( + ProjectExplorer::BuildManager::instance()); + ProjectExplorer::ProjectExplorerPlugin *pep = ProjectExplorer::ProjectExplorerPlugin::instance(); + pep->buildProject(project); + connect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished, + this, &TestRunner::buildFinished); +} + +void TestRunner::buildFinished(bool success) +{ + ProjectExplorer::BuildManager *mgr = static_cast( + ProjectExplorer::BuildManager::instance()); + disconnect(mgr, &ProjectExplorer::BuildManager::buildQueueFinished, + this, &TestRunner::buildFinished); + m_building = false; + m_buildSucceeded = success; +} + +static QString which(const QString &path, const QString &cmd) +{ + if (path.isEmpty() || cmd.isEmpty()) + return QString(); + + QStringList paths; +#ifdef Q_OS_WIN + paths = path.split(QLatin1Char(';')); +#else + paths = path.split(QLatin1Char(':')); +#endif + + foreach (const QString p, paths) { + QString fName = p + QDir::separator() + cmd; + QFileInfo fi(fName); + if (fi.exists() && fi.isExecutable()) + return fName; +#ifdef Q_OS_WIN + fi = QFileInfo(fName + QLatin1String(".exe")); + if (fi.exists()) + return fi.absoluteFilePath(); + fi = QFileInfo(fName + QLatin1String(".bat")); + if (fi.exists()) + return fi.absoluteFilePath(); + fi = QFileInfo(fName + QLatin1String(".cmd")); + if (fi.exists()) + return fi.absoluteFilePath(); +#endif + } + return QString(); +} + +bool TestRunner::exec(const QString &cmd, const QStringList &args, const QString &workingDir, + const Utils::Environment &env, int timeout) +{ + if (m_runner.state() != QProcess::NotRunning) { // kill the runner if it's running already + m_runner.kill(); + m_runner.waitForFinished(); + } + QString runCmd; + if (!QDir::toNativeSeparators(cmd).contains(QDir::separator())) { + if (env.hasKey(QLatin1String("PATH"))) + runCmd = which(env.value(QLatin1String("PATH")), cmd); + } else if (QFileInfo(cmd).exists()) { + runCmd = cmd; + } + + if (runCmd.isEmpty()) { + qDebug("Could not find cmd..."); + return false; + } + + m_runner.setWorkingDirectory(workingDir); + m_runner.setProcessEnvironment(env.toProcessEnvironment()); + QTime executionTimer; + + if (args.count()) { + m_runner.start(runCmd, args); + } else { + m_runner.start(runCmd); + } + + bool ok = m_runner.waitForStarted(); + executionTimer.start(); + if (ok) { + while (m_runner.state() == QProcess::Running && executionTimer.elapsed() < timeout) { + qApp->processEvents(); + } + } + if (ok && executionTimer.elapsed() < timeout) { + return m_runner.exitCode() == 0; + } else { + return false; + } +} + +} // namespace Internal +} // namespace Autotest diff --git a/plugins/autotest/testrunner.h b/plugins/autotest/testrunner.h new file mode 100644 index 00000000000..48f674716d2 --- /dev/null +++ b/plugins/autotest/testrunner.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc +** All rights reserved. +** For any questions to Digia, please use contact form at http://qt.digia.com +** +** This file is part of the Qt Creator Enterprise Auto Test Add-on. +** +** Licensees holding valid Qt Enterprise licenses may use this file in +** accordance with the Qt Enterprise License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. +** +** If you have questions regarding the use of this file, please use +** contact form at http://qt.digia.com +** +****************************************************************************/ + +#ifndef TESTRUNNER_H +#define TESTRUNNER_H + +#include "testconfiguration.h" +#include "testresult.h" + +#include +#include + +namespace ProjectExplorer { +class Project; +} + +namespace Autotest { +namespace Internal { + +class TestRunner : public QObject +{ + Q_OBJECT + +public: + static TestRunner* instance(); + ~TestRunner(); + + void setSelectedTests(const QList &selected); + +signals: + +public slots: + void runTests(); + void stopTestRun(); + +private slots: + void processOutput(); + void onRunnerFinished(int exitCode, QProcess::ExitStatus exitStatus); + void buildProject(ProjectExplorer::Project *project); + void buildFinished(bool success); + +private: + explicit TestRunner(QObject *parent = 0); + bool exec(const QString &cmd, const QStringList &args, const QString &workingDir = QString(), + const Utils::Environment &env = Utils::Environment(), int timeout = 60000); + + QProcess m_runner; + QList m_selectedTests; + bool m_building; + bool m_buildSucceeded; + + QStringList m_xmlLog; // holds complete xml log of the last test run + +}; + +} // namespace Internal +} // namespace Autotest + +#endif // TESTRUNNER_H diff --git a/plugins/autotest/testtreemodel.cpp b/plugins/autotest/testtreemodel.cpp index 88bc653d4ec..7cf8d2888d8 100644 --- a/plugins/autotest/testtreemodel.cpp +++ b/plugins/autotest/testtreemodel.cpp @@ -24,6 +24,16 @@ #include +#include +#include +#include +#include +#include +#include +#include + +#include + namespace Autotest { namespace Internal { @@ -115,6 +125,16 @@ int TestTreeModel::columnCount(const QModelIndex &) const return 1; } +static QIcon testTreeIcon(TestTreeItem::Type type) +{ + static QIcon icons[3] = { + QIcon(), + QIcon(QLatin1String(":/images/class.png")), + QIcon(QLatin1String(":/images/func.png")) + }; + return icons[type]; +} + QVariant TestTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) @@ -138,15 +158,7 @@ QVariant TestTreeModel::data(const QModelIndex &index, int role) const case Qt::ToolTipRole: return item->filePath(); case Qt::DecorationRole: - switch(item->type()) { - case TestTreeItem::TEST_CLASS: - return QIcon(QLatin1String(":/images/class.png")); - case TestTreeItem::TEST_FUNCTION: - return QIcon(QLatin1String(":/images/func.png")); - case TestTreeItem::ROOT: - default: - return QVariant(); - } + return testTreeIcon(item->type()); case Qt::CheckStateRole: if (item->type() == TestTreeItem::ROOT) return QVariant(); @@ -234,6 +246,110 @@ bool TestTreeModel::hasTests() const return m_autoTestRootItem->childCount() > 0 /*|| m_quickTestRootItem->childCount() > 0*/; } +static void addProjectInformation(TestConfiguration *config, const QString &filePath) +{ + QString targetFile; + QString targetName; + QString workDir; + QString proFile; + Utils::Environment env; + ProjectExplorer::Project *project = 0; + CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); + QList projParts = cppMM->projectPart(filePath); + if (!projParts.empty()) { + proFile = projParts.at(0)->projectFile; + project = projParts.at(0)->project; // necessary to grab this here? or should this be the current active startup project anyway? + } + if (project) { + ProjectExplorer::Target *target = project->activeTarget(); + ProjectExplorer::BuildTargetInfoList appTargets = target->applicationTargets(); + foreach (ProjectExplorer::BuildTargetInfo bti, appTargets.list) { + if (bti.isValid() && bti.projectFilePath.toString() == proFile) { + targetFile = bti.targetFilePath.toString(); + targetName = bti.targetName; + break; + } + } + + QList rcs = target->runConfigurations(); + foreach (ProjectExplorer::RunConfiguration *rc, rcs) { + if (ProjectExplorer::LocalApplicationRunConfiguration *localRunConfiguration + = qobject_cast(rc)) { + if (localRunConfiguration->executable() == targetFile) { + workDir = localRunConfiguration->workingDirectory(); + QList aspects + = localRunConfiguration->extraAspects(); + foreach (ProjectExplorer::IRunConfigurationAspect *aspect, aspects) { + if (ProjectExplorer::EnvironmentAspect *asp + = qobject_cast(aspect)) { + env = asp->environment(); + break; + } + } + break; + } + } + } + } + config->setTargetFile(targetFile); + config->setTargetName(targetName); + config->setWorkingDirectory(workDir); + config->setProFile(proFile); + config->setEnvironment(env); + config->setProject(project); +} + +QList TestTreeModel::getAllTestCases() const +{ + QList result; + + int count = m_autoTestRootItem->childCount(); + for (int row = 0; row < count; ++row) { + TestTreeItem *child = m_autoTestRootItem->child(row); + + TestConfiguration *tc = new TestConfiguration(child->name(), QStringList()); + addProjectInformation(tc, child->filePath()); + result << tc; + } + return result; +} + +QList TestTreeModel::getSelectedTests() const +{ + QList result; + TestConfiguration *tc; + + int count = m_autoTestRootItem->childCount(); + for (int row = 0; row < count; ++row) { + TestTreeItem *child = m_autoTestRootItem->child(row); + + switch (child->checked()) { + case Qt::Unchecked: + continue; + case Qt::Checked: + tc = new TestConfiguration(child->name(), QStringList()); + addProjectInformation(tc, child->filePath()); + result << tc; + continue; + case Qt::PartiallyChecked: + default: + QString childName = child->name(); + int grandChildCount = child->childCount(); + QStringList testCases; + for (int grandChildRow = 0; grandChildRow < grandChildCount; ++grandChildRow) { + const TestTreeItem *grandChild = child->child(grandChildRow); + if (grandChild->checked() == Qt::Checked) + testCases << grandChild->name(); + } + + tc = new TestConfiguration(childName, testCases); + addProjectInformation(tc, child->filePath()); + result << tc; + } + } + return result; +} + void TestTreeModel::modifyAutoTestSubtree(int row, TestTreeItem *newItem) { static QVector modificationRoles = QVector() << Qt::DisplayRole diff --git a/plugins/autotest/testtreemodel.h b/plugins/autotest/testtreemodel.h index 9ec25f04762..040e1c912c0 100644 --- a/plugins/autotest/testtreemodel.h +++ b/plugins/autotest/testtreemodel.h @@ -19,6 +19,8 @@ #ifndef TESTTREEMODEL_H #define TESTTREEMODEL_H +#include "testconfiguration.h" + #include #include @@ -56,6 +58,8 @@ public: TestCodeParser *parser() const { return m_parser; } bool hasTests() const; + QList getAllTestCases() const; + QList getSelectedTests() const; signals: diff --git a/plugins/autotest/testtreeview.cpp b/plugins/autotest/testtreeview.cpp index 04b2ffb60fb..fc2fffe1c1d 100644 --- a/plugins/autotest/testtreeview.cpp +++ b/plugins/autotest/testtreeview.cpp @@ -18,12 +18,15 @@ #include "autotestconstants.h" #include "testcodeparser.h" +#include "testrunner.h" #include "testtreeitem.h" #include "testtreemodel.h" #include "testtreeview.h" #include +#include + #include #include @@ -48,12 +51,11 @@ TestTreeViewWidget::TestTreeViewWidget(QWidget *parent) : QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); layout->setSpacing(0); - layout->addWidget(m_view); + layout->addWidget(Core::ItemViewFind::createSearchableWrapper(m_view)); setLayout(layout); TestCodeParser *parser = m_model->parser(); - ProjectExplorer::SessionManager *sm = static_cast( - ProjectExplorer::SessionManager::instance()); + ProjectExplorer::SessionManager *sm = ProjectExplorer::SessionManager::instance(); connect(sm, &ProjectExplorer::SessionManager::startupProjectChanged, parser, &TestCodeParser::updateTestTree); @@ -78,6 +80,8 @@ void TestTreeViewWidget::contextMenuEvent(QContextMenuEvent *event) // TODO remove? QAction *rescan = new QAction(tr("Rescan"), &menu); + connect(runAll, &QAction::triggered, this, &TestTreeViewWidget::onRunAllTriggered); + connect(runSelected, &QAction::triggered, this, &TestTreeViewWidget::onRunSelectedTriggered); connect(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll); connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll); connect(rescan, &QAction::triggered, @@ -103,11 +107,10 @@ QList TestTreeViewWidget::createToolButtons() { QList list; + m_sortAlphabetically = true; m_sort = new QToolButton(this); - m_sort->setIcon((QIcon(QLatin1String(":/images/sort.png")))); - m_sort->setToolTip(tr("Sort Alphabetically (not implemented yet)")); // TODO - m_sort->setCheckable(true); - m_sort->setChecked(true); + m_sort->setIcon((QIcon(QLatin1String(":/images/leafsort.png")))); + m_sort->setToolTip(tr("Sort Naturally (not implemented yet)")); QToolButton *expand = new QToolButton(this); expand->setIcon(QIcon(QLatin1String(":/images/expand.png"))); @@ -119,7 +122,7 @@ QList TestTreeViewWidget::createToolButtons() connect(expand, &QToolButton::clicked, m_view, &TestTreeView::expandAll); connect(collapse, &QToolButton::clicked, m_view, &TestTreeView::collapseAll); -// connect(m_sort, &QToolButton::toggled, m_view, &TestTreeView::onSortToggled); // TODO + connect(m_sort, &QToolButton::clicked, this, &TestTreeViewWidget::onSortClicked); list << m_sort << expand << collapse; return list; @@ -136,6 +139,33 @@ void TestTreeViewWidget::onItemActivated(const QModelIndex &index) } } +void TestTreeViewWidget::onRunAllTriggered() +{ + TestRunner *runner = TestRunner::instance(); + runner->setSelectedTests(m_model->getAllTestCases()); + runner->runTests(); +} + +void TestTreeViewWidget::onRunSelectedTriggered() +{ + TestRunner *runner = TestRunner::instance(); + runner->setSelectedTests(m_model->getSelectedTests()); + runner->runTests(); +} + +void TestTreeViewWidget::onSortClicked() +{ + if (m_sortAlphabetically) { + m_sort->setIcon((QIcon(QLatin1String(":/images/sort.png")))); + m_sort->setToolTip(tr("Sort Alphabetically")); + } else { + m_sort->setIcon((QIcon(QLatin1String(":/images/leafsort.png")))); + m_sort->setToolTip(tr("Sort Naturally (not implemented yet)")); + } + // TODO trigger the sorting change.. + m_sortAlphabetically = !m_sortAlphabetically; +} + TestViewFactory::TestViewFactory() { setDisplayName(tr("Tests")); @@ -145,10 +175,10 @@ TestViewFactory::TestViewFactory() Core::NavigationView TestViewFactory::createWidget() { - TestTreeViewWidget *treeView = new TestTreeViewWidget; + TestTreeViewWidget *treeViewWidget = new TestTreeViewWidget; Core::NavigationView view; - view.widget = treeView; - view.dockToolBarWidgets = treeView->createToolButtons(); + view.widget = treeViewWidget; + view.dockToolBarWidgets = treeViewWidget->createToolButtons(); return view; } diff --git a/plugins/autotest/testtreeview.h b/plugins/autotest/testtreeview.h index cb66fa12ccb..27e475f17c5 100644 --- a/plugins/autotest/testtreeview.h +++ b/plugins/autotest/testtreeview.h @@ -68,11 +68,15 @@ public slots: private slots: void onItemActivated(const QModelIndex &index); + void onRunAllTriggered(); + void onRunSelectedTriggered(); + void onSortClicked(); private: TestTreeModel *m_model; TestTreeView *m_view; QToolButton *m_sort; + bool m_sortAlphabetically; };