summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Welbourne <edward.welbourne@qt.io>2022-08-16 11:54:26 +0200
committerEdward Welbourne <edward.welbourne@qt.io>2022-09-13 14:50:59 +0200
commitceba44a3bf916b5e01eca5852b12e3cfa9102df4 (patch)
treed052cb9a0f332d1fbb090ae7164c6f850ba6d1d7
parentd19d918c60c9b99a081d00a06de9194ac963fa66 (diff)
Convert teamcity logger to use QTestCharBuffer
All our other loggers use it and teamcity's use of QString implied lots of conversions from UTF-8 and back, or in some cases to native encoding, that ended up failing to correctly propagate Unicode tokens that the other test loggers do deal with correctly. This does not change the generated output on Linux. Change-Id: I036d8043d7b3b19e7a0e1f296a23628f98223563 Reviewed-by: Jason McDonald <macadder1@gmail.com>
-rw-r--r--src/testlib/qteamcitylogger.cpp177
-rw-r--r--src/testlib/qteamcitylogger_p.h12
2 files changed, 114 insertions, 75 deletions
diff --git a/src/testlib/qteamcitylogger.cpp b/src/testlib/qteamcitylogger.cpp
index 20d15e8d3f..7fe58f4792 100644
--- a/src/testlib/qteamcitylogger.cpp
+++ b/src/testlib/qteamcitylogger.cpp
@@ -7,6 +7,8 @@
#include <QtTest/private/qtestlog_p.h>
#include <QtTest/private/qteamcitylogger_p.h>
#include <QtCore/qbytearray.h>
+
+#include <cctype>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -83,16 +85,20 @@ void QTeamCityLogger::startLogging()
{
QAbstractTestLogger::startLogging();
- flowID = tcEscapedString(QString::fromUtf8(QTestResult::currentTestObjectName()));
+ tcEscapedString(&flowID, QTestResult::currentTestObjectName());
- QString str = "##teamcity[testSuiteStarted name='%1' flowId='%1']\n"_L1.arg(flowID);
- outputString(qPrintable(str));
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf, "##teamcity[testSuiteStarted name='%s' flowId='%s']\n",
+ flowID.constData(), flowID.constData());
+ outputString(buf.constData());
}
void QTeamCityLogger::stopLogging()
{
- QString str = "##teamcity[testSuiteFinished name='%1' flowId='%1']\n"_L1.arg(flowID);
- outputString(qPrintable(str));
+ QTestCharBuffer buf;
+ QTest::qt_asprintf(&buf, "##teamcity[testSuiteFinished name='%s' flowId='%s']\n",
+ flowID.constData(), flowID.constData());
+ outputString(buf.constData());
QAbstractTestLogger::stopLogging();
}
@@ -114,56 +120,66 @@ void QTeamCityLogger::addIncident(IncidentTypes type, const char *description,
if ((type == Pass || type == XFail || type == BlacklistedPass || type == BlacklistedXFail) && QTestLog::verboseLevel() < 0)
return;
- QString buf;
+ QTestCharBuffer buf;
+ QTestCharBuffer tmpFuncName;
+ escapedTestFuncName(&tmpFuncName);
- QString tmpFuncName = escapedTestFuncName();
+ 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());
- if (tmpFuncName != currTestFuncName) {
- buf = "##teamcity[testStarted name='%1' flowId='%2']\n"_L1.arg(tmpFuncName, flowID);
- outputString(qPrintable(buf));
+ currTestFuncName.clear();
+ QTestPrivate::appendCharBuffer(&currTestFuncName, tmpFuncName);
}
- currTestFuncName = tmpFuncName;
-
if (type == QAbstractTestLogger::XFail) {
- addPendingMessage(QTest::incidentType2String(type), QString::fromUtf8(description), file, line);
+ addPendingMessage(QTest::incidentType2String(type), description, file, line);
return;
}
- QString detailedText = tcEscapedString(QString::fromUtf8(description));
+ QTestCharBuffer detailedText;
+ tcEscapedString(&detailedText, description);
// Test failed
if (type == Fail || type == XPass) {
- QString messageText(u"Failure!"_s);
-
+ QTestCharBuffer messageText;
if (file)
- messageText += " |[Loc: %1(%2)|]"_L1.arg(QString::fromUtf8(file)).arg(line);
+ QTest::qt_asprintf(&messageText, "Failure! |[Loc: %s(%d)|]", file, line);
+ else
+ QTest::qt_asprintf(&messageText, "Failure!");
- buf = "##teamcity[testFailed name='%1' message='%2' details='%3' flowId='%4']\n"_L1
- .arg(tmpFuncName, messageText, detailedText, flowID);
+ QTest::qt_asprintf(&buf, "##teamcity[testFailed name='%s' message='%s' details='%s'"
+ " flowId='%s']\n", tmpFuncName.constData(), messageText.constData(),
+ detailedText.constData(), flowID.constData());
- outputString(qPrintable(buf));
+ outputString(buf.constData());
} else if (type == Skip) {
- if (file)
- detailedText.append(" |[Loc: %1(%2)|]"_L1.arg(QString::fromUtf8(file)).arg(line));
+ if (file) {
+ QTestCharBuffer detail;
+ QTest::qt_asprintf(&detail, " |[Loc: %s(%d)|]", file, line);
+ QTestPrivate::appendCharBuffer(&detailedText, detail);
+ }
- buf = "##teamcity[testIgnored name='%1' message='%2' flowId='%3']\n"_L1
- .arg(escapedTestFuncName(), detailedText, flowID);
+ QTest::qt_asprintf(&buf, "##teamcity[testIgnored name='%s' message='%s' flowId='%s']\n",
+ currTestFuncName.constData(), detailedText.constData(),
+ flowID.constData());
- outputString(qPrintable(buf));
+ outputString(buf.constData());
}
if (!pendingMessages.isEmpty()) {
- buf = "##teamcity[testStdOut name='%1' out='%2' flowId='%3']\n"_L1
- .arg(tmpFuncName, pendingMessages, flowID);
-
- outputString(qPrintable(buf));
+ QTest::qt_asprintf(&buf, "##teamcity[testStdOut name='%s' out='%s' flowId='%s']\n",
+ tmpFuncName.constData(), pendingMessages.constData(),
+ flowID.constData());
+ outputString(buf.constData());
pendingMessages.clear();
}
- buf = "##teamcity[testFinished name='%1' flowId='%2']\n"_L1.arg(tmpFuncName, flowID);
- outputString(qPrintable(buf));
+ QTest::qt_asprintf(&buf, "##teamcity[testFinished name='%s' flowId='%s']\n",
+ tmpFuncName.constData(), flowID.constData());
+ outputString(buf.constData());
}
void QTeamCityLogger::addBenchmarkResult(const QBenchmarkResult &)
@@ -178,68 +194,91 @@ void QTeamCityLogger::addMessage(MessageTypes type, const QString &message,
if (type != QFatal && QTestLog::verboseLevel() < 0)
return;
- QString escapedMessage = tcEscapedString(message);
- addPendingMessage(QTest::messageType2String(type), escapedMessage, file, line);
+ QTestCharBuffer escapedMessage;
+ tcEscapedString(&escapedMessage, qUtf8Printable(message));
+ addPendingMessage(QTest::messageType2String(type), escapedMessage.constData(), file, line);
}
-QString QTeamCityLogger::tcEscapedString(const QString &str) const
+void QTeamCityLogger::tcEscapedString(QTestCharBuffer *buf, const char *str) const
{
- QString formattedString;
+ {
+ 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));
+ }
- for (QChar ch : str) {
- switch (ch.toLatin1()) {
+ bool swallowSpace = true;
+ char *p = buf->data();
+ for (; *str; ++str) {
+ char ch = *str;
+ switch (ch) {
case '\n':
- formattedString.append("|n"_L1);
+ p++[0] = '|';
+ ch = 'n';
+ swallowSpace = false;
break;
case '\r':
- formattedString.append("|r"_L1);
+ p++[0] = '|';
+ ch = 'r';
+ swallowSpace = false;
break;
case '|':
- formattedString.append("||"_L1);
- break;
case '[':
- formattedString.append("|["_L1);
- break;
case ']':
- formattedString.append("|]"_L1);
- break;
case '\'':
- formattedString.append("|'"_L1);
+ p++[0] = '|';
+ swallowSpace = false;
break;
default:
- formattedString.append(ch);
+ if (std::isspace(ch)) {
+ if (swallowSpace)
+ continue;
+ swallowSpace = true;
+ ch = ' ';
+ } else {
+ swallowSpace = false;
+ }
+ break;
}
+ p++[0] = ch;
}
-
- return std::move(formattedString).simplified();
+ Q_ASSERT(p < buf->data() + buf->size());
+ if (swallowSpace && p > buf->data()) {
+ Q_ASSERT(p[-1] == ' ');
+ --p;
+ }
+ Q_ASSERT(p == buf->data() || !std::isspace(p[-1]));
+ *p = '\0';
}
-QString QTeamCityLogger::escapedTestFuncName() const
+void QTeamCityLogger::escapedTestFuncName(QTestCharBuffer *buf) const
{
- const char *fn = QTestResult::currentTestFunction() ? QTestResult::currentTestFunction()
- : "UnknownTestFunc";
- const char *tag = QTestResult::currentDataTag() ? QTestResult::currentDataTag() : "";
-
- return tcEscapedString(QString::asprintf("%s(%s)", fn, tag));
+ QTestCharBuffer fn, tag;
+ const char *raw = QTestResult::currentTestFunction();
+ tcEscapedString(&fn, raw ? raw : "UnknownTestFunc");
+ raw = QTestResult::currentDataTag();
+ if (raw)
+ tcEscapedString(&tag, raw);
+
+ QTest::qt_asprintf(buf, "%s(%s)", fn.constData(), tag.constData());
}
-void QTeamCityLogger::addPendingMessage(const char *type, const QString &msg, const char *file, int line)
+void QTeamCityLogger::addPendingMessage(const char *type, const char *msg,
+ const char *file, int line)
{
- QString pendMessage;
-
- if (!pendingMessages.isEmpty())
- pendMessage += "|n"_L1;
+ const char *pad = pendingMessages.isEmpty() ? "" : "|n";
- if (file) {
- pendMessage += "%1 |[Loc: %2(%3)|]: %4"_L1
- .arg(QString::fromUtf8(type), QString::fromUtf8(file),
- QString::number(line), msg);
-
- } else {
- pendMessage += "%1: %2"_L1.arg(QString::fromUtf8(type), msg);
- }
+ QTestCharBuffer newMessage;
+ if (file)
+ QTest::qt_asprintf(&newMessage, "%s%s |[Loc: %s(%d)|]: %s", pad, type, file, line, msg);
+ else
+ QTest::qt_asprintf(&newMessage, "%s%s: %s", pad, type, msg);
- pendingMessages.append(pendMessage);
+ QTestPrivate::appendCharBuffer(&pendingMessages, newMessage);
}
QT_END_NAMESPACE
diff --git a/src/testlib/qteamcitylogger_p.h b/src/testlib/qteamcitylogger_p.h
index 41df8bb7d1..406aa690e5 100644
--- a/src/testlib/qteamcitylogger_p.h
+++ b/src/testlib/qteamcitylogger_p.h
@@ -41,13 +41,13 @@ public:
const char *file = nullptr, int line = 0) override;
private:
- QString currTestFuncName;
- QString pendingMessages;
- QString flowID;
+ QTestCharBuffer currTestFuncName;
+ QTestCharBuffer pendingMessages;
+ QTestCharBuffer flowID;
- QString tcEscapedString(const QString &str) const;
- QString escapedTestFuncName() const;
- void addPendingMessage(const char *type, const QString &msg, const char *file, int line);
+ void tcEscapedString(QTestCharBuffer *buf, const char *str) const;
+ void escapedTestFuncName(QTestCharBuffer *buf) const;
+ void addPendingMessage(const char *type, const char *msg, const char *file, int line);
};
QT_END_NAMESPACE