BMC#10150 - Manual test case without test steps crashes Testrunner UI
[qa-tools:testrunner-ui.git] / src / runnercontroller.cpp
1 /*
2  * This file is part of testrunner-ui
3  *
4  * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
5  *
6  * Contact: Timo Härkönen <ext-timo.p.harkonen@nokia.com>
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * version 2.1 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20  * 02110-1301 USA
21  *
22  */
23
24 #include "runnercontroller.h"
25 #include "testrunneruisettings.h"
26 #include "testmodel.h"
27
28 #include <QProcess>
29 #include <QDebug>
30 #include <QFile>
31
32 RunnerController::RunnerController(QObject *parent) :
33     QProcess(parent),
34     inputFile_(""),
35     outputFile_(""),
36     capturingAboutTrl(false),
37     waitingForManualResult(false),
38     currTestItem(0)
39 {
40     setProcessChannelMode(QProcess::MergedChannels);
41     setOpenMode(QProcess::Text | QProcess::Unbuffered);
42
43     connect(this, SIGNAL(readyReadStandardOutput()), this, SLOT(readStdOut()), Qt::DirectConnection);
44     connect(this, SIGNAL(readyReadStandardError()), this, SLOT(readStdErr()), Qt::DirectConnection);
45
46     captureAboutTrl();
47 }
48
49 void RunnerController::setTestModel(TestModel *testModel)
50 {
51     m_testModel = testModel;
52 }
53
54 void RunnerController::captureAboutTrl()
55 {
56     capturingAboutTrl = true;
57     aboutTrl = "";
58
59     connect(this, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged()));
60
61     QStringList args;
62     args << "-V";
63
64     start("/usr/bin/testrunner-lite", args);
65 }
66
67 QString RunnerController::aboutTestRunnerLite()
68 {
69     return aboutTrl;
70 }
71
72 bool RunnerController::startRunnerProcess()
73 {
74     QStringList args;
75
76     if (state() == QProcess::Running) {
77         qDebug() << "Process already running";
78         return false;
79     }
80
81     TestRunnerUiSettings *settings = TestRunnerUiSettings::instance();
82
83     QString cmd = settings->testRunnerLitePath();
84
85     if (inputFile_.isEmpty()) {
86         qDebug() << "no input file";
87         return false;
88     }
89     args << "-f" << inputFile_;
90
91     if (settings->debugLogEnabled())
92         args.append("-vv");
93     else
94         args.append("-v");
95
96     if (settings->rhlEnabled())
97         args << "-L" << settings->rhlAddress();
98
99     if (!settings->hwInfoEnabled())
100         args << "-H";
101
102     if (!settings->syslogEnabled())
103         args << "-S";
104
105     if (settings->stepOutputLogEnabled())
106         args << "-P";
107                 
108     if (settings->hbtEnabled()) {
109         QString address =
110                 ((settings->hbtUserName().length() > 0)?
111                 settings->hbtUserName() + "@": "")
112                 + settings->hbtAddress();
113         args << "-t" << address;
114     }
115
116     args << "-e" << settings->targetTestEnvironment();
117
118     args << "-o" << outputFile_;
119
120     qDebug() << args;
121
122     logFile.setFileName(logFile_);
123     if (!logFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
124         qDebug() << "Could not open log file for writing: " << logFile.fileName();
125     }
126
127     currTestItemId = TestItemId();
128     currTestItemId.testDefinition = inputFile_;
129     setCurrTestItem(m_testModel);
130     savedStdout = QByteArray();
131     insideCase = false;
132     stopRequested = false;
133
134     start(cmd, args);
135
136     return true;
137 }
138
139 void RunnerController::readStdOut()
140 {
141     QByteArray stdout = readAllStandardOutput();
142
143     if (capturingAboutTrl) {
144         aboutTrl += stdout;
145         return;
146     }
147
148     if (logFile.isOpen()) {
149         logFile.write(stdout);
150         logFile.flush();
151     }
152
153     QStringList lines = QString(savedStdout + stdout).split("\n");
154
155     for(int i = 0; i < lines.count(); ++i) {
156
157         QString line = lines.at(i);
158
159         if (line.contains("Test suite: ")) {
160             currTestItemId.testSuite = line.mid(line.indexOf("Test suite: ") + 12);
161             currTestItemId.testSet = QString();
162             currTestItemId.testCase = QString();
163             currTestItemId.testStep = -1;
164             setCurrTestItem(m_testModel->findTestItem(currTestItemId));
165         } else if (line.contains("Test set: ")) {
166             currTestItemId.testSet = line.mid(line.indexOf("Test set: ") + 10);
167             currTestItemId.testCase = QString();
168             currTestItemId.testStep = -1;
169             setCurrTestItem(m_testModel->findTestItem(currTestItemId));
170         } else if (line.contains("Starting test case ")) {
171             insideCase = true;
172             currTestItemId.testCase = line.mid(line.indexOf("Starting test case ") + 19);
173             currTestItemId.testStep = -1;
174             setCurrTestItem(m_testModel->findTestItem(currTestItemId));
175         } else if (line.contains("executor.c execute()")
176             && line.contains(" Executing command ")
177             && insideCase) {
178             if (currTestItem->itemType() == TestItem::Step)
179                 emit testResultAvailable(currTestItem, ResultPassed);
180             currTestItemId.testStep++;
181             setCurrTestItem(m_testModel->findTestItem(currTestItemId));
182         } else if(line.contains("Finished test case Result: PASS")) {
183             insideCase = false;
184             emit testResultAvailable(currTestItem, ResultPassed);
185             emit testResultAvailable(currTestItem->parentItem(), ResultPassed);
186         } else if(line.contains("Finished test case Result: FAIL")) {
187             emit testResultAvailable(currTestItem, ResultFailed);
188             emit testResultAvailable(currTestItem->parentItem(), ResultFailed);
189             insideCase = false;
190         } else if(line.contains("Finished test case Result: N/A")) {
191             emit testResultAvailable(currTestItem, ResultNotApplicable);
192             emit testResultAvailable(currTestItem->parentItem(), ResultNotApplicable);
193             insideCase = false;
194         } else if(line.contains("Please enter the result")) {
195             currTestItemId.testStep++;
196             TestItem *step = m_testModel->findTestItem(currTestItemId);
197             if (step) {
198                 setCurrTestItem(step);
199                 waitingForManualResult = true;
200                 // check if there are results already
201                 newResultsEntered(step);
202             } else {
203                 write("n\n");
204             }
205         } else if(line.contains("Please enter additional comments")) {
206             if (stopRequested) {
207                 write("aborted\n");
208             } else {
209                 TestCase *testCase = (currTestItem->itemType() == TestItem::Case)?
210                                      currTestItem->toCase(): currTestItem->parentItem()->toCase();
211                 write((testCase->comment() + "\n").toAscii());
212             }
213         } else if(i == lines.count()-1) {
214             savedStdout = line.toAscii();
215         }
216     }
217 }
218
219 void RunnerController::readStdErr()
220 {
221     qDebug() << "*** stderr *** ";
222     qDebug() << readAllStandardError();
223 }
224
225 void RunnerController::newResultsEntered(TestItem *item)
226 {
227     if (!waitingForManualResult || !item || item->itemType() != TestItem::Step)
228         return;
229
230     TestResult result = item->toStep()->manualResult();
231     if (result == ResultNotRun && stopRequested)
232         result = ResultNotApplicable;
233         
234     if (item == currTestItem && result != ResultNotRun) {
235         QString resultStr;
236         switch (result)  {
237         case ResultPassed:
238             resultStr = "p\n"; break;
239         case ResultFailed:
240             resultStr = "f\n"; break;
241         case ResultNotApplicable:
242             resultStr = "n\n"; break;
243         default:
244             resultStr = "";
245         }
246         write(resultStr.toAscii());
247         emit manualResultsWritten(item);
248         waitingForManualResult = false;
249     } else {
250         emit manualResultsExpected(currTestItem);
251     }
252 }
253
254 void RunnerController::setInputFile(const QString &file)
255 {
256     inputFile_ = file;
257 }
258
259 void RunnerController::setOutputFile(const QString &file)
260 {
261     outputFile_ = file;
262 }
263
264 void RunnerController::setLogFile(const QString &file)
265 {
266     logFile_ = file;
267 }
268
269 void RunnerController::processStateChanged()
270 {
271     if (capturingAboutTrl && state() == QProcess::NotRunning) {
272         capturingAboutTrl = false;
273         aboutTrl.replace(17,1,""); // remove the unprintable character
274     } else if (state() == QProcess::NotRunning) {
275         if (logFile.isOpen())
276             logFile.close();
277         setCurrTestItem(0);
278     }
279 }
280
281 void RunnerController::emitItemStartedEndedSignals(TestItem *oldi, TestItem *newi)
282 {
283     if (oldi == newi)
284         return;
285
286     if (oldi) {
287         if (newi && oldi->itemType() < newi->itemType()) {
288             emitItemStartedEndedSignals(oldi, newi->parentItem());
289         } else if (newi && oldi->itemType() == newi->itemType()) {
290             emit itemExecutionEnded(oldi);
291             emitItemStartedEndedSignals(oldi->parentItem(), newi->parentItem());
292         } else  {
293             emit itemExecutionEnded(oldi);
294             emitItemStartedEndedSignals(oldi->parentItem(), newi);
295         }
296     }
297
298     // Now all itemExecutionEnded signals are sent, start sending itemExecutionStarted signals
299
300     if ((oldi && newi && oldi->itemType() <= newi->itemType())
301         || !oldi ) {
302         emit itemExecutionStarted(newi);
303     }
304 }
305
306 void RunnerController::setCurrTestItem(TestItem *item)
307 {
308     emitItemStartedEndedSignals(currTestItem, item);
309     currTestItem = item;
310 }
311
312 void RunnerController::stopRunnerProcess()
313 {
314     if (state() == Running) {
315         terminate();
316         stopRequested = true;
317         if (waitingForManualResult)
318             newResultsEntered(currTestItem);
319     }
320 }
321
322 void RunnerController::killRunnerProcess()
323 {
324     if (state() == Running) {
325         kill();
326     }
327 }