Revision control

Copy as Markdown

// Copyright (C) 2020 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/QtCore>
#include <QtTest/QtTest>
#include "qbic.h"
#include "global.h"
#include <stdlib.h>
typedef QPair<int, int> Version;
static QString compiler()
{
#if defined(Q_CC_CLANG) && defined(__INTEL_CLANG_COMPILER)
return QLatin1String("icx");
#elif defined(Q_CC_CLANG)
return QLatin1String("clang++");
#elif defined(Q_CC_GNU)
return QLatin1String("g++");
#elif defined(Q_CC_MSVC)
return QLatin1String("cl");
#else
return QLatin1String("unknown");
#endif
}
bool compilerVersion(const QString &compiler, QString *output, Version *version, QString *errorMessage)
{
*version = Version(-1, -1);
output->clear();
#ifndef QT_NO_PROCESS
// Run compiler to obtain version information.
QProcess proc;
proc.start(compiler, QStringList(QLatin1String("--version")), QIODevice::ReadOnly);
if (!proc.waitForStarted()) {
*errorMessage = QLatin1String("Cannot launch: ") + compiler + QLatin1String(": ") + proc.errorString();
return false;
}
if (!proc.waitForFinished()) {
proc.kill();
*errorMessage = compiler + QLatin1String(" did not terminate.");
return false;
}
*output = QString::fromLocal8Bit(proc.readAllStandardOutput());
// Extract version from last token of first line ("g++ (Ubuntu 4.8.2-19ubuntu1) 4.8.2 [build (prerelease)]\n...")
QRegularExpression versionPattern(QLatin1String("^[^(]+\\([^)]+\\) (\\d+)\\.(\\d+)\\.\\d+.*$"),
QRegularExpression::MultilineOption);
Q_ASSERT(versionPattern.isValid());
QRegularExpressionMatch match = versionPattern.match(*output);
if (!match.hasMatch()) {
*errorMessage = compiler + QLatin1String(" produced unexpected output: \"")
+ *output + QLatin1String("\", matching up to ") + QString::number(match.capturedLength());
return false;
}
version->first = match.captured(1).toInt();
version->second = match.captured(2).toInt();
return true;
#else // !QT_NO_PROCESS
Q_UNUSED(compiler);
*errorMessage = QLatin1String("Platform does not support QProcess");
return false;
#endif
}
static QStringList compilerArguments(const QString &compiler, const QStringList &incPaths)
{
Q_UNUSED(compiler);
QStringList result;
result << "-c"
<< incPaths
#ifdef Q_OS_MAC
<< "-arch" << "i386" // Always use 32-bit data on Mac.
#endif
#ifndef Q_OS_WIN
<< "-I/usr/X11R6/include/"
#endif
<< "-DQT_NO_STL"
<< "-xc++"
#if !defined(Q_OS_AIX) && !defined(Q_OS_WIN)
<< "-o" << "/dev/null"
#endif
#if __GNUC__ >= 8
<< "-fdump-lang-class"
#else
<< "-fdump-class-hierarchy"
#endif
<< "-std=c++17"
<< "-fPIC"; // As of 5.4, "reduce relocations" requires "-fPIC"
return result;
}
static const char noneSuchFileSuffix[] = "nonsuch";
static QString fileSuffix(const QString &compiler, const Version &compilerVersion)
{
Q_UNUSED(compiler);
Q_UNUSED(compilerVersion);
QString result;
#if !defined(Q_CC_GNU)
return result;
#endif
#if defined Q_OS_LINUX
# if defined(__powerpc__) && !defined(__powerpc64__)
result = QLatin1String("linux-gcc-ppc32");
# elif defined(__amd64__)
result = QLatin1String("linux-gcc-amd64");
# elif defined(__i386__)
result = QLatin1String("linux-gcc-ia32");
# elif defined(__arm__)
result = QLatin1String("linux-gcc-arm");
# endif
#elif defined Q_OS_MACOS && defined(__powerpc__)
result = QLatin1String("macx-gcc-ppc32");
#elif defined Q_OS_MACOS && defined(__i386__)
result = QLatin1String("macx-gcc-ia32");
#elif defined Q_OS_MACOS && defined(__amd64__)
result = QLatin1String("macx-gcc-amd64");
#elif defined Q_OS_WIN && defined Q_CC_GNU
result = QLatin1String("win32-gcc-ia32");
#else
result = QLatin1String(noneSuchFileSuffix);
#endif
return result;
}
class tst_Bic: public QObject
{
Q_OBJECT
public:
tst_Bic(const char *appFilePath);
#ifndef QT_NO_PROCESS
QBic::Info getCurrentInfo(const QString &libName);
#endif
QHash<QString, QBic::Info> cachedCurrentInfo;
private slots:
void initTestCase();
void cleanupTestCase();
void sizesAndVTables_data();
void sizesAndVTables();
private:
QBic bic;
QString qtModuleDir;
QString qtDir;
QString qtSourceDir;
QStringList qtModules;
QHash<QString, QString> modules;
QStringList incPaths;
QString m_compiler;
Version m_compilerVersion;
QString m_fileSuffix;
QStringList m_compilerArguments;
const char *m_appFilePath;
};
typedef QPair<QString, QString> QStringPair;
tst_Bic::tst_Bic(const char *appFilePath)
: m_compiler(compiler())
, m_compilerVersion(0, 0)
, m_appFilePath(appFilePath)
{
bic.addBlacklistedClass(QLatin1String("std::*"));
bic.addBlacklistedClass(QLatin1String("qIsNull*"));
bic.addBlacklistedClass(QLatin1String("_*"));
bic.addBlacklistedClass(QLatin1String("<anonymous*"));
/* some system stuff we don't care for */
bic.addBlacklistedClass(QLatin1String("drand"));
bic.addBlacklistedClass(QLatin1String("itimerspec"));
bic.addBlacklistedClass(QLatin1String("lconv"));
bic.addBlacklistedClass(QLatin1String("pthread_attr_t"));
bic.addBlacklistedClass(QLatin1String("random"));
bic.addBlacklistedClass(QLatin1String("sched_attr"));
bic.addBlacklistedClass(QLatin1String("sched_param"));
bic.addBlacklistedClass(QLatin1String("sigcontext"));
bic.addBlacklistedClass(QLatin1String("sigaltstack"));
bic.addBlacklistedClass(QLatin1String("timespec"));
bic.addBlacklistedClass(QLatin1String("timeval"));
bic.addBlacklistedClass(QLatin1String("timex"));
bic.addBlacklistedClass(QLatin1String("tm"));
bic.addBlacklistedClass(QLatin1String("ucontext64"));
bic.addBlacklistedClass(QLatin1String("ucontext"));
bic.addBlacklistedClass(QLatin1String("wait"));
/* QtOpenGL includes qt_windows.h, and some SDKs don't have these structs present */
bic.addBlacklistedClass(QLatin1String("tagTITLEBARINFO"));
bic.addBlacklistedClass(QLatin1String("tagMENUITEMINFOA"));
bic.addBlacklistedClass(QLatin1String("tagMENUITEMINFOW"));
bic.addBlacklistedClass(QLatin1String("tagENHMETAHEADER"));
/* some bug in gcc also reported template instanciations */
bic.addBlacklistedClass(QLatin1String("QTypeInfo<*>"));
bic.addBlacklistedClass(QLatin1String("QMetaTypeId<*>"));
bic.addBlacklistedClass(QLatin1String("QVector<QGradientStop>*"));
/* this guy is never instantiated, just for compile-time checking */
bic.addBlacklistedClass(QLatin1String("QMap<*>::PayloadNode"));
/* QFileEngine was removed in 4.1 */
bic.addBlacklistedClass(QLatin1String("QFileEngine"));
bic.addBlacklistedClass(QLatin1String("QFileEngineHandler"));
bic.addBlacklistedClass(QLatin1String("QFlags<QFileEngine::FileFlag>"));
/* QTest::toString lambda error is false positive */
bic.addBlacklistedClass(QRegularExpression::escape(QLatin1String("QTest::toString(const T&) [with T = QUrl]::__lambda0")));
/* Private classes */
bic.addBlacklistedClass(QLatin1String("QBrushData"));
bic.addBlacklistedClass(QLatin1String("QObjectData"));
bic.addBlacklistedClass(QLatin1String("QAtomic"));
bic.addBlacklistedClass(QLatin1String("QBasicAtomic"));
bic.addBlacklistedClass(QLatin1String("QRegion::QRegionData"));
bic.addBlacklistedClass(QLatin1String("QtConcurrent::ThreadEngineSemaphore"));
bic.addBlacklistedClass(QLatin1String("QDrawPixmaps::Data"));
bic.addBlacklistedClass(QLatin1String("QS60Style"));
bic.addBlacklistedClass(QLatin1String("QPointerBase"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctionsPrivate"));
bic.addBlacklistedClass(QLatin1String("QOpenGLExtraFunctionsPrivate::Functions"));
bic.addBlacklistedClass(QLatin1String("QOpenGLExtraFunctionsPrivate"));
bic.addBlacklistedClass(QLatin1String("QGLFunctionsPrivate"));
bic.addBlacklistedClass(QLatin1String("QDebug::Stream"));
bic.addBlacklistedClass(QLatin1String("QtPrivate::*"));
/* Jambi-related classes in Designer */
bic.addBlacklistedClass(QLatin1String("QDesignerLanguageExtension"));
/* Frederik says it's undocumented and private :) */
bic.addBlacklistedClass(QLatin1String("QAccessible"));
bic.addBlacklistedClass(QLatin1String("QAccessible::QPrivateSignal"));
bic.addBlacklistedClass(QLatin1String("QAccessibleWidget"));
bic.addBlacklistedClass(QLatin1String("QAccessibleTextInterface"));
bic.addBlacklistedClass(QLatin1String("QAccessibleEditableTextInterface"));
bic.addBlacklistedClass(QLatin1String("QAccessibleValueInterface"));
bic.addBlacklistedClass(QLatin1String("QAccessibleTableCellInterface"));
bic.addBlacklistedClass(QLatin1String("QAccessibleTableInterface"));
bic.addBlacklistedClass(QLatin1String("QAccessibleActionInterface"));
bic.addBlacklistedClass(QLatin1String("QAccessibleImageInterface"));
// Accidentally made public in 5.4.0, all in separate headers that don't start with q and look out of place
const char *accessibilityClasses[] = {
"QAccessibleAbstractScrollArea",
"QAccessibleAbstractSlider",
"QAccessibleAbstractSpinBox",
"QAccessibleButton",
"QAccessibleCalendarWidget",
"QAccessibleComboBox",
"QAccessibleDial",
"QAccessibleDialogButtonBox",
"QAccessibleDisplay",
"QAccessibleDockWidget",
"QAccessibleDoubleSpinBox",
"QAccessibleGroupBox",
"QAccessibleLineEdit",
"QAccessibleMainWindow",
"QAccessibleMdiArea",
"QAccessibleMdiSubWindow",
"QAccessibleMenu",
"QAccessibleMenuBar",
"QAccessibleMenuItem",
"QAccessiblePlainTextEdit",
"QAccessibleProgressBar",
"QAccessibleScrollArea",
"QAccessibleScrollBar",
"QAccessibleSlider",
"QAccessibleSpinBox",
"QAccessibleStackedWidget",
"QAccessibleTabBar",
"QAccessibleTable",
"QAccessibleTableCell",
"QAccessibleTableCornerButton",
"QAccessibleTableHeaderCell",
"QAccessibleTextBrowser",
"QAccessibleTextEdit",
"QAccessibleTextWidget",
"QAccessibleToolBox",
"QAccessibleToolButton",
"QAccessibleTree",
"QAccessibleWindowContainer"
};
for (uint i = 0; i < sizeof(accessibilityClasses)/sizeof(char*); ++i) {
bic.addBlacklistedClass(QLatin1String(accessibilityClasses[i]));
}
/* This structure is semi-private and should never shrink */
bic.addBlacklistedClass(QLatin1String("QVFbHeader"));
/* Those structures have a version field that allows extension */
bic.addBlacklistedClass(QLatin1String("QDeclarativePrivate::RegisterType"));
bic.addBlacklistedClass(QLatin1String("QQmlPrivate::RegisterType"));
bic.addBlacklistedClass(QLatin1String("QQmlPrivate::RegisterTypeAndRevisions"));
bic.addBlacklistedClass(QLatin1String("QQmlPrivate::RegisterSingletonType"));
bic.addBlacklistedClass(QLatin1String("QQmlPrivate::RegisterInterface"));
/* this class is not used in a way that requires its size to be stable,
but it has grown between releases */
bic.addBlacklistedClass(QLatin1String("QQmlPrivate::AOTCompiledFunction"));
bic.addBlacklistedClass(QLatin1String("QQmlPrivate::TypedFunction"));
/* This class was added in a patch release */
bic.addBlacklistedClass(QLatin1String("QQmlPrivate::AOTTrackedLocalsStorage"));
/* according to Thiago this is a false positive */
bic.addBlacklistedClass(QLatin1String("QLoggingCategory::AtomicBools"));
bic.addBlacklistedClass(QLatin1String("QOperatingSystemVersion::HighSierra"));
/* according to Sean Harmer these are a false positive (qtbase/ea80316f) */
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_1_1_DeprecatedBackend"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_2_0_DeprecatedBackend"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_3_0_DeprecatedBackend"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_1_1_CoreBackend"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_2_0_CoreBackend"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_3_0_CoreBackend"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_3_3_CoreBackend"));
bic.addBlacklistedClass(QLatin1String("QOpenGLFunctions_4_3_CoreBackend"));
/* this class is not used in a way that requires its size to be stable,
but it has grown between releases */
bic.addBlacklistedClass(QLatin1String("QBindingStatus"));
/* don't bic-test Vulkan types */
bic.addBlacklistedClass(QLatin1String("Vk*"));
/* don't bic-test Vulkan video codec types */
bic.addBlacklistedClass(QLatin1String("StdVideo*"));
/* vtable churn */
bic.addBlacklistedClass(QLatin1String("QNativeInterface::QEGLContext"));
bic.addBlacklistedClass(QLatin1String("QNativeInterface::QWaylandApplication"));
/* deliberately removed */
bic.addBlacklistedClass(QLatin1String("QNativeInterface::QSGVulkanTexture*"));
/* deliberately added in a patch release */
bic.addBlacklistedClass(QLatin1String("QVulkanLayer"));
bic.addBlacklistedClass(QLatin1String("QVulkanExtension"));
bic.addBlacklistedClass(QLatin1String("QVulkanInstance"));
bic.addBlacklistedClass(QLatin1String("QOperatingSystemVersionUnexported"));
bic.addBlacklistedClass(QLatin1String("QAnyStringView::QCharContainer"));
/* allowed to grow */
bic.addBlacklistedClass(QLatin1String("QSGMaterialShader::GraphicsPipelineState"));
/* intentionally removed */
bic.addBlacklistedClass(QLatin1String("QDeferredDeleteEvent"));
bic.addBlacklistedClass(QLatin1String("QUntypedPropertyData::InheritsQUntypedPropertyData"));
/* an override added in a patch release */
bic.addBlacklistedClass(QLatin1String("QDialogButtonBox"));
/* likewise, an override added in another patch release */
bic.addBlacklistedClass(QLatin1String("QRasterWindow"));
/* intentionally ABI-unstable */
bic.addBlacklistedClass(QLatin1String("QSignalSpy"));
bic.addBlacklistedClass(QLatin1String("QTest::*"));
bic.addBlacklistedClass(QLatin1String("QJSListForInIterator"));
bic.addBlacklistedClass(QLatin1String("QJSListForOfIterator"));
}
void tst_Bic::initTestCase()
{
const char moduleVar[] = "QT_MODULE_TO_TEST";
#ifndef Q_OS_WIN
const char qmake[] = "qmake";
#else
const char qmake[] = "qmake.exe";
#endif
qWarning("This test needs the correct qmake in PATH, we need it to generate INCPATH for qt modules.");
QString moduleEnvVar = QFile::decodeName(qgetenv(moduleVar));
bool qt = moduleEnvVar.startsWith(QLatin1String("qt="));
if (qt) {
qtDir = QFile::decodeName(qgetenv("QTDIR"));
if (qtDir.isEmpty())
QSKIP("QTDIR is unset. QTDIR must point to Qt headers when testing in qt mode.");
QStringList qtComponents = moduleEnvVar.split(QLatin1Char(';'));
QStringList qtSplit = qtComponents[0].split(QLatin1Char('='));
if (qtSplit.size() == 2) {
qtModules = qtSplit[1].split(QLatin1Char(','));
if (qtComponents.size() == 2)
qtSourceDir = qtComponents[1];
}
} else {
qtModuleDir = QDir::cleanPath(QFile::decodeName(qgetenv(moduleVar)));
if (qtModuleDir.isEmpty()) {
QSKIP("$QT_MODULE_TO_TEST is unset - nothing to test. "
"Set QT_MODULE_TO_TEST to the absolute path of a Qt module to test.");
}
}
if (m_compiler != QLatin1String("g++")) {
const QString message = QLatin1String("Support for \"")
+ m_compiler + QLatin1String("\" is not implemented yet.");
QSKIP(qPrintable(message));
}
if (qgetenv("PATH").contains("teambuilder"))
qWarning("This test might not work with teambuilder, consider switching it off.");
if (!qt) {
QString configFile = qtModuleDir + "/tests/global/global.cfg";
if (!QFile(configFile).exists()) {
QSKIP(
qPrintable(QString(
"%1 does not exist. Create it if you want to run this test."
).arg(configFile))
);
}
QString workDir = qtModuleDir + QStringLiteral("/tests/global");
modules = qt_tests_shared_global_get_modules(workDir, configFile);
if (!modules.size())
QSKIP("No modules found.");
incPaths = qt_tests_shared_global_get_include_paths(workDir, modules);
} else
incPaths << QLatin1String("-I") + qtDir + QLatin1String("/include");
QVERIFY2(incPaths.size() > 0, "Parse INCPATH failed.");
m_compilerArguments = compilerArguments(m_compiler, incPaths);
// Run compiler to obtain version information.
QString output;
QString errorMessage;
QVERIFY2(compilerVersion(m_compiler, &output, &m_compilerVersion, &errorMessage), qPrintable(errorMessage));
m_fileSuffix = fileSuffix(m_compiler, m_compilerVersion);
QString message;
QTextStream str(&message);
str << "\nBinary : " << m_appFilePath
<< "\nBuilt : " << __DATE__
<< "\nQTDIR : " << qgetenv("QTDIR")
<< '\n' << moduleVar << ": " << qtModuleDir
<< "\nqmake : " << QStandardPaths::findExecutable(qmake) << '\n'
<< "\nCompiler: " << m_compiler << ' ' << m_compilerVersion.first << '.'
<< m_compilerVersion.second << '\n' << output << "\nArguments: ";
for (int i = 0; i < m_compilerArguments.size(); ++i) {
if (i)
str << ' ';
const bool needsQuote = m_compilerArguments.at(i).contains(QLatin1Char(' '));
if (needsQuote)
str << '"';
str << m_compilerArguments.at(i);
if (needsQuote)
str << '"';
}
str << "\n\nFile suffix: " << m_fileSuffix << "\n\n";
qDebug().nospace()
#if QT_VERSION >= 0x050000
.noquote()
#endif
<< message;
}
void tst_Bic::cleanupTestCase()
{
}
void tst_Bic::sizesAndVTables_data()
{
if (m_fileSuffix.isEmpty())
QSKIP("Test not implemented for this compiler/platform");
if (m_fileSuffix == QLatin1String(noneSuchFileSuffix))
QSKIP("No reference files found for this platform");
QTest::addColumn<QString>("libName");
QTest::addColumn<QString>("oldLib");
QTest::addColumn<bool>("isPatchRelease");
QStringList keys;
if (qtModules.isEmpty())
keys = modules.keys();
else
keys << QLatin1String("qt");
int major = QT_VERSION_MAJOR;
int minor = QT_VERSION_MINOR;
int patch = QT_VERSION_PATCH;
QFile qmakeConf(qtSourceDir.isEmpty() ? qtModuleDir : qtSourceDir + "/.qmake.conf");
if (qmakeConf.open(QIODevice::ReadOnly)) {
const QString contents = QString::fromUtf8(qmakeConf.readAll());
qmakeConf.close();
QRegularExpression versionRegExp("MODULE_VERSION\\s*=\\s*(\\d+)\\.(\\d+)\\.(\\d+)");
QRegularExpressionMatch match = versionRegExp.match(contents);
if (match.hasMatch()) {
major = match.captured(1).toInt();
minor = match.captured(2).toInt();
patch = match.captured(3).toInt();
qDebug() << "Detected module version major:" << major << "minor:" << minor << "patch:" << patch;
}
}
if (minor == 0) {
QSKIP("This is the first minor release in the major series, there is no binary compatibility reference data by definition.");
}
for (int i = 0, end = keys.size(); i < end; i++) {
QString key = keys.at(i);
for (int i = 0; i <= minor; ++i) {
if (i != minor || patch) {
#if QT_VERSION >= 0x060000
QTest::newRow(key.toLatin1() + ":6." + QByteArray::number(i))
#else
QTest::newRow(key.toLatin1() + ":5." + QByteArray::number(i))
#endif
<< key
<< (QString(qtSourceDir.isEmpty() ? qtModuleDir : qtSourceDir + "/tests/auto/bic/data/%1.")
+ QString::number(major)
+ QLatin1Char('.')
+ QString::number(i)
+ QLatin1String(".0.") + m_fileSuffix + QLatin1String(".txt"))
<< (i == minor && patch);
}
}
}
}
#ifndef QT_NO_PROCESS
QBic::Info tst_Bic::getCurrentInfo(const QString &libName)
{
QBic::Info &inf = cachedCurrentInfo[libName];
if (!inf.classSizes.isEmpty())
return inf;
QTemporaryFile tmpQFile;
if (!tmpQFile.open()) {
qFatal() << "Could not open temporary file" << tmpQFile.fileName()
<< "for writing" << tmpQFile.errorString();
return {};
}
QString tmpFileName = tmpQFile.fileName();
QByteArray tmpFileContents;
if (qtModules.isEmpty()) {
tmpFileContents = "#include<" + libName.toLatin1() + "/" + libName.toLatin1() + ">\n";
} else {
for (auto &&x : qtModules)
tmpFileContents += "#include<" + x.toLatin1() + "/" + x.toLatin1() + ">\n";
}
tmpQFile.write(tmpFileContents);
tmpQFile.flush();
QStringList args = m_compilerArguments;
args.append(tmpFileName);
QProcess proc;
proc.start(m_compiler, args, QIODevice::ReadOnly);
if (!proc.waitForFinished(6000000)) {
qWarning() << m_compiler << "didn't finish" << proc.errorString();
return QBic::Info();
}
if (proc.exitCode() != 0) {
qWarning() << m_compiler << "returned with" << proc.exitCode();
qDebug() << proc.readAllStandardError();
return QBic::Info();
}
QString errs = QString::fromLocal8Bit(proc.readAllStandardError().constData());
if (!errs.isEmpty()) {
qDebug() << "Arguments:" << args << "Warnings:" << errs;
return QBic::Info();
}
// See if we find the gcc output file, which seems to change
// from release to release
QDir dir;
QStringList files = dir.entryList(QStringList() << "*.class");
if (files.isEmpty()) {
const QString message = QLatin1String("Could not locate the GCC output file in ")
+ QDir::toNativeSeparators(dir.absolutePath())
+ QLatin1String(", update this test");
qFatal("%s", qPrintable(message));
return QBic::Info();
} else if (files.size() > 1) {
const QString message = QLatin1String("Located more than one output file (")
+ files.join(QLatin1String(" ")) + QLatin1String(") in ")
+ QDir::toNativeSeparators(dir.absolutePath())
+ QLatin1String(", please clean up before running this test");
qFatal("%s", qPrintable(message));
return QBic::Info();
}
QString resultFileName = files.first();
inf = bic.parseFile(resultFileName);
QFile::remove(resultFileName);
tmpQFile.close();
return inf;
}
#endif
void tst_Bic::sizesAndVTables()
{
#if !defined(Q_CC_GNU)
QSKIP("Test not implemented for this compiler/platform");
#elif defined(QT_NO_PROCESS)
QSKIP("This Qt build does not have QProcess support");
#else
QFETCH(QString, libName);
QFETCH(QString, oldLib);
QFETCH(bool, isPatchRelease);
bool isFailed = false;
if (oldLib.isEmpty())
QSKIP("No platform spec found for this platform/version.");
const QString oldLibFileName = oldLib.arg(libName);
if (!QFile::exists(oldLibFileName)) {
const QString message = QLatin1String("No platform spec found for this platform/version - ")
+ QDir::toNativeSeparators(oldLibFileName) + QLatin1String(" not found.");
QSKIP(qPrintable(message));
}
const QBic::Info oldLibInfo = bic.parseFile(oldLibFileName);
QVERIFY(!oldLibInfo.classVTables.isEmpty());
const QBic::Info currentLibInfo = getCurrentInfo(libName);
QVERIFY(!currentLibInfo.classVTables.isEmpty());
const QBic::VTableDiff diff = bic.diffVTables(oldLibInfo, currentLibInfo);
if (!diff.removedVTables.isEmpty()) {
qWarning() << "VTables for the following classes were removed" << diff.removedVTables;
isFailed = true;
}
if (!diff.modifiedVTables.isEmpty()) {
if (diff.modifiedVTables.size() != 1 ||
strcmp(QTest::currentDataTag(), "4.4") != 0 ||
diff.modifiedVTables.at(0).first != "QGraphicsProxyWidget") {
for (const QStringPair &entry : diff.modifiedVTables)
qWarning() << "modified VTable:\n Old: " << entry.first
<< "\n New: " << entry.second;
isFailed = true;
}
}
if (isPatchRelease && !diff.addedVTables.isEmpty()) {
qWarning() << "VTables for the following classes were added in a patch release:"
<< diff.addedVTables;
isFailed = true;
}
if (isPatchRelease && !diff.reimpMethods.isEmpty()) {
for (const QStringPair &entry : diff.reimpMethods)
qWarning() << "reimplemented virtual in patch release:\n Old: " << entry.first
<< "\n New: " << entry.second;
isFailed = true;
}
QBic::SizeDiff sizeDiff = bic.diffSizes(oldLibInfo, currentLibInfo);
if (!sizeDiff.mismatch.isEmpty()) {
for (QString className : sizeDiff.mismatch)
qWarning() << "size mismatch for" << className
<< "old" << oldLibInfo.classSizes.value(className)
<< "new" << currentLibInfo.classSizes.value(className);
isFailed = true;
}
#ifdef Q_CC_MINGW
/**
* These symbols are from Windows' imm.h header, and is available
* conditionally depending on the value of the WINVER define. We pull
* them out since they're not relevant to the testing done.
*/
sizeDiff.removed.removeAll(QLatin1String("tagIMECHARPOSITION"));
sizeDiff.removed.removeAll(QLatin1String("tagRECONVERTSTRING"));
#endif
if (!sizeDiff.removed.isEmpty()) {
qWarning() << "the following classes were removed:" << sizeDiff.removed;
isFailed = true;
}
if (isPatchRelease && !sizeDiff.added.isEmpty()) {
qWarning() << "the following classes were added in a patch release:" << sizeDiff.added;
isFailed = true;
}
if (isFailed)
QFAIL("Test failed, read warnings above.");
#endif
}
int main(int argc, char *argv[])
{
tst_Bic tc(argv[0]);
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&tc, argc, argv);
}
#include "tst_bic.moc"