AutoTest: Handle re-implemented test functions appropriate

If a derived test case implements the same private slot as one of
its base classes one of these information got lost as both were
using the same key to store the information at.
Additionally take care of what is possible and sensible when using
QtTest and deriving test cases from others.

Task-number: QTCREATORBUG-17522
Change-Id: I0d2a47c820d5eb002f8bdd851a07a4774e9838f0
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Stenger
2017-01-05 10:34:03 +01:00
parent e0c4f87e6e
commit 92db5afba4
2 changed files with 66 additions and 17 deletions

View File

@@ -30,6 +30,7 @@
#include "../autotest_utils.h" #include "../autotest_utils.h"
#include <cplusplus/TypeOfExpression.h> #include <cplusplus/TypeOfExpression.h>
#include <utils/algorithm.h>
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
@@ -175,24 +176,67 @@ static QMap<QString, QtTestCodeLocationList> checkForDataTags(const QString &fil
return visitor.dataTags(); return visitor.dataTags();
} }
static QMap<QString, QtTestCodeLocationAndType> baseClassTestFunctions(const QSet<QString> &bases, /*!
const CPlusPlus::Document::Ptr &doc, const CPlusPlus::Snapshot &snapshot) * \brief Checks whether \a testFunctions (keys are full qualified names) contains already the
* given \a function (unqualified name).
*
* \return true if this function is already contained, false otherwise
*/
static bool containsFunction(const QMap<QString, QtTestCodeLocationAndType> &testFunctions,
const QString &function)
{ {
QMap<QString, QtTestCodeLocationAndType> testFunctions; const QString search = "::" + function;
for (const QString &baseClassName : bases) { return Utils::anyOf(testFunctions.keys(), [&search] (const QString &key) {
TestVisitor baseVisitor(baseClassName, snapshot); return key.endsWith(search);
});
}
static void mergeTestFunctions(QMap<QString, QtTestCodeLocationAndType> &testFunctions,
const QMap<QString, QtTestCodeLocationAndType> &inheritedFunctions)
{
static const QString dataSuffix("_data");
// take over only inherited test functions that have not been re-implemented
QMap<QString, QtTestCodeLocationAndType>::ConstIterator it = inheritedFunctions.begin();
QMap<QString, QtTestCodeLocationAndType>::ConstIterator end = inheritedFunctions.end();
for ( ; it != end; ++it) {
const QString functionName = it.key();
const QString &shortName = functionName.mid(functionName.lastIndexOf(':') + 1);
if (shortName.endsWith(dataSuffix)) {
const QString &correspondingFunc = functionName.left(functionName.size()
- dataSuffix.size());
// inherited test data functions only if we're inheriting the corresponding test
// function as well (and the inherited test function is not omitted)
if (inheritedFunctions.contains(correspondingFunc)) {
if (!testFunctions.contains(correspondingFunc))
continue;
testFunctions.insert(functionName, it.value());
}
} else if (!containsFunction(testFunctions, shortName)) {
// normal test functions only if not re-implemented
testFunctions.insert(functionName, it.value());
}
}
}
static void fetchAndMergeBaseTestFunctions(const QSet<QString> &baseClasses,
QMap<QString, QtTestCodeLocationAndType> &testFunctions,
const CPlusPlus::Document::Ptr &doc,
const CPlusPlus::Snapshot &snapshot)
{
QList<QString> bases = baseClasses.toList();
while (!bases.empty()) {
const QString base = bases.takeFirst();
TestVisitor baseVisitor(base, snapshot);
baseVisitor.setInheritedMode(true); baseVisitor.setInheritedMode(true);
CPlusPlus::Document::Ptr declaringDoc = declaringDocument(doc, snapshot, baseClassName); CPlusPlus::Document::Ptr declaringDoc = declaringDocument(doc, snapshot, base);
if (declaringDoc.isNull()) if (declaringDoc.isNull())
continue; continue;
baseVisitor.accept(declaringDoc->globalNamespace()); baseVisitor.accept(declaringDoc->globalNamespace());
if (baseVisitor.resultValid()) if (!baseVisitor.resultValid())
testFunctions.unite(baseVisitor.privateSlots()); continue;
const QSet<QString> currentBaseBases = baseVisitor.baseClasses(); bases.append(baseVisitor.baseClasses().toList());
// recursively check base classes mergeTestFunctions(testFunctions, baseVisitor.privateSlots());
testFunctions.unite(baseClassTestFunctions(currentBaseBases, doc, snapshot));
} }
return testFunctions;
} }
static QtTestCodeLocationList tagLocationsFor(const QtTestParseResult *func, static QtTestCodeLocationList tagLocationsFor(const QtTestParseResult *func,
@@ -239,8 +283,11 @@ static bool handleQtTest(QFutureInterface<TestParseResultPtr> futureInterface,
return false; return false;
QMap<QString, QtTestCodeLocationAndType> testFunctions = visitor.privateSlots(); QMap<QString, QtTestCodeLocationAndType> testFunctions = visitor.privateSlots();
// gather appropriate information of base classes as well // gather appropriate information of base classes as well and merge into already found
testFunctions.unite(baseClassTestFunctions(visitor.baseClasses(), declaringDoc, snapshot)); // functions - but only as far as QtTest can handle this appropriate
fetchAndMergeBaseTestFunctions(
visitor.baseClasses(), testFunctions, declaringDoc, snapshot);
const QSet<QString> &files = filesWithDataFunctionDefinitions(testFunctions); const QSet<QString> &files = filesWithDataFunctionDefinitions(testFunctions);
// TODO: change to QHash<> // TODO: change to QHash<>
@@ -263,10 +310,12 @@ static bool handleQtTest(QFutureInterface<TestParseResultPtr> futureInterface,
const QMap<QString, QtTestCodeLocationAndType>::ConstIterator end = testFunctions.end(); const QMap<QString, QtTestCodeLocationAndType>::ConstIterator end = testFunctions.end();
for ( ; it != end; ++it) { for ( ; it != end; ++it) {
const QtTestCodeLocationAndType &location = it.value(); const QtTestCodeLocationAndType &location = it.value();
QString functionName = it.key();
functionName = functionName.mid(functionName.lastIndexOf(':') + 1);
QtTestParseResult *func = new QtTestParseResult(id); QtTestParseResult *func = new QtTestParseResult(id);
func->itemType = location.m_type; func->itemType = location.m_type;
func->name = testCaseName + "::" + it.key(); func->name = testCaseName + "::" + functionName;
func->displayName = it.key(); func->displayName = functionName;
func->fileName = location.m_name; func->fileName = location.m_name;
func->line = location.m_line; func->line = location.m_line;
func->column = location.m_column; func->column = location.m_column;

View File

@@ -84,7 +84,7 @@ bool TestVisitor::visit(CPlusPlus::Class *symbol)
else else
locationAndType.m_type = TestTreeItem::TestFunctionOrSet; locationAndType.m_type = TestTreeItem::TestFunctionOrSet;
locationAndType.m_inherited = m_inherited; locationAndType.m_inherited = m_inherited;
m_privSlots.insert(name, locationAndType); m_privSlots.insert(className + "::" + name, locationAndType);
} }
} }
for (unsigned counter = 0, end = symbol->baseClassCount(); counter < end; ++counter) { for (unsigned counter = 0, end = symbol->baseClassCount(); counter < end; ++counter) {