testlib: Replace custom QTestCoreList with std::vector

The custom linked list implementation was implemented using
recursion, and as a result didn't handle long lists of test
cases, exhausting the stack on e.g. Windows where the default
stack is only 1MB. This was the case with e.g. the tst_QChar
test that produces 20K test cases.

Replacing with a std::vector should do nicely for our use-case.

No attempt has been made at further reducing the complexity
of QTestElement/QTestCoreElement/QTestElementAttribute.

Pick-to: 6.2
Change-Id: Ie295f7cf937ec6abdc4606b6120818551ad285c7
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Tor Arne Vestbø 2021-09-08 16:34:19 +02:00
parent 4e0082a9ca
commit b8191f41c6
10 changed files with 71 additions and 210 deletions

View File

@ -39,7 +39,6 @@ qt_internal_add_module(Test
qtestblacklist.cpp qtestblacklist_p.h
qtestcase.cpp qtestcase.h
qtestcoreelement_p.h
qtestcorelist_p.h
qtestdata.cpp qtestdata.h
qtestelement.cpp qtestelement_p.h
qtestelementattribute.cpp qtestelementattribute_p.h

View File

@ -105,19 +105,19 @@ void QJUnitTestLogger::startLogging()
property = new QTestElement(QTest::LET_Property);
property->addAttribute(QTest::AI_Name, "QTestVersion");
property->addAttribute(QTest::AI_PropertyValue, QTEST_VERSION_STR);
properties->addLogElement(property);
properties->addChild(property);
property = new QTestElement(QTest::LET_Property);
property->addAttribute(QTest::AI_Name, "QtVersion");
property->addAttribute(QTest::AI_PropertyValue, qVersion());
properties->addLogElement(property);
properties->addChild(property);
property = new QTestElement(QTest::LET_Property);
property->addAttribute(QTest::AI_Name, "QtBuild");
property->addAttribute(QTest::AI_PropertyValue, QLibraryInfo::build());
properties->addLogElement(property);
properties->addChild(property);
currentTestSuite->addLogElement(properties);
currentTestSuite->addChild(properties);
elapsedTestcaseTime.start();
}
@ -141,14 +141,9 @@ void QJUnitTestLogger::stopLogging()
currentTestSuite->addAttribute(QTest::AI_Time,
toSecondsFormat(QTestLog::msecsTotalTime()).constData());
currentTestSuite->addLogElement(listOfTestcases);
// For correct indenting, make sure every testcase knows its parent
QTestElement *testcase = listOfTestcases;
while (testcase) {
testcase->setParent(currentTestSuite);
testcase = testcase->nextElement();
}
for (auto *testCase : listOfTestcases)
currentTestSuite->addChild(testCase);
listOfTestcases.clear();
logFormatter->output(currentTestSuite);
@ -168,7 +163,7 @@ void QJUnitTestLogger::enterTestCase(const char *name)
currentTestCase = new QTestElement(QTest::LET_TestCase);
currentTestCase->addAttribute(QTest::AI_Name, name);
currentTestCase->addAttribute(QTest::AI_Classname, QTestResult::currentTestObjectName());
currentTestCase->addToList(&listOfTestcases);
listOfTestcases.push_back(currentTestCase);
Q_ASSERT(!systemOutputElement && !systemErrorElement);
systemOutputElement = new QTestElement(QTest::LET_SystemOutput);
@ -212,13 +207,13 @@ void QJUnitTestLogger::leaveTestCase()
currentTestCase->addAttribute(QTest::AI_Time,
toSecondsFormat(elapsedTestCaseSeconds()).constData());
if (systemOutputElement->childElements())
currentTestCase->addLogElement(systemOutputElement);
if (!systemOutputElement->childElements().empty())
currentTestCase->addChild(systemOutputElement);
else
delete systemOutputElement;
if (systemErrorElement->childElements())
currentTestCase->addLogElement(systemErrorElement);
if (!systemErrorElement->childElements().empty())
currentTestCase->addChild(systemErrorElement);
else
delete systemErrorElement;
@ -252,8 +247,7 @@ void QJUnitTestLogger::addFailure(QTest::LogElementType elementType,
if (elementType == QTest::LET_Failure) {
// Make sure we're not adding failure when we already have error,
// or adding additional failures when we already have a failure.
for (auto *childElement = currentTestCase->childElements();
childElement; childElement = childElement->nextElement()) {
for (auto *childElement : currentTestCase->childElements()) {
if (childElement->elementType() == QTest::LET_Error ||
childElement->elementType() == QTest::LET_Failure)
return;
@ -272,10 +266,10 @@ void QJUnitTestLogger::addFailure(QTest::LogElementType elementType,
if (!details.isEmpty()) {
auto textNode = new QTestElement(QTest::LET_Text);
textNode->addAttribute(QTest::AI_Value, details.toUtf8().constData());
failureElement->addLogElement(textNode);
failureElement->addChild(textNode);
}
currentTestCase->addLogElement(failureElement);
currentTestCase->addChild(failureElement);
switch (elementType) {
case QTest::LET_Failure: ++failureCounter; break;
@ -292,7 +286,7 @@ void QJUnitTestLogger::addMessage(MessageTypes type, const QString &message, con
if (type == QAbstractTestLogger::Skip) {
auto skippedElement = new QTestElement(QTest::LET_Skipped);
skippedElement->addAttribute(QTest::AI_Message, message.toUtf8().constData());
currentTestCase->addLogElement(skippedElement);
currentTestCase->addChild(skippedElement);
return;
} else if (type == QAbstractTestLogger::QFatal) {
addFailure(QTest::LET_Error, "qfatal", message);
@ -316,7 +310,7 @@ void QJUnitTestLogger::addMessage(MessageTypes type, const QString &message, con
auto textNode = new QTestElement(QTest::LET_Text);
textNode->addAttribute(QTest::AI_Value, message.toUtf8().constData());
systemLogElement->addLogElement(textNode);
systemLogElement->addChild(textNode);
}
QT_END_NAMESPACE

View File

@ -51,9 +51,13 @@
// We mean it.
//
#include <QtTest/qttestglobal.h>
#include <QtTest/private/qabstracttestlogger_p.h>
#include <QtTest/private/qtestelementattribute_p.h>
#include <vector>
QT_BEGIN_NAMESPACE
class QTestJUnitStreamer;
@ -88,7 +92,7 @@ class QJUnitTestLogger : public QAbstractTestLogger
const char *failureType, const QString &failureDescription);
QTestElement *currentTestSuite = nullptr;
QTestElement *listOfTestcases = nullptr;
std::vector<QTestElement*> listOfTestcases;
QTestElement *currentTestCase = nullptr;
QTestElement *systemOutputElement = nullptr;
QTestElement *systemErrorElement = nullptr;

View File

@ -51,21 +51,23 @@
// We mean it.
//
#include <QtTest/private/qtestcorelist_p.h>
#include <QtTest/qttestglobal.h>
#include <QtTest/private/qtestelementattribute_p.h>
#include <vector>
QT_BEGIN_NAMESPACE
template <class ElementType>
class QTestCoreElement: public QTestCoreList<ElementType>
class QTestCoreElement
{
public:
QTestCoreElement( int type = -1 );
virtual ~QTestCoreElement();
void addAttribute(const QTest::AttributeIndex index, const char *value);
QTestElementAttribute *attributes() const;
const std::vector<QTestElementAttribute*> &attributes() const;
const char *attributeValue(QTest::AttributeIndex index) const;
const char *attributeName(QTest::AttributeIndex index) const;
const QTestElementAttribute *attribute(QTest::AttributeIndex index) const;
@ -74,7 +76,7 @@ class QTestCoreElement: public QTestCoreList<ElementType>
QTest::LogElementType elementType() const;
private:
QTestElementAttribute *listOfAttributes = nullptr;
std::vector<QTestElementAttribute*> listOfAttributes;
QTest::LogElementType type;
};
@ -87,7 +89,8 @@ QTestCoreElement<ElementType>::QTestCoreElement(int t)
template<class ElementType>
QTestCoreElement<ElementType>::~QTestCoreElement()
{
delete listOfAttributes;
for (auto *attribute : listOfAttributes)
delete attribute;
}
template <class ElementType>
@ -98,11 +101,11 @@ void QTestCoreElement<ElementType>::addAttribute(const QTest::AttributeIndex att
QTestElementAttribute *testAttribute = new QTestElementAttribute;
testAttribute->setPair(attributeIndex, value);
testAttribute->addToList(&listOfAttributes);
listOfAttributes.push_back(testAttribute);
}
template <class ElementType>
QTestElementAttribute *QTestCoreElement<ElementType>::attributes() const
const std::vector<QTestElementAttribute*> &QTestCoreElement<ElementType>::attributes() const
{
return listOfAttributes;
}
@ -159,12 +162,9 @@ QTest::LogElementType QTestCoreElement<ElementType>::elementType() const
template <class ElementType>
const QTestElementAttribute *QTestCoreElement<ElementType>::attribute(QTest::AttributeIndex index) const
{
QTestElementAttribute *iterator = listOfAttributes;
while (iterator) {
if (iterator->index() == index)
return iterator;
iterator = iterator->nextElement();
for (auto *attribute : listOfAttributes) {
if (attribute->index() == index)
return attribute;
}
return nullptr;

View File

@ -1,123 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtTest module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QTESTCORELIST_P_H
#define QTESTCORELIST_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
template <class T>
class QTestCoreList
{
public:
QTestCoreList();
virtual ~QTestCoreList();
void addToList(T **list);
T *nextElement();
T *previousElement();
private:
T *next;
T *prev;
};
template <class T>
QTestCoreList<T>::QTestCoreList()
: next(nullptr)
, prev(nullptr)
{
}
template <class T>
QTestCoreList<T>::~QTestCoreList()
{
if (prev) {
prev->next = nullptr;
}
delete prev;
if (next) {
next->prev = nullptr;
}
delete next;
}
template <class T>
void QTestCoreList<T>::addToList(T **list)
{
if (next)
next->addToList(list);
else {
next = *list;
if (next)
next->prev = static_cast<T*>(this);
}
*list = static_cast<T*>(this);
}
template <class T>
T *QTestCoreList<T>::nextElement()
{
return next;
}
template <class T>
T *QTestCoreList<T>::previousElement()
{
return prev;
}
QT_END_NAMESPACE
#endif

View File

@ -48,16 +48,17 @@ QTestElement::QTestElement(int type)
QTestElement::~QTestElement()
{
delete listOfChildren;
for (auto *child : listOfChildren)
delete child;
}
bool QTestElement::addLogElement(QTestElement *element)
bool QTestElement::addChild(QTestElement *element)
{
if (!element)
return false;
if (element->elementType() != QTest::LET_Undefined) {
element->addToList(&listOfChildren);
listOfChildren.push_back(element);
element->setParent(this);
return true;
}
@ -65,7 +66,7 @@ bool QTestElement::addLogElement(QTestElement *element)
return false;
}
QTestElement *QTestElement::childElements() const
const std::vector<QTestElement*> &QTestElement::childElements() const
{
return listOfChildren;
}

View File

@ -51,26 +51,27 @@
// We mean it.
//
#include <QtTest/qttestglobal.h>
#include <QtTest/private/qtestcoreelement_p.h>
QT_BEGIN_NAMESPACE
class QTestElement: public QTestCoreElement<QTestElement>
class QTestElement : public QTestCoreElement<QTestElement>
{
public:
QTestElement(int type = -1);
~QTestElement();
bool addLogElement(QTestElement *element);
QTestElement *childElements() const;
bool addChild(QTestElement *element);
const std::vector<QTestElement*> &childElements() const;
const QTestElement *parentElement() const;
void setParent(const QTestElement *p);
private:
QTestElement *listOfChildren = nullptr;
const QTestElement * parent = nullptr;
std::vector<QTestElement*> listOfChildren;
const QTestElement *parent = nullptr;
};

View File

@ -51,7 +51,7 @@
// We mean it.
//
#include <QtTest/private/qtestcorelist_p.h>
#include <QtTest/qttestglobal.h>
QT_BEGIN_NAMESPACE
@ -92,7 +92,7 @@ namespace QTest {
};
}
class QTestElementAttribute: public QTestCoreList<QTestElementAttribute>
class QTestElementAttribute
{
public:
QTestElementAttribute();

View File

@ -95,7 +95,7 @@ void QTestJUnitStreamer::formatEnd(const QTestElement *element, QTestCharBuffer
if (!element || !formatted )
return;
if (!element->childElements()) {
if (element->childElements().empty()) {
formatted->data()[0] = '\0';
return;
}
@ -135,7 +135,7 @@ void QTestJUnitStreamer::formatAfterAttributes(const QTestElement *element, QTes
return;
}
if (!element->childElements())
if (element->childElements().empty())
QTest::qt_asprintf(formatted, "/>\n");
else
QTest::qt_asprintf(formatted, ">\n");
@ -145,54 +145,39 @@ void QTestJUnitStreamer::output(QTestElement *element) const
{
QTEST_ASSERT(element);
outputString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
outputElements(element);
}
if (!element->parentElement())
outputString("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
void QTestJUnitStreamer::outputElements(QTestElement *element, bool) const
{
QTestCharBuffer buf;
bool hasChildren;
// Elements are in reverse order of occurrence, so
// start from the end and work our way backwards.
while (element && element->nextElement())
element = element->nextElement();
formatStart(element, &buf);
outputString(buf.data());
while (element) {
hasChildren = element->childElements();
outputElementAttributes(element, element->attributes());
formatStart(element, &buf);
outputString(buf.data());
formatAfterAttributes(element, &buf);
outputString(buf.data());
outputElementAttributes(element, element->attributes());
if (!element->childElements().empty())
outputElements(element->childElements());
formatAfterAttributes(element, &buf);
outputString(buf.data());
if (hasChildren)
outputElements(element->childElements(), true);
formatEnd(element, &buf);
outputString(buf.data());
element = element->previousElement();
}
formatEnd(element, &buf);
outputString(buf.data());
}
void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, QTestElementAttribute *attribute) const
void QTestJUnitStreamer::outputElements(const std::vector<QTestElement*> &elements) const
{
for (auto *element : elements)
output(element);
}
void QTestJUnitStreamer::outputElementAttributes(const QTestElement* element, const std::vector<QTestElementAttribute*> &attributes) const
{
QTestCharBuffer buf;
// Attributes are in reverse order of occurrence, so
// start from the end and work our way backwards.
while (attribute && attribute->nextElement())
attribute = attribute->nextElement();
while (attribute) {
for (auto *attribute : attributes) {
formatAttributes(element, attribute, &buf);
outputString(buf.data());
attribute = attribute->previousElement();
}
}

View File

@ -72,8 +72,8 @@ class QTestJUnitStreamer
void formatAfterAttributes(const QTestElement *element, QTestCharBuffer *formatted) const;
void formatAttributes(const QTestElement *element, const QTestElementAttribute *attribute, QTestCharBuffer *formatted) const;
void output(QTestElement *element) const;
void outputElements(QTestElement *element, bool isChildElement = false) const;
void outputElementAttributes(const QTestElement *element, QTestElementAttribute *attribute) const;
void outputElements(const std::vector<QTestElement*> &) const;
void outputElementAttributes(const QTestElement *element, const std::vector<QTestElementAttribute*> &attributes) const;
void outputString(const char *msg) const;