|
1 /* |
|
2 * Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
3 * All rights reserved. |
|
4 * This component and the accompanying materials are made available |
|
5 * under the terms of "Eclipse Public License v1.0" |
|
6 * which accompanies this distribution, and is available |
|
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html". |
|
8 * |
|
9 * Initial Contributors: |
|
10 * Nokia Corporation - initial contribution. |
|
11 * |
|
12 * Contributors: |
|
13 * |
|
14 * Description: |
|
15 * |
|
16 */ |
|
17 |
|
18 #include "testrunner.h" |
|
19 #include <QtTest/QtTest> |
|
20 #include <QDir> |
|
21 #include <stdio.h> |
|
22 |
|
23 const char testCaseElement[] = "TestCase"; |
|
24 const char testFunctionElement[] = "TestFunction"; |
|
25 const char incidentElement[] = "Incident"; |
|
26 const char descriptionElement[] = "Description"; |
|
27 const char nameAttr[] = "name"; |
|
28 const char typeAttr[] = "type"; |
|
29 const char fileAttr[] = "file"; |
|
30 const char lineAttr[] = "line"; |
|
31 const char attrValueFail[] = "fail"; |
|
32 |
|
33 |
|
34 TestRunner::TestRunner(const QString& name, const QString& combinedOutputFileName) |
|
35 : mTestCount(0), |
|
36 mParsingIncidentElement(false), |
|
37 mParsingDescriptionElement(false), |
|
38 mCurrentTestFailed(false), |
|
39 mCurrentTestFailureLine(0), |
|
40 mCombinedOutputFileName(combinedOutputFileName) |
|
41 { |
|
42 mTestRunParams.append(name); |
|
43 mTestRunParams.append("-xml"); |
|
44 mTestRunParams.append("-o"); |
|
45 mHomeDir = QDir::homePath(); |
|
46 mTestRunParams.append(QString()); // Initial result file name |
|
47 |
|
48 if (!mHomeDir.endsWith(QString::fromAscii("/"))) |
|
49 mHomeDir += QString::fromAscii("/"); |
|
50 } |
|
51 |
|
52 TestRunner::~TestRunner() |
|
53 { |
|
54 } |
|
55 |
|
56 int TestRunner::runTests(QObject& testObject) |
|
57 { |
|
58 QString className(testObject.metaObject()->className()); |
|
59 printf("Running tests for %s ... ", className.toUtf8().data()); |
|
60 QString resultFileName = mHomeDir + className + ".xml"; |
|
61 mTestRunParams.replace(mTestRunParams.count()-1,resultFileName); |
|
62 int errorsBefore = mErrors.count(); |
|
63 int error = QTest::qExec(&testObject, mTestRunParams); |
|
64 parse(resultFileName); |
|
65 mResultFiles.append(resultFileName); |
|
66 printf("Failures: %d\n",mErrors.count()-errorsBefore); |
|
67 fflush(stdout); |
|
68 return error; |
|
69 } |
|
70 |
|
71 void TestRunner::printResults() |
|
72 { |
|
73 printf("\nTests executed: %d\n",mTestCount); |
|
74 if (mErrors.count() > 0) { |
|
75 printf("Failures (%d):\n", mErrors.count()); |
|
76 foreach(QString error, mErrors) { |
|
77 printf("\n%s", error.toUtf8().data()); |
|
78 } |
|
79 printf("\n"); |
|
80 } else { |
|
81 printf("All passed.\n\n"); |
|
82 } |
|
83 fflush(stdout); |
|
84 |
|
85 combineResults(); |
|
86 } |
|
87 |
|
88 void TestRunner::parse(const QString& fileName) |
|
89 { |
|
90 QFile file(fileName); |
|
91 QXmlInputSource inputSource(&file); |
|
92 QXmlSimpleReader reader; |
|
93 reader.setContentHandler(this); |
|
94 reader.parse(inputSource); |
|
95 } |
|
96 |
|
97 bool TestRunner::startElement( |
|
98 const QString& /*namespaceURI*/, |
|
99 const QString& /*localName*/, |
|
100 const QString& qName, |
|
101 const QXmlAttributes& atts) |
|
102 { |
|
103 if (qName == QString::fromAscii(testFunctionElement)) { |
|
104 mTestCount++; |
|
105 mCurrentTestName = atts.value(QString::fromAscii(nameAttr)); |
|
106 return true; |
|
107 } |
|
108 if (qName == QString::fromAscii(incidentElement)) { |
|
109 mParsingIncidentElement = true; |
|
110 if (atts.value(QString::fromAscii(typeAttr)) == QString::fromAscii(attrValueFail)) { |
|
111 mCurrentTestFailed = true; |
|
112 mCurrentTestFile = atts.value(QString::fromAscii(fileAttr)); |
|
113 mCurrentTestFailureLine = atts.value(QString::fromAscii(lineAttr)).toInt(); |
|
114 } |
|
115 return true; |
|
116 } |
|
117 mParsingDescriptionElement = |
|
118 (qName == QString::fromAscii(descriptionElement)); |
|
119 return true; |
|
120 } |
|
121 |
|
122 bool TestRunner::endElement( |
|
123 const QString& /*namespaceURI*/, |
|
124 const QString& /*localName*/, |
|
125 const QString& qName) |
|
126 { |
|
127 if (qName == QString::fromAscii(incidentElement)) { |
|
128 mParsingIncidentElement = false; |
|
129 mCurrentTestFailed = false; |
|
130 return true; |
|
131 } |
|
132 if (qName == QString::fromAscii(descriptionElement)) { |
|
133 mParsingDescriptionElement = false; |
|
134 } |
|
135 return true; |
|
136 } |
|
137 |
|
138 bool TestRunner::characters(const QString& ch) |
|
139 { |
|
140 if (mParsingIncidentElement && |
|
141 mParsingDescriptionElement && |
|
142 mCurrentTestFailed) { |
|
143 QByteArray testResult = mCurrentTestName.toAscii() + " failed:\n"; |
|
144 testResult += "File: "; |
|
145 testResult += mCurrentTestFile.toAscii(); |
|
146 testResult += "\n"; |
|
147 testResult += "Line: "; |
|
148 testResult += QByteArray::number(mCurrentTestFailureLine); |
|
149 testResult += "\n"; |
|
150 testResult += "Reason: "; |
|
151 testResult += ch.toAscii(); |
|
152 testResult += "\n"; |
|
153 mErrors.append(QString::fromAscii(testResult.data())); |
|
154 } |
|
155 return true; |
|
156 } |
|
157 |
|
158 void TestRunner::combineResults() |
|
159 { |
|
160 if ( mCombinedOutputFileName.isEmpty() ){ |
|
161 return; |
|
162 } |
|
163 if ( !mCombinedOutputFileName.contains(QString::fromAscii("/")) ){ |
|
164 mCombinedOutputFileName.prepend( mHomeDir ); |
|
165 } |
|
166 QFile file(mCombinedOutputFileName); |
|
167 if (!file.open(QIODevice::WriteOnly)){ |
|
168 return; |
|
169 } |
|
170 |
|
171 QXmlStreamWriter writer(&file); |
|
172 writer.setAutoFormatting(true); |
|
173 writer.writeStartDocument(); |
|
174 QString caseName; |
|
175 foreach ( QString resultFile, mResultFiles ){ |
|
176 QFile readFile( resultFile ); |
|
177 if ( resultFile != mCombinedOutputFileName && readFile.open(QIODevice::ReadOnly) ){ |
|
178 QXmlStreamReader reader(&readFile); |
|
179 appendToXml(writer, reader, caseName); |
|
180 readFile.close(); |
|
181 } |
|
182 } |
|
183 writer.writeEndDocument(); |
|
184 file.close(); |
|
185 } |
|
186 |
|
187 void TestRunner::appendToXml(QXmlStreamWriter& writer, QXmlStreamReader& reader, QString& caseName) |
|
188 { |
|
189 while (!reader.atEnd()) { |
|
190 QXmlStreamReader::TokenType type = reader.readNext(); |
|
191 if ( type == QXmlStreamReader::StartDocument || |
|
192 type == QXmlStreamReader::EndDocument ){ |
|
193 // Ignored |
|
194 } else if ( reader.name() == testCaseElement ){ |
|
195 if ( type == QXmlStreamReader::StartElement ){ |
|
196 QString tempCaseName = reader.attributes().value(nameAttr).toString(); |
|
197 if ( caseName.isEmpty() && type == QXmlStreamReader::StartElement){ |
|
198 writer.writeStartElement(testCaseElement); |
|
199 writer.writeAttribute(nameAttr, mTestRunParams.at(0)); |
|
200 } |
|
201 caseName = tempCaseName; |
|
202 } |
|
203 } else if ( type == QXmlStreamReader::StartElement ) { |
|
204 if ( reader.name() == testFunctionElement ){ |
|
205 QString tempFuncName = reader.attributes().value(nameAttr).toString(); |
|
206 if ( !tempFuncName.isEmpty() ){ |
|
207 writer.writeStartElement(testFunctionElement); |
|
208 writer.writeAttribute(nameAttr, caseName + "::" + tempFuncName); |
|
209 } |
|
210 } else { |
|
211 writer.writeCurrentToken( reader ); |
|
212 } |
|
213 } else { |
|
214 writer.writeCurrentToken( reader ); |
|
215 } |
|
216 } |
|
217 } |