123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279 | // Copyright (C) 2022 The Qt Company Ltd.// Copyright (C) 2017 Borgar Ovsthus// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include <QtTest/private/qtestresult_p.h>#include <QtTest/qtestassert.h>#include <QtTest/private/qtestlog_p.h>#include <QtTest/private/qteamcitylogger_p.h>#include <QtCore/qbytearray.h>#include <private/qlocale_p.h>#include <limits.h>#include <stdarg.h>#include <stdio.h>#include <stdlib.h>#include <string.h> QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;namespace QTest {static const char*tcIncidentType2String(QAbstractTestLogger::IncidentTypes type){switch(type) {caseQAbstractTestLogger::Skip:return"SKIP";caseQAbstractTestLogger::Pass:return"PASS";caseQAbstractTestLogger::XFail:return"XFAIL";caseQAbstractTestLogger::Fail:return"FAIL!";caseQAbstractTestLogger::XPass:return"XPASS";caseQAbstractTestLogger::BlacklistedPass:return"BPASS";caseQAbstractTestLogger::BlacklistedFail:return"BFAIL";caseQAbstractTestLogger::BlacklistedXPass:return"BXPASS";caseQAbstractTestLogger::BlacklistedXFail:return"BXFAIL";}return"??????";}static const char*tcMessageType2String(QAbstractTestLogger::MessageTypes type){switch(type) {caseQAbstractTestLogger::QDebug:return"QDEBUG";caseQAbstractTestLogger::QInfo:return"QINFO";caseQAbstractTestLogger::QWarning:return"QWARN";caseQAbstractTestLogger::QCritical:return"QCRITICAL";caseQAbstractTestLogger::QFatal:return"QFATAL";caseQAbstractTestLogger::Info:return"INFO";caseQAbstractTestLogger::Warn:return"WARNING";}return"??????";}}/*! \internal \class QTeamCityLogger \inmodule QtTest QTeamCityLogger implements logging in the \l{TeamCity} format.*/QTeamCityLogger::QTeamCityLogger(const char*filename):QAbstractTestLogger(filename){}QTeamCityLogger::~QTeamCityLogger() =default;voidQTeamCityLogger::startLogging(){QAbstractTestLogger::startLogging();tcEscapedString(&flowID,QTestResult::currentTestObjectName()); QTestCharBuffer buf;QTest::qt_asprintf(&buf,"##teamcity[testSuiteStarted name='%s' flowId='%s']\n", flowID.constData(), flowID.constData());outputString(buf.constData());}voidQTeamCityLogger::stopLogging(){ QTestCharBuffer buf;QTest::qt_asprintf(&buf,"##teamcity[testSuiteFinished name='%s' flowId='%s']\n", flowID.constData(), flowID.constData());outputString(buf.constData());QAbstractTestLogger::stopLogging();}voidQTeamCityLogger::enterTestFunction(const char*/*function*/){// don't print anything}voidQTeamCityLogger::leaveTestFunction(){// don't print anything}voidQTeamCityLogger::addIncident(IncidentTypes type,const char*description,const char*file,int line){// suppress B?PASS and B?XFAIL in silent modeif((type == Pass || type == XFail || type == BlacklistedPass || type == BlacklistedXFail) &&QTestLog::verboseLevel() <0)return; QTestCharBuffer buf; QTestCharBuffer tmpFuncName;escapedTestFuncName(&tmpFuncName);if(qstrcmp(tmpFuncName.constData(), currTestFuncName.constData()) !=0) {QTest::qt_asprintf(&buf,"##teamcity[testStarted name='%s' flowId='%s']\n", tmpFuncName.constData(), flowID.constData());outputString(buf.constData()); currTestFuncName.clear();QTestPrivate::appendCharBuffer(&currTestFuncName, tmpFuncName);}if(type ==QAbstractTestLogger::XFail) {addPendingMessage(QTest::tcIncidentType2String(type), description, file, line);return;} QTestCharBuffer detailedText;tcEscapedString(&detailedText, description);// Test failedif(type == Fail || type == XPass) { QTestCharBuffer messageText;if(file)QTest::qt_asprintf(&messageText,"Failure! |[Loc: %s(%d)|]", file, line);elseQTest::qt_asprintf(&messageText,"Failure!");QTest::qt_asprintf(&buf,"##teamcity[testFailed name='%s' message='%s' details='%s'"" flowId='%s']\n", tmpFuncName.constData(), messageText.constData(), detailedText.constData(), flowID.constData());outputString(buf.constData());}else if(type == Skip) {if(file) { QTestCharBuffer detail;QTest::qt_asprintf(&detail," |[Loc: %s(%d)|]", file, line);QTestPrivate::appendCharBuffer(&detailedText, detail);}QTest::qt_asprintf(&buf,"##teamcity[testIgnored name='%s' message='%s' flowId='%s']\n", currTestFuncName.constData(), detailedText.constData(), flowID.constData());outputString(buf.constData());}if(!pendingMessages.isEmpty()) {QTest::qt_asprintf(&buf,"##teamcity[testStdOut name='%s' out='%s' flowId='%s']\n", tmpFuncName.constData(), pendingMessages.constData(), flowID.constData());outputString(buf.constData()); pendingMessages.clear();}QTest::qt_asprintf(&buf,"##teamcity[testFinished name='%s' flowId='%s']\n", tmpFuncName.constData(), flowID.constData());outputString(buf.constData());}voidQTeamCityLogger::addBenchmarkResult(const QBenchmarkResult &){// don't print anything}voidQTeamCityLogger::addMessage(MessageTypes type,const QString &message,const char*file,int line){// suppress non-fatal messages in silent modeif(type != QFatal &&QTestLog::verboseLevel() <0)return; QTestCharBuffer escapedMessage;tcEscapedString(&escapedMessage,qUtf8Printable(message));addPendingMessage(QTest::tcMessageType2String(type), escapedMessage.constData(), file, line);}voidQTeamCityLogger::tcEscapedString(QTestCharBuffer *buf,const char*str)const{{size_t size =qstrlen(str) +1;for(const char*p = str; *p; ++p) {if(strchr("\n\r|[]'", *p))++size;}Q_ASSERT(size <=size_t(INT_MAX)); buf->resize(int(size));}bool swallowSpace =true;char*p = buf->data();for(; *str; ++str) {char ch = *str;switch(ch) {case'\n': p++[0] ='|'; ch ='n'; swallowSpace =false;break;case'\r': p++[0] ='|'; ch ='r'; swallowSpace =false;break;case'|':case'[':case']':case'\'': p++[0] ='|'; swallowSpace =false;break;default:if(ascii_isspace(ch)) {if(swallowSpace)continue; swallowSpace =true; ch =' ';}else{ swallowSpace =false;}break;} p++[0] = ch;}Q_ASSERT(p < buf->data() + buf->size());if(swallowSpace && p > buf->data()) {Q_ASSERT(p[-1] ==' ');--p;}Q_ASSERT(p == buf->data() || !ascii_isspace(p[-1]));*p ='\0';}voidQTeamCityLogger::escapedTestFuncName(QTestCharBuffer *buf)const{constexprint TestTag =QTestPrivate::TestFunction |QTestPrivate::TestDataTag;QTestPrivate::generateTestIdentifier(buf, TestTag);}voidQTeamCityLogger::addPendingMessage(const char*type,const char*msg,const char*file,int line){const char*pad = pendingMessages.isEmpty() ?"":"|n"; QTestCharBuffer newMessage;if(file)QTest::qt_asprintf(&newMessage,"%s%s |[Loc: %s(%d)|]: %s", pad, type, file, line, msg);elseQTest::qt_asprintf(&newMessage,"%s%s: %s", pad, type, msg);QTestPrivate::appendCharBuffer(&pendingMessages, newMessage);} QT_END_NAMESPACE
|