BMC#6101 (Reopened): Reseting the order of test suites is not working.
[qa-tools:testrunner-ui.git] / src / testmodel.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: Ky√∂sti Ranto <ext-kyosti.1.ranto@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 "testmodel.h"
25 #include "testrunneruisettings.h"
26
27 #include <QFile>
28 #include <QDebug>
29 #include <QDateTime>
30 #include <QStringList>
31
32 // helper funcs
33
34 QString getAttributeValue(QDomElement element, QString attributeName)
35 {
36     QDomNamedNodeMap map = element.attributes();
37
38     for(int i = 0; i < map.count(); ++i) {
39         if(map.item(i).localName() == attributeName) {
40             return map.item(i).nodeValue();
41         }
42     }
43     return QString();
44 }
45
46 QDateTime parseTime(const QDomElement &timeElement)
47 {
48     QDateTime time;
49
50     if (!timeElement.isNull()) {
51         QString timeStr = timeElement.text();
52         // date format: yyyy-mm-dd hh:mm:ss
53         QRegExp rx("(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})");
54         if (rx.exactMatch(timeStr)) {
55             time.setDate(QDate(rx.cap(1).toInt(), rx.cap(2).toInt(), rx.cap(3).toInt()));
56             time.setTime(QTime(rx.cap(4).toInt(), rx.cap(5).toInt(), rx.cap(6).toInt()));
57         }
58     }
59
60     return time;
61 }
62
63 TestResult parseResultFromDomString(QString result)
64 {
65     if (result == "PASS")
66         return ResultPassed;
67     else if (result == "FAIL")
68         return ResultFailed;
69     else if (result == "N/A")
70         return ResultNotApplicable;
71     else
72         return ResultNotRun;
73 }
74
75 QStringList parseSelectorString(QString str)
76 {
77     if (str.trimmed().isEmpty())
78         return QStringList("");
79
80     QStringList list1 = str.split(",", QString::SkipEmptyParts);
81     QStringList list2;
82     foreach (QString item, list1)
83         list2.append(item.trimmed());
84
85     return list2;
86 }
87
88 // ------- TestItem ------------------------
89
90 TestItem::TestItem(QObject *parent, Type type):
91         QObject(parent),
92         m_startTime(QDateTime()),
93         m_endTime(QDateTime()),
94         m_type(type),
95         m_checkState(Qt::Checked),
96         m_isInExecution(false)
97 {
98     m_testModel = (m_type == Model)? static_cast<TestModel *>(this): parentItem()? parentItem()->testModel(): 0;
99     resetCaseCount();
100     resetSelectors();
101     clearCompositeResults();
102 }
103
104 bool TestItem::hasChildren()
105 {
106     return !children().isEmpty();
107 }
108
109 QList<TestItem *> TestItem::children()
110 {
111     return QList<TestItem *>();
112 }
113
114 TestItem::Type TestItem::itemType() const
115 {
116     return m_type;
117 }
118
119 void TestItem::updateTotalCaseCount(int delta)
120 {
121     if (delta != 0) {
122         m_results.total += delta;
123         m_results.selected += delta;
124         emit itemChanged();
125         if (parentItem())
126             parentItem()->updateTotalCaseCount(delta);
127     }
128 }
129
130 void TestItem::updateSelectedCaseCount(int delta)
131 {
132     if (delta != 0) {
133         m_results.selected += delta;
134         emit itemChanged();
135         if (parentItem())
136             parentItem()->updateSelectedCaseCount(delta);
137     }
138 }
139
140 void TestItem::resetCaseCount()
141 {
142     m_results.total = 0;
143     m_results.selected = 0;
144 }
145
146 void TestItem::clearCompositeResults()
147 {
148     m_results.passed = 0;
149     m_results.failed = 0;
150     m_results.na = 0;
151     m_results.result = ResultNotRun;
152 }
153
154 CompositeResults TestItem::compositeResults()
155 {
156     return m_results;
157 }
158
159 void TestItem::addSelectors(CaseSelectors selectors)
160 {
161     m_selectors.requirements.append(selectors.requirements);
162     m_selectors.requirements.sort();
163     m_selectors.requirements.removeDuplicates();
164
165     m_selectors.features.append(selectors.features);
166     m_selectors.features.sort();
167     m_selectors.features.removeDuplicates();
168
169     m_selectors.types.append(selectors.types);
170     m_selectors.types.sort();
171     m_selectors.types.removeDuplicates();
172
173     m_selectors.manual |= selectors.manual;
174     m_selectors.automatic |= selectors.automatic;
175
176     if (parentItem())
177         parentItem()->addSelectors(selectors);
178 }
179
180 CaseSelectors TestItem::selectors()
181 {
182     return m_selectors;
183 }
184
185 void TestItem::applySelectors(CaseSelectors selectors)
186 {
187     foreach (TestItem *child, children())
188         child->applySelectors(selectors);
189 }
190
191 void TestItem::resetSelectors()
192 {
193     m_selectors.requirements = QStringList();
194     m_selectors.features = QStringList();
195     m_selectors.types = QStringList();
196     m_selectors.automatic = false;
197     m_selectors.manual = false;
198 }
199
200 void TestItem::processNewResult(TestResult result)
201 {
202     switch (result) {
203     case ResultPassed:
204         m_results.passed++;
205         break;
206     case ResultFailed:
207         m_results.failed++;
208         break;
209     case ResultNotApplicable:
210         m_results.na++;
211         break;
212     default:
213         return;
214     }
215
216     int total = testModel()->runModeAll()? m_results.total: m_results.selected;
217
218     if (m_results.passed + m_results.failed + m_results.na != total) {
219         m_results.result = ResultNotRun;
220     } else if (m_results.na == total) {
221         m_results.result = ResultNotApplicable;
222     } else if (m_results.passed + m_results.na == total) {
223         m_results.result = ResultPassed;
224     } else {  // m_results.failed > 0
225         m_results.result = ResultFailed;
226     }
227
228     emit itemChanged();
229
230     if (parentItem()) {
231         parentItem()->processNewResult(result);
232     }
233 }
234
235 bool TestItem::isInExecution()
236 {
237     return m_isInExecution;
238 }
239
240 void TestItem::setIsInExecution(bool isInExecution)
241 {
242     if (isInExecution != m_isInExecution) {
243         m_isInExecution = isInExecution;
244         emit itemChanged();
245
246         //in case of a step, rest of the steps may have changed as well (semiautomatic case)
247         if (itemType() == Step)
248             foreach (TestItem *sibling, parentItem()->children())
249                 sibling->touch();
250     }
251 }
252
253 void TestItem::touch()
254 {
255     emit itemChanged();
256 }
257
258 bool TestItem::isEnabledInEnvironment(QString environment)
259 {
260     return parentItem()? parentItem()->isEnabledInEnvironment(environment): true;
261 }
262
263 void TestItem::toggleCheckState()
264 {
265
266     Qt::CheckState oldState = m_checkState;
267     if (m_checkState == Qt::Checked)
268         m_checkState = Qt::Unchecked;
269     else
270         m_checkState = Qt::Checked;
271
272     if (m_checkState != oldState) {
273         if (itemType() == Case)
274             updateSelectedCaseCount(m_checkState == Qt::Checked? 1: -1);
275         emit itemChanged();
276         foreach (TestItem *childItem, children())
277             childItem->updateChildCheckState(m_checkState);
278         if (parentItem())
279             parentItem()->updateParentalCheckState();
280     }
281 }
282
283 void TestItem::updateParentalCheckState()
284 {
285     bool checkedDescendants = false;
286     bool uncheckedDescendants = false;
287
288     foreach (TestItem *childItem, children()) {
289         // count only enabled children
290         if (childItem->isEnabledInEnvironment()) {
291             Qt::CheckState childState = childItem->checkState();
292             checkedDescendants |= (childState == Qt::Checked)
293                                   | (childState == Qt::PartiallyChecked);
294             uncheckedDescendants |= (childState == Qt::Unchecked)
295                                     | (childState == Qt::PartiallyChecked);
296         }
297     }
298
299     Qt::CheckState newState = Qt::Unchecked;
300     if (checkedDescendants)
301         newState = Qt::Checked;
302     if (checkedDescendants & uncheckedDescendants)
303         newState = Qt::PartiallyChecked;
304
305     if (newState != checkState()) {
306         m_checkState = newState;
307         emit itemChanged();
308         // update parent, if not top level
309         if (parentItem())
310             parentItem()->updateParentalCheckState();
311     }
312 }
313
314 void TestItem::updateChildCheckState(Qt::CheckState newState)
315 {
316     if (!isEnabledInEnvironment())
317         return;
318
319     if (newState != checkState()) {
320         m_checkState = newState;
321         if (itemType() == Case)
322             updateSelectedCaseCount(newState == Qt::Checked? 1: -1);
323         emit itemChanged();
324         foreach (TestItem *childItem, children())
325             childItem->updateChildCheckState(newState);
326     }
327 }
328
329 Qt::CheckState TestItem::checkState()
330 {
331     return m_checkState;
332 }
333
334 TestModel *TestItem::testModel() {
335     return m_testModel;
336 }
337
338 TestItem *TestItem::parentItem() {
339     if (itemType() != Model) {
340         return static_cast<TestItem *>(parent());
341     }
342     return 0;
343 }
344
345 int TestItem::childIndexAt(int row)
346 {
347     if (m_childIndexes.isEmpty())
348         return row;
349     else
350         return m_childIndexes.at(row);
351 }
352
353 void TestItem::moveChild(int fromRow, int toRow)
354 {
355     QList<TestItem *> childList = children();
356
357     // if in original order, init m_childIndexes
358     if (m_childIndexes.isEmpty())
359         for (int i = 0; i < childList.count(); i++)
360             m_childIndexes.append(i);
361
362     int moving = m_childIndexes.at(fromRow);
363     m_childIndexes.remove(fromRow);
364     m_childIndexes.insert(toRow, moving);
365 }
366
367 void TestItem::resetChildOrder()
368 {
369     m_childIndexes.clear();
370 }
371
372 void TestItem::updateDefinitionDom(bool selectedOnly, bool updateChildren)
373 {
374     QList<TestItem *> childItems = children();
375
376     foreach (TestItem *child, childItems) {
377         m_definitionDom.removeChild(child->definitionDom());
378     }
379
380     foreach (TestItem *child, childItems) {
381         // environments are not taken into account here, because we trust on
382         // testrunner-lite command line option
383         if (child->checkState() != Qt::Unchecked || !selectedOnly) {
384             if (updateChildren)
385                 child->updateDefinitionDom(selectedOnly, true);
386             m_definitionDom.appendChild(child->definitionDom());
387         }
388     }
389 }
390
391 QDomElement TestItem::definitionDom()
392 {
393     return m_definitionDom;
394 }
395
396 QString TestItem::name()
397 {
398     return m_name;
399 }
400
401 QString TestItem::description()
402 {
403     QDomElement descriptionElement = m_definitionDom.firstChildElement("description");
404     QString tagDescription = (!descriptionElement.isNull())? descriptionElement.text(): QString();
405     QString attrDescription = getAttributeValue(m_definitionDom, "description");
406
407     return tagDescription + (!tagDescription.isEmpty() && !attrDescription.isEmpty()? " - ": "")
408             + attrDescription;
409 }
410
411 QString TestItem::requirement()
412 {
413     QString str = getAttributeValue(m_definitionDom, "requirement");
414     if (str.isEmpty() && parentItem())
415         return parentItem()->requirement();
416     return str;
417 }
418
419 int TestItem::timeout()
420 {
421     QString str = getAttributeValue(m_definitionDom, "timeout");
422     if (str.isEmpty()) {
423         if (parentItem())
424             return parentItem()->timeout();
425         else
426             return 90;
427     } else {
428         return str.toInt();
429     }
430 }
431
432 QString TestItem::type()
433 {
434     QString str = getAttributeValue(m_definitionDom, "type");
435     if (str.isEmpty() && parentItem())
436         return parentItem()->type();
437     return str;
438 }
439
440 QString TestItem::level()
441 {
442     QString str = getAttributeValue(m_definitionDom, "level");
443     if (str.isEmpty() && parentItem())
444         return parentItem()->level();
445     return str;
446 }
447
448 bool TestItem::insignificant()
449 {
450     QString attrStr = getAttributeValue(m_definitionDom, "insignificant");
451     if (attrStr == "true")
452         return true;
453     else if (attrStr == "false")
454         return false;
455     else if (!parentItem())
456         return false;
457     else
458         return parentItem()->insignificant();
459 }
460
461 bool TestItem::manual()
462 {
463     QString attrStr = getAttributeValue(m_definitionDom, "manual");
464     if (attrStr == "true")
465         return true;
466     else if (attrStr == "false")
467         return false;
468     else if (!parentItem())
469         return false;
470     else
471         return parentItem()->manual();
472 }
473
474 QDateTime TestItem::startTime()
475 {
476     if (m_startTime.isValid()) {
477         return m_startTime;
478     } else {
479         QDateTime minStartTime;
480         foreach(TestItem *child, children()) {
481             QDateTime childStartTime = child->startTime();
482             if (childStartTime.isValid() && childStartTime > QDateTime(QDate(1990,1,1)))
483                 if (!minStartTime.isValid() || childStartTime <= minStartTime)
484                     minStartTime = childStartTime;
485         }
486         return minStartTime;
487     }
488 }
489
490 QDateTime TestItem::endTime()
491 {
492     if (m_endTime.isValid()) {
493         return m_endTime;
494     } else {
495         QDateTime maxEndTime;
496         foreach(TestItem *child, children()) {
497             QDateTime childEndTime = child->endTime();
498             if (childEndTime.isValid())
499                 if (!maxEndTime.isValid() || childEndTime >= maxEndTime)
500                     maxEndTime = childEndTime;
501         }
502         return maxEndTime;
503     }
504 }
505
506 int TestItem::duration()
507 {
508     QDateTime start = startTime();
509     QDateTime end = endTime();
510
511     if (start.isValid() && end.isValid()) {
512         return start.secsTo(end);
513     }
514     else {
515         return -1;
516     }
517 }
518
519 bool TestItem::resultsAvailable()
520 {
521     return !m_resultDom.isNull();
522 }
523
524
525 // ------- TestModel -----------------------
526
527 TestModel::TestModel(QObject *parent) :
528         TestItem(parent, TestItem::Model),
529     m_state(StateNotLoaded),
530     m_itemInExecution(0),
531     m_runModeAll(false),
532     m_currentEnvironment(QString())
533 {
534     m_definitions << new TestDef(this);
535
536     connect(TestRunnerUiSettings::instance(), SIGNAL(settingsChanged()),
537             this, SLOT(processEnvironmentChanged()));
538 }
539
540 TestResult TestModel::parseResultFromString(QString resultStr)
541 {
542     if (resultStr == tr("Passed"))
543         return ResultPassed;
544     else if (resultStr == tr("Failed"))
545         return ResultFailed;
546     else if (resultStr == tr("N/A"))
547         return ResultNotApplicable;
548     else
549         return ResultNotRun;
550 }
551
552 QString TestModel::resultAsString(TestResult result)
553 {
554     switch (result) {
555     case ResultPassed:
556         return tr("Passed");
557     case ResultFailed:
558         return tr("Failed");
559     case ResultNotApplicable:
560         return tr("N/A");
561     default:
562         return tr("");
563     }
564 }
565
566 void TestModel::setTestRunnerResultFile(const QString &path)
567 {
568     m_testRunnerResultFile = path;
569 }
570
571 TestState TestModel::state()
572 {
573     return m_state;
574 }
575
576 void TestModel::setState(TestState state)
577 {
578     if (state != m_state) {
579         m_state = state;
580         emit stateChanged(state);
581     }
582 }
583
584 void TestModel::runStateChanged(QProcess::ProcessState state)
585 {
586     if (state == QProcess::Running && m_state == StateNotRun) {
587         setState(StateRunning);
588     } else if (state == QProcess::NotRunning && m_state == StateRunning) {
589         if (QFileInfo(m_testRunnerResultFile).size() == 0) {
590             // no results == process killed
591             setState(StateError);
592         } else {
593             setState(StateRunNotSaved);
594             clearCompositeResults();
595             // TODO: replace hardcoded temp result file
596             m_definitions.at(0)->loadTempResultFile(m_testRunnerResultFile);
597             // TODO: detect stopped and error states
598             itemChanged();
599         }
600     }
601 }
602
603 void TestModel::processNewResults(TestItem *item, TestResult result)
604 {
605     if (!item || (item->itemType() != Case && item->itemType() != Step))
606         return;
607
608     if (item->itemType() == Case) {
609         item->toCase()->setResult(result);
610     } else {
611         TestStep *testStep = item->toStep();
612         testStep->setResult(result);
613         if (result != ResultPassed) {
614             TestCase *testCase = testStep->testCase();
615             for (int i = testStep->index() + 1; i < testCase->testSteps().count(); i++)
616                 testCase->testSteps().at(i)->setResult(ResultNotApplicable);
617         }
618     }
619 }
620
621 void TestModel::processEnvironmentChanged()
622 {
623     QString newEnvironment = TestRunnerUiSettings::instance()->targetTestEnvironment();
624     if (newEnvironment != m_currentEnvironment) {
625         m_currentEnvironment = newEnvironment;
626         applySelectors(selectors());
627     }
628 }
629
630 bool TestModel::loadTestDef(const QString &path)
631 {
632     clearCompositeResults();
633     resetResults(true);
634     resetCaseCount();
635     resetSelectors();
636
637     TestDef *def = findTestDef(path);
638     if (!def) {
639          def = new TestDef(this);
640          m_definitions.append(def);
641          // Only one def supported, delete old one
642          m_definitions.at(0)->unloadDefinitionFile();
643          m_definitions.removeAt(0);
644     }
645
646     if (def->loadDefinitionFile(path)) {
647         m_currentEnvironment = QString();
648         processEnvironmentChanged();
649
650         setState(StateNotRun);
651
652         emit newModelLoaded();
653         emit itemChanged();
654
655         return true;
656     }
657
658     return false;
659 }
660
661 void TestModel::closeTestDef(TestDef *testDef)
662 {
663     if (testDef) {
664         testDef->unloadDefinitionFile();
665         resetResults(true);
666         setState(StateNotLoaded);
667         resetCaseCount();
668         resetSelectors();
669         emit newModelLoaded();
670     }
671 }
672
673 void TestModel::setRunModeAll(bool runModeAll)
674 {
675     if (runModeAll != m_runModeAll) {
676         m_runModeAll = runModeAll;
677         emit itemChanged();
678     }
679 }
680
681 bool TestModel::runModeAll()
682 {
683     return m_runModeAll;
684 }
685
686 bool TestModel::saveResultsFile(const QString &path, bool overwrite)
687 {
688     if (!(m_state & StateSaveAsEnabled))
689         return false;
690
691     if (m_definitions.at(0)->saveResultsFile(path, overwrite)) {
692         setState(StateRunSaved);
693         return true;
694     }
695
696     return false;
697 }
698
699 TestItem *TestModel::findTestItem(const TestItemId &itemId)
700 {
701     if (itemId.testDefinition.isEmpty())
702         return this;
703
704     TestDef *testDef = m_definitions.at(0);
705     if (testDef == 0)
706         return 0;
707
708     if (itemId.testSuite.isEmpty())
709         return testDef;
710
711     TestSuite *testSuite = testDef->findTestSuite(itemId.testSuite);
712     if (testSuite == 0)
713         return 0;
714
715     if (itemId.testSet.isEmpty())
716         return testSuite;
717
718     TestSet *testSet = testSuite->findTestSet(itemId.testSet);
719     if (testSet == 0)
720         return 0;
721
722     if (itemId.testCase.isEmpty())
723         return testSet;
724
725     TestCase *testCase = testSet->findTestCase(itemId.testCase);
726     if (testCase == 0)
727         return 0;
728
729     if (itemId.testStep < 0)
730         return testCase;
731
732     if (testCase->testSteps().count() <= itemId.testStep)
733         return 0;
734
735     return testCase->testSteps().at(itemId.testStep);
736 }
737
738 TestDef *TestModel::findTestDef(const QFileInfo &fileInfo)
739 {
740     if (fileInfo == m_definitions.at(0)->definitionFile())
741         return m_definitions.at(0);
742     return 0;
743 }
744
745 void TestModel::itemResultEntered()
746 {
747     TestItem *testItem = qobject_cast<TestItem*>(sender());
748     emit resultsEntered(testItem);
749 }
750
751 void TestModel::resetResults(bool resetManualResults)
752 {
753     m_runModeAll = false;
754
755     if (state() != StateRunNotSaved && state() != StateRunSaved && state() != StateError)
756         return;
757
758     clearCompositeResults();
759
760     foreach (TestDef *testDef, testDefs()) {
761         testDef->resetResults(resetManualResults);
762     }
763
764     setState(StateNotRun);
765     emit itemChanged();
766 }
767
768 void TestModel::resetTestOrder()
769 {
770     if (!(state() & StateDefEditEnabled))
771         return;
772
773     emit itemsToBeMoved();
774
775     foreach (TestDef *testDef, testDefs()) {
776         foreach (TestSuite *testSuite, testDef->testSuites()) {
777             foreach (TestSet *testSet, testSuite->testSets()) {
778                 testSet->resetChildOrder();
779             }
780             testSuite->resetChildOrder();
781         }
782         testDef->resetChildOrder();
783     }
784     this->resetChildOrder();
785     emit itemsMoved();
786 }
787
788 QList<TestDef*> TestModel::testDefs() const
789 {
790     return QList<TestDef *>(m_definitions);
791 }
792
793 QList<TestItem *> TestModel::children()
794 {
795     QList<TestItem *> children;
796     for (int i = 0; i < testDefs().count(); i++)
797         children.append(testDefs().at(childIndexAt(i)));
798
799     return children;
800 }
801
802 void TestModel::emitItemExecutionStarted(TestItem *item)
803 {
804     m_itemInExecution = item;
805     if (item)
806         item->setIsInExecution(true);
807     emit itemExecutionStarted(item);
808 }
809
810 void TestModel::emitItemExecutionEnded(TestItem *item)
811 {
812     m_itemInExecution = item? item->parentItem(): 0;
813     if (item)
814         item->setIsInExecution(false);
815     emit itemExecutionEnded(item);
816 }
817
818 TestItem *TestModel::itemInExecution()
819 {
820     return m_itemInExecution;
821 }
822
823 void TestModel::processManualResultsWritten(TestItem *item)
824 {
825     if (item && item->itemType() == TestItem::Step) {
826         TestStep *step = item->toStep();
827         step->setResult(step->manualResult());
828     }
829 }
830
831 void TestModel::emitItemChanged()
832 {
833     TestItem *item = static_cast<TestItem *>(sender());
834     emit testItemChanged(item);
835 }
836
837 // ------- TestDef -----------------------
838
839 TestDef::TestDef(QObject *parent) :
840         TestItem(parent, TestItem::Def),
841     m_state(StateNotLoaded),
842     m_definitionFile(""),
843     m_resultFile("")
844 {
845 }
846
847 TestState TestDef::state()
848 {
849     return m_state;
850 }
851
852 void TestDef::setState(TestState state)
853 {
854     if (state != m_state) {
855         m_state = state;
856     }
857 }
858
859 bool TestDef::loadDefinitionFile(const QString &path)
860 {
861     unloadDefinitionFile();
862
863     if (QFile::exists(path)) {
864         QFile file(path);
865         if (file.open(QIODevice::ReadOnly)) {
866             QString errorStr;
867             int errorLine;
868             int errorColumn;
869
870             /*** TODO: validate ***/
871
872             testDefinitionDom.setContent(&file, true, &errorStr, &errorLine, &errorColumn);
873
874             m_definitionDom = testDefinitionDom.documentElement();
875
876             QDomElement suiteElement = m_definitionDom.firstChildElement("suite");
877             while(!suiteElement.isNull()) {
878                 TestSuite *suite = new TestSuite(this);
879                 connect(suite, SIGNAL(itemChanged()), testModel(), SLOT(emitItemChanged()));
880                 suite->parseDefinitionDom(suiteElement);
881                 m_suites.append(suite);
882
883                 suiteElement = suiteElement.nextSiblingElement("suite");
884             }
885
886             m_name = QFileInfo(path).absoluteFilePath();
887             m_definitionFile = QFileInfo(path);
888             setState(StateNotRun);
889
890             emit itemChanged();
891
892             return true;
893         } else {
894             qDebug() << __PRETTY_FUNCTION__ << "Failed to open file: " << path;
895         }
896     } else {
897         qDebug() << __PRETTY_FUNCTION__ << "File does not exist";
898     }
899
900     return false;
901 }
902
903 void TestDef::unloadDefinitionFile()
904 {
905     m_definitionFile = QFileInfo("");
906     m_suites.clear();
907     setState(StateNotLoaded);
908 }
909
910 bool TestDef::saveTempDefinitionFile(const QString &path, bool selectedOnly)
911 {
912     QFile file(path);
913     if (file.open(QIODevice::WriteOnly)) {
914
915         updateDefinitionDom(selectedOnly, true);
916
917         QByteArray content = testDefinitionDom.toByteArray();
918         file.write(content);
919         file.close();
920         return true;
921     } else {
922         qDebug() << __PRETTY_FUNCTION__ << "Failed to open file: " << path;
923     }
924
925     return false;
926 }
927
928 void TestDef::loadTempResultFile(const QString &path)
929 {
930     //qDebug() << __PRETTY_FUNCTION__ << path;
931
932     if (QFile::exists(path)) {
933         QFile file(path);
934         if (file.open(QIODevice::ReadOnly)) {
935             QString errorStr;
936             int errorLine;
937             int errorColumn;
938
939             /*** TODO: validate ***/
940
941             testResultDom.setContent(&file, true, &errorStr, &errorLine, &errorColumn);
942             clearCompositeResults();
943
944             m_resultDom = testResultDom.documentElement();
945
946             QDomElement suiteElement = m_resultDom.firstChildElement("suite");
947             while(!suiteElement.isNull()) {
948                 QString suiteName = getAttributeValue(suiteElement, "name");
949                 TestSuite *suite = findTestSuite(suiteName);
950                 if (suite) {
951                     suite->parseResultDom(suiteElement);
952                 }
953                 suiteElement = suiteElement.nextSiblingElement("suite");
954             }
955             emit itemChanged();
956             file.close();
957             setState(StateRunNotSaved);
958         } else {
959             qDebug() << __PRETTY_FUNCTION__ << "Failed to open file: " << path;
960         }
961     } else {
962         qDebug() << __PRETTY_FUNCTION__ << "File does not exist";
963     }
964 }
965
966 bool TestDef::saveResultsFile(const QString &path, bool overwrite)
967 {
968     if (!(m_state & StateSaveAsEnabled))
969         return false;
970
971     if (!QFile::exists(path) || overwrite) {
972         QFile file(path);
973         if (file.open(QIODevice::WriteOnly)) {
974
975             QByteArray content = testResultDom.toByteArray();
976
977             file.write(content);
978
979             m_resultFile = QFileInfo(path);
980             setState(StateRunSaved);
981             file.close();
982             return true;
983         } else {
984             qDebug() << __PRETTY_FUNCTION__ << "Failed to open file: " << path;
985         }
986     } else {
987         qDebug() << __PRETTY_FUNCTION__ << "File exists";
988     }
989
990     return false;
991 }
992
993 const QFileInfo TestDef::definitionFile()
994 {
995     return m_definitionFile;
996 }
997
998 const QFileInfo TestDef::resultFile()
999 {
1000     return m_resultFile;
1001 }
1002
1003 TestSuite *TestDef::findTestSuite(const QString &name)
1004 {
1005     QList<TestSuite*> suites = testSuites();
1006     for (int i = 0; i < suites.length(); i++) {
1007         if (suites[i]->name() == name)
1008             return suites[i];
1009     }
1010     return 0;
1011 }
1012
1013 void TestDef::resetResults(bool resetManualResults)
1014 {
1015     foreach (TestSuite *testSuite, testSuites()) {
1016         testSuite->parseResultDom(QDomElement());
1017         foreach (TestSet *testSet, testSuite->testSets()) {
1018             testSet->parseResultDom(QDomElement());
1019             foreach (TestCase *testCase, testSet->testCases()) {
1020                 testCase->parseResultDom(QDomElement());
1021                 if (resetManualResults) {
1022                     testCase->setManualResult(ResultNotRun);
1023                     testCase->setComment(QString());
1024                 }
1025                 foreach (TestStep *testStep, testCase->testSteps()) {
1026                     testStep->parseResultDom(QDomElement());
1027                     if (resetManualResults)
1028                         testStep->setManualResult(ResultNotRun);
1029                 }
1030             }
1031         }
1032     }
1033
1034     m_resultFile = QFileInfo("");
1035     setState(StateNotRun);
1036     emit itemChanged();
1037 }
1038
1039 QList<TestSuite *> TestDef::testSuites() const
1040 {
1041     return QList<TestSuite *>(m_suites);
1042 }
1043
1044 QList<TestItem *> TestDef::children()
1045 {
1046     QList<TestItem *> children;
1047     for (int i = 0; i < testSuites().count(); i++)
1048         children.append(testSuites().at(childIndexAt(i)));
1049
1050     return children;
1051 }
1052
1053 // ------------- TestSuite -------------
1054
1055
1056 TestSuite::TestSuite(QObject *parent) :
1057         TestItem(parent, TestItem::Suite)
1058 {
1059 }
1060
1061 TestSuite::~TestSuite()
1062 {
1063 }
1064
1065 TestDef *TestSuite::testDef()
1066 {
1067     return qobject_cast<TestDef*>(parent());
1068 }
1069
1070 void TestSuite::parseDefinitionDom(const QDomElement &element)
1071 {
1072     m_definitionDom = element;
1073
1074     QDomElement setElement = element.firstChildElement("set");
1075
1076     m_name = getAttributeValue(element, "name");
1077
1078     while(!setElement.isNull()) {
1079         TestSet *set = new TestSet(this);
1080         connect(set, SIGNAL(itemChanged()), testModel(), SLOT(emitItemChanged()));
1081         set->parseDefinitionDom(setElement);
1082         m_sets.append(set);
1083         setElement = setElement.nextSiblingElement("set");
1084     }
1085 }
1086
1087 void TestSuite::parseResultDom(const QDomElement &element)
1088 {
1089     m_resultDom = element;
1090     clearCompositeResults();
1091
1092     if (!m_resultDom.isNull()) {
1093         QDomElement setElement = element.firstChildElement("set");
1094         while(!setElement.isNull()) {
1095             QString setName = getAttributeValue(setElement, "name");
1096             TestSet *set = findTestSet(setName);
1097             if (set)
1098                 set->parseResultDom(setElement);
1099             setElement = setElement.nextSiblingElement("set");
1100         }
1101     }
1102
1103     emit itemChanged();
1104 }
1105
1106 QList<TestSet *> TestSuite::testSets() const
1107 {
1108     return QList<TestSet *>(m_sets);
1109 }
1110
1111 TestSet *TestSuite::findTestSet(QString name)
1112 {
1113     QList<TestSet*> sets = testSets();
1114     for (int i = 0; i < sets.length(); i++)
1115         if (sets[i]->name() == name)
1116             return sets[i];
1117
1118     return 0;
1119 }
1120
1121 QList<TestItem *> TestSuite::children()
1122 {
1123     QList<TestItem *> children;
1124     for (int i = 0; i < testSets().count(); i++)
1125         children.append(testSets().at(childIndexAt(i)));
1126     return children;
1127 }
1128
1129 QString TestSuite::domain()
1130 {
1131     return getAttributeValue(m_definitionDom, "domain");
1132 }
1133
1134
1135 // ------------- TestSet ---------------
1136
1137 TestSet::TestSet(QObject *parent) :
1138         TestItem(parent, TestItem::Set)
1139 {
1140     //m_cases = new QList<TestCase *>();
1141 }
1142
1143 TestSet::~TestSet()
1144 {
1145 }
1146
1147 TestSuite *TestSet::testSuite()
1148 {
1149     return qobject_cast<TestSuite*>(parent());
1150 }
1151
1152 void TestSet::parseDefinitionDom(const QDomElement &element)
1153 {
1154     m_definitionDom = element;
1155
1156     m_name = getAttributeValue(element, "name");
1157
1158     m_targetTestEnvironments = QStringList() << "hardware" << "scratchbox";
1159     QDomElement envBlockElement = element.firstChildElement("environments");
1160     if (!envBlockElement.isNull()) {
1161         QDomElement envElement = envBlockElement.firstChildElement();
1162         while (!envElement.isNull()) {
1163             if (envElement.text().trimmed().toLower()=="false")
1164                 m_targetTestEnvironments.removeAll(envElement.tagName());
1165             envElement = envElement.nextSiblingElement();
1166         }
1167     }
1168
1169     QDomElement caseElement = element.firstChildElement("case");
1170     while(!caseElement.isNull()) {
1171         TestCase *testCase = new TestCase(this);
1172         connect(testCase, SIGNAL(itemChanged()), testModel(), SLOT(emitItemChanged()));
1173         testCase->parseDefinitionDom(caseElement);
1174         m_cases.append(testCase);
1175         caseElement = caseElement.nextSiblingElement("case");
1176     }
1177 }
1178
1179 void TestSet::parseResultDom(const QDomElement &element)
1180 {
1181     m_resultDom = element;
1182     clearCompositeResults();
1183
1184     if (!m_resultDom.isNull()) {
1185         QDomElement caseElement = element.firstChildElement("case");
1186         while(!caseElement.isNull()) {
1187             QString caseName = getAttributeValue(caseElement, "name");
1188             TestCase *testCase = findTestCase(caseName);
1189             if (testCase)
1190                 testCase->parseResultDom(caseElement);
1191             caseElement = caseElement.nextSiblingElement("case");
1192         }
1193     }
1194
1195     emit itemChanged();
1196 }
1197
1198 QList<TestCase *> TestSet::testCases() const
1199 {
1200     return QList<TestCase *>(m_cases);
1201 }
1202
1203 TestCase *TestSet::findTestCase(QString name)
1204 {
1205     QList<TestCase*> cases = testCases();
1206     for (int i = 0; i < cases.length(); i++)
1207         if (cases[i]->name() == name)
1208             return cases[i];
1209
1210     return 0;
1211 }
1212
1213 bool TestSet::isEnabledInEnvironment(QString environment)
1214 {
1215     if (environment.isEmpty())
1216         environment = TestRunnerUiSettings::instance()->targetTestEnvironment();
1217     return m_targetTestEnvironments.contains(environment);
1218 }
1219
1220 QString TestSet::targetTestEnvironments()
1221 {
1222     return m_targetTestEnvironments.join(", ");
1223 }
1224
1225 QString TestSet::feature()
1226 {
1227     return getAttributeValue(m_definitionDom, "feature");
1228 }
1229
1230
1231 QList<TestItem *> TestSet::children()
1232 {
1233     QList<TestItem *> children;
1234     for (int i = 0; i < testCases().count(); i++)
1235         children.append(testCases().at(childIndexAt(i)));
1236     return children;
1237 }
1238
1239 // ------------- TestCase --------------
1240
1241
1242 TestCase::TestCase(QObject *parent) :
1243     TestItem(parent, TestItem::Case),
1244     m_result(ResultNotRun),
1245     m_manualResult(ResultNotRun),
1246     m_comment(QString())
1247 {
1248     //m_steps = new QList<TestStep*>();
1249     updateTotalCaseCount(1);
1250 }
1251
1252 TestCase::~TestCase()
1253 {
1254 }
1255
1256 TestSet *TestCase::testSet()
1257 {
1258     return qobject_cast<TestSet*>(parent());
1259 }
1260
1261 void TestCase::parseDefinitionDom(const QDomElement &element)
1262 {
1263     m_definitionDom = element;
1264
1265     m_name = getAttributeValue(element, "name");
1266
1267     QDomElement stepElement = element.firstChildElement("step");
1268
1269     CaseSelectors selectors;
1270     QString requirementStr = requirement();
1271     selectors.requirements = parseSelectorString(requirement());
1272     selectors.features = parseSelectorString(testSet()->feature());
1273     selectors.types = parseSelectorString(type());
1274     selectors.manual = manual();
1275     selectors.automatic = !selectors.manual;
1276     addSelectors(selectors);
1277
1278     int index = 0;
1279     while(!stepElement.isNull()) {
1280         TestStep *step = new TestStep(index++, this);
1281         connect(step, SIGNAL(itemChanged()), testModel(), SLOT(emitItemChanged()));
1282         step->parseDefinitionDom(stepElement);
1283         m_steps.append(step);
1284         stepElement = stepElement.nextSiblingElement("step");
1285     }
1286
1287     emit itemChanged();
1288 }
1289
1290 void TestCase::parseResultDom(const QDomElement & element)
1291 {
1292     m_resultDom = element;
1293
1294     setResult(ResultNotRun);
1295     clearCompositeResults();
1296
1297     if (!m_resultDom.isNull()) {
1298         QString result = getAttributeValue(m_resultDom, "result");
1299         setResult(parseResultFromDomString(result));
1300
1301         QString domComment = getAttributeValue(m_resultDom, "comment");
1302
1303         if (isCommentEditable())
1304             setComment(comment()); // discard dom commment
1305         else
1306             setComment(domComment);
1307
1308         QDomElement stepElement = element.firstChildElement("step");
1309         foreach (TestStep *step, testSteps()) {
1310             if (stepElement.isNull())
1311                 break;
1312             step->parseResultDom(stepElement);
1313             stepElement = stepElement.nextSiblingElement("step");
1314         }
1315     } else {
1316         setComment(QString());
1317     }
1318
1319     emit itemChanged();
1320 }
1321
1322 QString TestCase::subfeature()
1323 {
1324     return getAttributeValue(m_definitionDom, "subfeature");
1325 }
1326
1327
1328 void TestCase::setResult(TestResult result)
1329 {
1330     if (m_result != result) {
1331         m_result = result;
1332         processNewResult(result);
1333         // emit itemChanged();  // emited now in processNewResult
1334     }
1335 }
1336
1337 TestResult TestCase::result()
1338 {
1339     return m_result;
1340 }
1341
1342 void TestCase::setManualResult(TestResult result)
1343 {
1344     foreach (TestStep *step, testSteps())
1345         step->setManualResult(result);
1346
1347     if (m_manualResult != result) {
1348         m_manualResult = result;
1349         emit itemChanged();
1350     }
1351 }
1352
1353 /** Updates the manual result of TestCase when manual result
1354   * of one of the steps has been entered.
1355  */
1356 void TestCase::updateManualResult()
1357 {
1358     bool resultFound = false;
1359     TestResult result = ResultNotRun;
1360
1361     foreach (TestStep *step, testSteps()) {
1362         if (step->manual()) {
1363             if (!resultFound) {
1364                 result = step->manualResult();
1365                 resultFound = true;
1366             } else if (result != step->manualResult()) {
1367                 result = ResultNotRun;
1368             }
1369         }
1370     }
1371
1372     if (result != m_manualResult) {
1373         m_manualResult = result;
1374         emit itemChanged();
1375     }
1376
1377 }
1378
1379 TestResult TestCase::manualResult()
1380 {
1381     return m_manualResult;
1382 }
1383
1384 bool TestCase::isSemiAutomatic()
1385 {
1386     int manualStepCount = 0;
1387     foreach (TestStep *step, testSteps())
1388         if (step->manual())
1389             manualStepCount++;
1390
1391     return manualStepCount != 0 && manualStepCount != testSteps().count();
1392 }
1393
1394 bool TestCase::isManualResultEditable()
1395 {
1396     return (manual()
1397             && !isSemiAutomatic()
1398             && result() == ResultNotRun
1399             && (testModel()->state() & StateResultEditEnabled)
1400             && (testModel()->state() != StateRunning)
1401             && (testModel()->runModeAll() || checkState() == Qt::Checked));
1402 }
1403
1404 bool TestCase::isCommentEditable()
1405 {
1406     return (manual()
1407             && (testModel()->state() & StateCommentEditEnabled)
1408             && (testModel()->runModeAll() || checkState() == Qt::Checked));
1409 }
1410
1411 QString TestCase::comment()
1412 {
1413     return m_comment;
1414 }
1415
1416 void TestCase::setComment(QString comment)
1417 {
1418     if (m_comment != comment) {
1419         m_comment = comment;
1420         if (testModel()->state() == StateRunSaved)
1421             testModel()->setState(StateRunNotSaved);
1422         emit itemChanged();
1423     }
1424     if (resultsAvailable()) {
1425         m_resultDom.setAttribute("comment", comment);
1426     }
1427 }
1428
1429 QString TestCase::failureInfo()
1430 {
1431     return getAttributeValue(m_resultDom, "failure_info");
1432 }
1433
1434 QList<TestStep*> TestCase::testSteps() const
1435 {
1436     return QList<TestStep *>(m_steps);
1437 }
1438
1439 QList<TestItem *> TestCase::children()
1440 {
1441     QList<TestItem *> children;
1442     for (int i = 0; i < testSteps().count(); i++)
1443         children.append(testSteps().at(childIndexAt(i)));
1444     return children;
1445 }
1446
1447 void TestCase::applySelectors(CaseSelectors applied)
1448 {
1449     // find out if the selectors in the definition and the applied selectors
1450     // have common items
1451     bool selected = true;
1452     QStringList tmp = QStringList() << selectors().requirements << applied.requirements;
1453     QStringList tmp2(tmp);
1454     tmp2.removeDuplicates();
1455     if (tmp.count() == tmp2.count())
1456         selected = false;
1457     tmp = QStringList() << selectors().features << applied.features;
1458     tmp2 = QStringList(tmp);
1459     tmp2.removeDuplicates();
1460     if (tmp.count() == tmp2.count())
1461         selected = false;
1462     tmp = QStringList() << selectors().types << applied.types;
1463     tmp2 = QStringList(tmp);
1464     tmp2.removeDuplicates();
1465     if (tmp.count() == tmp2.count())
1466         selected = false;
1467     if ((manual() && !applied.manual) || (!manual() && !applied.automatic))
1468         selected = false;
1469
1470     if (!isEnabledInEnvironment())
1471         selected = false;
1472
1473     // There is not setCheckState(), so toggle if needed
1474     if (checkState() != (selected? Qt::Checked: Qt::Unchecked))
1475         toggleCheckState();
1476 }
1477 // ------------- TestStep --------------
1478
1479 TestStep::TestStep(int index, QObject *parent) :
1480         TestItem(parent, TestItem::Step),
1481         m_index(index),
1482         m_result(ResultNotRun),
1483         m_manualResult(ResultNotRun)
1484 {
1485     connect(this, SIGNAL(resultEntered()), testModel(), SLOT(itemResultEntered()));
1486 }
1487
1488 TestStep::~TestStep()
1489 {
1490
1491 }
1492
1493 int TestStep::index()
1494 {
1495     return m_index;
1496 }
1497
1498 TestCase *TestStep::testCase()
1499 {
1500     return qobject_cast<TestCase*>(parent());
1501 }
1502
1503 void TestStep::parseDefinitionDom(const QDomElement &element)
1504 {
1505     m_definitionDom = element;
1506 }
1507
1508 void TestStep::parseResultDom(const QDomElement & element)
1509 {
1510     m_resultDom = element;
1511
1512     setResult(ResultNotRun);
1513     m_startTime = QDateTime();
1514     m_endTime = QDateTime();
1515
1516     if (!m_resultDom.isNull()) {
1517         QString result = getAttributeValue(m_resultDom, "result");
1518         setResult(parseResultFromDomString(result));
1519         m_startTime = parseTime(m_resultDom.firstChildElement("start"));
1520         m_endTime = parseTime(m_resultDom.firstChildElement("end"));
1521     }
1522
1523     emit itemChanged();
1524 }
1525
1526 QString TestStep::command()
1527 {
1528     return m_definitionDom.text();
1529 }
1530
1531 QString TestStep::stdout()
1532 {
1533     if (!m_resultDom.isNull()) {
1534         QDomElement element = m_resultDom.firstChildElement("stdout");
1535         if (!element.isNull())
1536             return element.text();
1537     }
1538     return QString();
1539 }
1540
1541 QString TestStep::stderr()
1542 {
1543     if (!m_resultDom.isNull()) {
1544         QDomElement element = m_resultDom.firstChildElement("stderr");
1545         if (!element.isNull())
1546             return element.text();
1547     }
1548     return QString();
1549 }
1550
1551 TestResult TestStep::result()
1552 {
1553     return m_result;
1554 }
1555
1556 void TestStep::setResult(TestResult result)
1557 {
1558     if (result != m_result) {
1559         m_result = result;
1560         emit itemChanged();
1561     }
1562 }
1563
1564 void TestStep::setManualResult(TestResult result)
1565 {
1566     if (m_manualResult != result) {
1567         m_manualResult = result;
1568         emit resultEntered();
1569         emit itemChanged();
1570         testCase()->updateManualResult();
1571     }
1572 }
1573
1574 TestResult TestStep::manualResult()
1575 {
1576     return m_manualResult;
1577 }
1578
1579 bool TestStep::allAutoStepsBeforeHaveResults()
1580 {
1581     QList<TestStep *> steps = testCase()->testSteps();
1582
1583     for (int i = index(); i >= 0; i--) {
1584         if (!steps.at(i)->manual())
1585             return false;
1586         else if (steps.at(i)->isInExecution())
1587             return true;
1588     }
1589
1590     return true;
1591 }
1592
1593 bool TestStep::isManualResultEditable()
1594 {
1595     return (manual()
1596             && result() == ResultNotRun
1597             && allAutoStepsBeforeHaveResults()
1598             && (testModel()->state() & StateResultEditEnabled)
1599             && (testModel()->runModeAll() || parentItem()->checkState() == Qt::Checked));
1600 }
1601
1602
1603 QString TestStep::failureInfo()
1604 {
1605     if (!m_resultDom.isNull())
1606         return getAttributeValue(m_resultDom, "failure_info");
1607     else
1608         return QString();
1609
1610 }
1611
1612 int TestStep::expectedResult()
1613 {
1614     if (!m_resultDom.isNull()) {
1615         QDomElement element = m_resultDom.firstChildElement("expected_result");
1616         if (!element.isNull())
1617             return element.text().toInt();
1618     }
1619     QString str = getAttributeValue(m_definitionDom, "expected_result");
1620     if (!str.isEmpty())
1621         return str.toInt();
1622     else
1623         return 0;
1624 }
1625
1626 int TestStep::returnCode()
1627 {
1628     if (!m_resultDom.isNull()) {
1629         QDomElement element = m_resultDom.firstChildElement("return_code");
1630         if (!element.isNull())
1631             return element.text().toInt();
1632     }
1633     return 0;
1634 }