LIMA
Libre Multilingual Analyzer — C++ API
Loading...
Searching...
No Matches
TestCasesReader.cpp
Go to the documentation of this file.
1// Copyright 2002-2022 CEA LIST
2// SPDX-FileCopyrightText: 2022 CEA LIST <gael.de-chalendar@cea.fr>
3//
4// SPDX-License-Identifier: MIT
5
6/***************************************************************************
7 * @author Benoit Mathieu
8 ***************************************************************************/
9
13
14#include <QXmlStreamReader>
15
16#include <iostream>
17
18namespace Lima
19{
20namespace Common
21{
22namespace TGV
23{
24
25char const * const FATAL_ERROR_TYPE = "bloquant";
26int const EXIT_FATAL_ERROR = 64;
27
29{
30 friend class TestCasesReader;
31
32public:
35
36 bool parse(QIODevice *device);
37 void readTestcases();
38 void readTestcase();
39 void readCallParameters();
40 void readParam();
41 void readList();
42 void readMap();
43 void readItem();
44 void readExpl();
45 void readTest();
46 void readText();
47
48 QXmlStreamReader m_reader;
49
50 std::map<std::string, TestCasesReader::TestReport> m_reportByType;
51
52
55
56 std::string getName(const QString& localName,
57 const QString& qName);
58
60 bool m_inMap;
61 std::string m_listKey;
62 std::string m_mapKey;
63
65};
66
67
69 : m_reportByType(), m_processor(processor), m_hasFatalError(false)
70{
71}
72
77
79{
80 delete m_d;
81}
82
83const std::map<std::string, TestCasesReader::TestReport>& TestCasesReader::reportByType() const
84{
85 return m_d->m_reportByType;
86}
87
88bool TestCasesReader::parse(QIODevice *device)
89{
90 return m_d->parse(device);
91}
92
93// <?xml version='1.0' encoding='UTF-8'?>
94// <testcases>
95// <testcase id="eng.se.DATE.1" type="bloquant">
96// <call-parameters>
97// <param key="text" value="he was born on 10 April 1950."/>
98// <list key="pipelines">
99// <item value="limaserver"/>
100// </list>
101// …
102// </call-parameters>
103// <expl>rule for complete date with cardinal number</expl>
104// <test id="eng.se.DATE.1.1" trace=".se.xml"
105// comment="type"
106// left="XPATH#//entities/entity[pos=16][len=13]/type"
107// operator="="
108// right="DateTime.DATE"/>
109// …
110// </testcase>
111// …
112// </testcases>
113bool TestCasesReaderPrivate::parse(QIODevice *device)
114{
116 LDEBUG << "TestCasesReaderPrivate::parse";
117 m_reader.setDevice(device);
118 if (m_reader.readNextStartElement()) {
119 if (m_reader.name() == QLatin1String("testcases"))
120 {
122 }
123 else
124 {
125 m_reader.raiseError(QObject::tr("The file is not a LIMA test cases file."));
126 }
127 }
128 return !m_reader.error();
129}
130
131// <testcases>
132// <testcase id="eng.se.DATE.1" type="bloquant">
133// …
134// </testcase>
135// …
136// </testcases>
138{
140 LTRACE << "TestCasesReaderPrivate::readCodes" << m_reader.name();
141 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("testcases"));
142
143 while (m_reader.readNextStartElement()) {
144 if (m_reader.name() == QLatin1String("testcase"))
145 readTestcase();
146 else
147 m_reader.raiseError(QObject::tr("Expected a code but got a %1.").arg(m_reader.name()));
148 }
149}
150
151// <testcase id="eng.se.DATE.1" type="bloquant">
152// <call-parameters>
153// …
154// </call-parameters>
155// <expl>rule for complete date with cardinal number</expl>
156// <test id="eng.se.DATE.1.1" trace=".se.xml"
157// comment="type"
158// left="XPATH#//entities/entity[pos=16][len=13]/type"
159// operator="="
160// right="DateTime.DATE"/>
161// …
162// </testcase>
164{
166 LTRACE << "TestCasesReaderPrivate::readTestcase" << m_reader.name();
167 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("testcase"));
168
169 LDEBUG << "TestCasesReaderPrivate::readTestcase: new testcase ";
171 currentTestCase.id = m_reader.attributes().value("id").toString().toStdString();
172 auto typech = m_reader.attributes().value("type").toString().toStdString();
173 currentTestCase.type = typech.empty() ? "undefined": typech;
174 LDEBUG << "TestCasesReaderPrivate::readTestcase: id=" << currentTestCase.id
175 << ", type=" << currentTestCase.type;
176
177 bool hasExpl = false;
178 bool hasCallParameters = false;
179 bool hasTest = false;
180 while (m_reader.readNextStartElement())
181 {
182 if (m_reader.name() == QLatin1String("call-parameters"))
183 {
184 hasCallParameters = true;
186 }
187 else if (m_reader.name() == QLatin1String("expl"))
188 {
189 hasExpl = true;
190 readExpl();
191 }
192 else if (m_reader.name() == QLatin1String("test"))
193 {
194 hasTest = true;
195 readTest();
196 }
197 else
198 m_reader.raiseError(QObject::tr("Expected a call-parameters, a expl or a test but got a %1.").arg(m_reader.name()));
199 }
200 if (!hasCallParameters)
201 m_reader.raiseError(QObject::tr("Testcase is missing a call-parameters."));
202 if (!hasTest)
203 m_reader.raiseError(QObject::tr("Testcase should have at least one test. None found."));
204 if (!hasExpl)
205 {
206 LINFO << "Test case" << currentTestCase.id << "," << currentTestCase.type << "at line"
207 << m_reader.lineNumber() << ", column" << m_reader.columnNumber()
208 << "has no explanation. This is wrong.";
209 }
210
212 LDEBUG << "TestCasesReader::endElement: call processTestCase(" << currentTestCase.id
213 << "," << currentTestCase.type << ")";
215 if (e())
216 {
217 std::cout << currentTestCase.id << " (" << currentTestCase.type << ") got error (type: '"<<e()<<"'): "
218 << std::endl << e.what() << std::endl;
220 {
221 m_hasFatalError = true;
222 }
224 {
225 if (e.isConditional())
226 {
227 m_reportByType[currentTestCase.type].conditional++;
228 }
229 else
230 {
232 }
233 }
234 else
235 {
236 std::cerr << "runtime error: " << e.what() << std::endl;
238 throw std::runtime_error(e.what());
239 }
240 }
241 else
242 {
243 std::cout << currentTestCase.id << " (" << currentTestCase.type << ") passed successfully." << std::endl;
245 }
246
247}
248
249// <call-parameters>
250// <param key="text" value="he was born on 10 April 1950."/>
251// <list key="pipelines">
252// <item value="limaserver"/>
253// </list>
254// …
255// </call-parameters>
257{
259 LTRACE << "TestCasesReaderPrivate::readCallParameters" << m_reader.name();
260 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("call-parameters"));
261
262 while (m_reader.readNextStartElement())
263 {
264 if (m_reader.name() == QLatin1String("param"))
265 readParam();
266 else if (m_reader.name() == QLatin1String("list"))
267 readList();
268 else if (m_reader.name() == QLatin1String("map"))
269 readMap();
270 else
271 m_reader.raiseError(QObject::tr("Expected a param, a list or a map but got a %1.").arg(m_reader.name()));
272 }
273// LTRACE << "TestCasesReaderPrivate::readCallParameters no more start element";
274
275}
276
277// <param key="text" value="he was born on 10 April 1950."/>
278// OR <param key="text">he was born on 10 April 1950.</param>
280{
282 LTRACE << "TestCasesReaderPrivate::readParam" << m_reader.name();
283 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("param"));
284
285 std::string key = m_reader.attributes().value("key").toString().toStdString();
286 std::string val;
287 if (m_reader.attributes().hasAttribute("value"))
288 {
289 val = m_reader.attributes().value("value").toString().toStdString();
290 m_reader.skipCurrentElement();
291 }
292 else
293 {
294 val = m_reader.readElementText().toStdString();
295 }
296 LTRACE << "TestCasesReaderPrivate::readParam key:" << key << "value:" << val;
297 currentTestCase.simpleValCallParams.insert(std::pair<std::string, std::string>(key,val) );
298 // TODO: read multiValCallParams "pipelines"
299 // std::string pipch=m_reader.attributes().value("pipelines");
300}
301
302// <list key="pipelines">
303// <item value="limaserver"/>
304// …
305// </list>
307{
309 LTRACE << "TestCasesReaderPrivate::readList" << m_reader.name();
310 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("list"));
311
312 m_listKey = m_reader.attributes().value("key").toString().toStdString();
314 if( pos == currentTestCase.multiValCallParams.end() )
315 {
316 // typedef std::map<std::string,std::list<std::string> > MultiValCallParams;
317 std::pair<std::string,std::list<std::string> > newElem(m_listKey,std::list<std::string>() );
318// std::pair<MultiValCallParams::iterator,bool> ret =
319// currentTestCase.multiValCallParams.insert(
320// std::pair<std::string,std::list<std::string> >(m_listKey,std::list<std::string>) );
321 /*std::pair<MultiValCallParams::iterator,bool> ret =*/ currentTestCase.multiValCallParams.insert(newElem);
322 }
323 m_inList = true;
324
325 while (m_reader.readNextStartElement())
326 {
327 if (m_reader.name() == QLatin1String("item"))
328 readItem();
329 else
330 m_reader.raiseError(QObject::tr("Expected an item but got a %1.").arg(m_reader.name()));
331 }
332
333 m_inList=false;
334
335}
336
337// <map key="pipelines">
338// <item value="limaserver"/>
339// …
340// </map>
342{
344 LTRACE << "TestCasesReaderPrivate::readMap" << m_reader.name();
345 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("map"));
346
347 m_mapKey = m_reader.attributes().value("key").toString().toStdString();
348 MapValCallParams::iterator pos = currentTestCase.mapValCallParams.find(m_mapKey);
349 if( pos == currentTestCase.mapValCallParams.end() ) {
350 // typedef std::map<std::string,std::list<std::string> > MultiValCallParams;
351 std::pair<std::string,std::map<std::string,std::string> > newElem(m_mapKey,std::map<std::string,std::string>() );
352// std::pair<MultiValCallParams::iterator,bool> ret =
353// currentTestCase.multiValCallParams.insert(
354// std::pair<std::string,std::list<std::string> >(m_listKey,std::list<std::string>) );
355 /*std::pair<MapValCallParams::iterator,bool> ret =*/ currentTestCase.mapValCallParams.insert(newElem);
356 }
357 m_inMap=true;
358
359 while (m_reader.readNextStartElement())
360 {
361 if (m_reader.name() == QLatin1String("item"))
362 readItem();
363 else
364 m_reader.raiseError(QObject::tr("Expected ??? but got a %1.").arg(m_reader.name()));
365 }
366
367 m_inMap=false;
368
369}
370
371// <item value="limaserver"/>
373{
375 LTRACE << "TestCasesReaderPrivate::readItem" << m_reader.name();
376 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("item"));
377
378 if(m_inList)
379 {
380 MultiValCallParams::iterator pos = currentTestCase.multiValCallParams.find(m_listKey);
381 if( pos != currentTestCase.multiValCallParams.end() ) {
382 std::string val = m_reader.attributes().value("value").toString().toStdString();
383 (*pos).second.push_back(val);
384 }
385 else {
386 LERROR << "!! TestCasesReader::startElement: no list for param " << m_listKey;
387 }
388 }
389 else if (m_inMap == true)
390 {
391 MapValCallParams::iterator pos = currentTestCase.mapValCallParams.find(m_mapKey);
392 if( pos != currentTestCase.mapValCallParams.end() ) {
393 std::string attr = m_reader.attributes().value("key").toString().toStdString();
394 std::string val = m_reader.attributes().value("value").toString().toStdString();
395 (*pos).second.insert(make_pair(attr,val));
396 }
397 else {
398 LERROR << "!! TestCasesReader::startElement: no list for param " << m_listKey;
399 }
400 }
401 else
402 {
403 m_reader.raiseError(QObject::tr("Should be in a list or a map when reading an item.").arg(m_reader.name()));
404 }
405// currentTestCase.multiValCallParams.insert(std::pair<std::string, std::string>(key,val) );
406 m_reader.skipCurrentElement();
407
408}
409
410// <expl>rule for complete date with cardinal number</expl>
412{
414 LTRACE << "TestCasesReaderPrivate::readExpl" << m_reader.name();
415 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("expl"));
416 currentTestCase.explanation.append(m_reader.text().toString().toStdString());
417
418 m_reader.skipCurrentElement();
419
420}
421
422// <test id="eng.se.DATE.1.1" trace=".se.xml"
423// comment="type"
424// left="XPATH#//entities/entity[pos=16][len=13]/type"
425// operator="="
426// right="DateTime.DATE"/>
428{
430 LTRACE << "TestCasesReaderPrivate::readTest" << m_reader.name();
431 Q_ASSERT(m_reader.isStartElement() && m_reader.name() == QLatin1String("test"));
432
433 // build a testUnit
434 //cout << "add test" << std::endl;
436 tu.id = m_reader.attributes().value("id").toString().toStdString();
437 tu.trace = m_reader.attributes().value("trace").toString().toStdString();
438 tu.comment = m_reader.attributes().value("comment").toString().toStdString();
439 tu.left = m_reader.attributes().value("left").toString().toStdString();
440 tu.op = m_reader.attributes().value("operator").toString().toStdString();
441 tu.right = m_reader.attributes().value("right").toString().toStdString();
442 tu.conditional = (m_reader.attributes().value("conditional").toString()=="yes");
443 currentTestCase.tests.push_back(tu);
444 LDEBUG << "TestCasesReader::startElement: push testUnit"
445 << ", id =" << tu.id
446 << ", trace =" << tu.trace
447 << ", comment =" << tu.comment
448 << ", left =" << tu.left
449 << ", op =" << tu.op
450 << ", right =" << tu.right
451 << ", conditional =" << tu.conditional;
452 m_reader.skipCurrentElement();
453}
454
456{
457 // For compatibility with old testTva format
458 std::string newTextVal(m_reader.readElementText().toStdString());
459
460 SimpleValCallParams::iterator pos = currentTestCase.simpleValCallParams.find("text");
461 if( pos == currentTestCase.simpleValCallParams.end() )
462 {
463// std::pair<SimpleValCallParams::iterator,bool> ret =
464 currentTestCase.simpleValCallParams.insert(std::pair<std::string, std::string>("text", newTextVal));
465 }
466 else
467 {
468 std::string& textVal = (*pos).second;
469 textVal.append(newTextVal);
470 }
471}
472
474{
475 return m_d->m_hasFatalError;
476}
477
478int exitCode(TestCasesReader const & tch)
479{
480 return tch.hasFatalError() ? EXIT_FATAL_ERROR : EXIT_SUCCESS;
481}
482
484{
486 auto errorStr = QObject::tr("%1, Line %2, column %3")
487 .arg(m_d->m_reader.errorString())
488 .arg(m_d->m_reader.lineNumber())
489 .arg(m_d->m_reader.columnNumber());
490 LERROR << errorStr;
491 return errorStr;
492}
493
495{
496 m_d->m_reportByType.clear();
497}
498
499} // TGV
500} // Common
501} // Lima
#define LTRACE
Definition LimaCommon.h:156
#define TGVLOGINIT
Definition LimaCommon.h:216
#define LDEBUG
Definition LimaCommon.h:157
#define LINFO
Definition LimaCommon.h:158
#define LERROR
Definition LimaCommon.h:161
const std::string & what() const
virtual TestCaseError processTestCase(const TestCase &testCase)=0
std::string getName(const QString &localName, const QString &qName)
std::map< std::string, TestCasesReader::TestReport > m_reportByType
TestCasesReaderPrivate(TestCaseProcessor &processor)
TestCasesReader(TestCaseProcessor &processor)
const std::map< std::string, TestReport > & reportByType() const
char const *const FATAL_ERROR_TYPE
int exitCode(TestCasesHandler const &tch)
NAUTITIA.
MultiValCallParams multiValCallParams
Definition TestCase.h:62
SimpleValCallParams simpleValCallParams
Definition TestCase.h:61
MapValCallParams mapValCallParams
Definition TestCase.h:63
std::list< TestUnit > tests
Definition TestCase.h:68