qt 6.5.1 original

This commit is contained in:
kleuter
2023-10-29 23:33:08 +01:00
parent 71d22ab6b0
commit 85d238dfda
21202 changed files with 5499099 additions and 0 deletions

View File

@ -0,0 +1,18 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(kernel)
if(NOT UIKIT)
add_subdirectory(animation)
add_subdirectory(global)
add_subdirectory(io)
add_subdirectory(itemmodels)
add_subdirectory(mimetypes)
add_subdirectory(plugin)
add_subdirectory(serialization)
add_subdirectory(text)
add_subdirectory(thread)
add_subdirectory(time)
add_subdirectory(tools)
endif()
add_subdirectory(platform)

View File

@ -0,0 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
add_subdirectory(qabstractanimation)
add_subdirectory(qanimationgroup)
add_subdirectory(qparallelanimationgroup)
add_subdirectory(qpauseanimation)
add_subdirectory(qsequentialanimationgroup)
add_subdirectory(qvariantanimation)
if(TARGET Qt::Widgets)
add_subdirectory(qpropertyanimation)
endif()

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qabstractanimation Test:
#####################################################################
qt_internal_add_test(tst_qabstractanimation
SOURCES
tst_qabstractanimation.cpp
LIBRARIES
Qt::TestPrivate
)

View File

@ -0,0 +1,298 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qabstractanimation.h>
#include <QtCore/qanimationgroup.h>
#include <QTest>
#include <QtTest/private/qpropertytesthelper_p.h>
class tst_QAbstractAnimation : public QObject
{
Q_OBJECT
private slots:
void construction();
void destruction();
void currentLoop();
void currentLoopTime();
void currentTime();
void direction();
void group();
void loopCount();
void state();
void totalDuration();
void avoidJumpAtStart();
void avoidJumpAtStartWithStop();
void avoidJumpAtStartWithRunning();
void stateBinding();
void loopCountBinding();
void currentTimeBinding();
void currentLoopBinding();
void directionBinding();
};
class TestableQAbstractAnimation : public QAbstractAnimation
{
Q_OBJECT
public:
TestableQAbstractAnimation() : m_duration(10) {}
virtual ~TestableQAbstractAnimation() override { }
int duration() const override { return m_duration; }
virtual void updateCurrentTime(int) override {}
void setDuration(int duration) { m_duration = duration; }
private:
int m_duration;
};
class DummyQAnimationGroup : public QAnimationGroup
{
Q_OBJECT
public:
int duration() const override { return 10; }
virtual void updateCurrentTime(int) override {}
};
void tst_QAbstractAnimation::construction()
{
TestableQAbstractAnimation anim;
}
void tst_QAbstractAnimation::destruction()
{
TestableQAbstractAnimation *anim = new TestableQAbstractAnimation;
delete anim;
// Animations should stop when deleted
auto *stopWhenDeleted = new TestableQAbstractAnimation;
QAbstractAnimation::State lastOldState, lastNewState;
QObject::connect(stopWhenDeleted, &QAbstractAnimation::stateChanged,
[&](QAbstractAnimation::State newState, QAbstractAnimation::State oldState) {
lastNewState = newState;
lastOldState = oldState;
});
stopWhenDeleted->start();
QCOMPARE(lastOldState, QAbstractAnimation::Stopped);
QCOMPARE(lastNewState, QAbstractAnimation::Running);
delete stopWhenDeleted;
QCOMPARE(lastOldState, QAbstractAnimation::Running);
QCOMPARE(lastNewState, QAbstractAnimation::Stopped);
}
void tst_QAbstractAnimation::currentLoop()
{
TestableQAbstractAnimation anim;
QCOMPARE(anim.currentLoop(), 0);
}
void tst_QAbstractAnimation::currentLoopTime()
{
TestableQAbstractAnimation anim;
QCOMPARE(anim.currentLoopTime(), 0);
}
void tst_QAbstractAnimation::currentTime()
{
TestableQAbstractAnimation anim;
QCOMPARE(anim.currentTime(), 0);
anim.setCurrentTime(10);
QCOMPARE(anim.currentTime(), 10);
}
void tst_QAbstractAnimation::direction()
{
TestableQAbstractAnimation anim;
QCOMPARE(anim.direction(), QAbstractAnimation::Forward);
anim.setDirection(QAbstractAnimation::Backward);
QCOMPARE(anim.direction(), QAbstractAnimation::Backward);
anim.setDirection(QAbstractAnimation::Forward);
QCOMPARE(anim.direction(), QAbstractAnimation::Forward);
}
void tst_QAbstractAnimation::group()
{
TestableQAbstractAnimation *anim = new TestableQAbstractAnimation;
DummyQAnimationGroup group;
group.addAnimation(anim);
QCOMPARE(anim->group(), &group);
}
void tst_QAbstractAnimation::loopCount()
{
TestableQAbstractAnimation anim;
QCOMPARE(anim.loopCount(), 1);
anim.setLoopCount(10);
QCOMPARE(anim.loopCount(), 10);
}
void tst_QAbstractAnimation::state()
{
TestableQAbstractAnimation anim;
QCOMPARE(anim.state(), QAbstractAnimation::Stopped);
}
void tst_QAbstractAnimation::totalDuration()
{
TestableQAbstractAnimation anim;
QCOMPARE(anim.duration(), 10);
anim.setLoopCount(5);
QCOMPARE(anim.totalDuration(), 50);
}
void tst_QAbstractAnimation::avoidJumpAtStart()
{
TestableQAbstractAnimation anim;
anim.setDuration(1000);
/*
the timer shouldn't actually start until we hit the event loop,
so the sleep should have no effect
*/
anim.start();
QTest::qSleep(300);
QCoreApplication::processEvents();
QVERIFY(anim.currentTime() < 50);
}
void tst_QAbstractAnimation::avoidJumpAtStartWithStop()
{
TestableQAbstractAnimation anim;
anim.setDuration(1000);
TestableQAbstractAnimation anim2;
anim2.setDuration(1000);
TestableQAbstractAnimation anim3;
anim3.setDuration(1000);
anim.start();
QTest::qWait(300);
anim.stop();
/*
same test as avoidJumpAtStart, but after there is a
running animation that is stopped
*/
anim2.start();
QTest::qSleep(300);
anim3.start();
QCoreApplication::processEvents();
QVERIFY(anim2.currentTime() < 50);
QVERIFY(anim3.currentTime() < 50);
}
void tst_QAbstractAnimation::avoidJumpAtStartWithRunning()
{
TestableQAbstractAnimation anim;
anim.setDuration(2000);
TestableQAbstractAnimation anim2;
anim2.setDuration(1000);
TestableQAbstractAnimation anim3;
anim3.setDuration(1000);
anim.start();
QTest::qWait(300); //make sure timer has started
/*
same test as avoidJumpAtStart, but with an
existing running animation
*/
anim2.start();
QTest::qSleep(300); //force large delta for next tick
anim3.start();
QCoreApplication::processEvents();
QVERIFY(anim2.currentTime() < 50);
QVERIFY(anim3.currentTime() < 50);
}
void tst_QAbstractAnimation::stateBinding()
{
TestableQAbstractAnimation animation;
QTestPrivate::testReadOnlyPropertyBasics(animation, QAbstractAnimation::Stopped,
QAbstractAnimation::Running, "state",
[&] { animation.start(); });
}
void tst_QAbstractAnimation::loopCountBinding()
{
TestableQAbstractAnimation animation;
QTestPrivate::testReadWritePropertyBasics(animation, 42, 43, "loopCount");
}
void tst_QAbstractAnimation::currentTimeBinding()
{
TestableQAbstractAnimation animation;
QProperty<int> currentTimeProperty;
animation.bindableCurrentTime().setBinding(Qt::makePropertyBinding(currentTimeProperty));
QCOMPARE(animation.currentTime(), currentTimeProperty);
// This should cancel the binding
animation.start();
currentTimeProperty = 5;
QVERIFY(animation.currentTime() != currentTimeProperty);
QTestPrivate::testReadWritePropertyBasics(animation, 6, 7, "currentTime");
}
void tst_QAbstractAnimation::currentLoopBinding()
{
TestableQAbstractAnimation animation;
QTestPrivate::testReadOnlyPropertyBasics(animation, 0, 3, "currentLoop", [&] {
// Trigger an update of currentLoop
animation.setLoopCount(4);
// This brings us to the end of the animation, so currentLoop should be loopCount - 1
animation.setCurrentTime(42);
});
}
void tst_QAbstractAnimation::directionBinding()
{
TestableQAbstractAnimation animation;
QTestPrivate::testReadWritePropertyBasics(animation, QAbstractAnimation::Backward,
QAbstractAnimation::Forward, "direction");
// setDirection() may trigger a currentLoop update. Make sure the observers
// are notified about direction and currentLoop changes only after a consistent
// state is reached.
QProperty<int> currLoopObserver;
currLoopObserver.setBinding([&] { return animation.currentLoop(); });
QProperty<QAbstractAnimation::Direction> directionObserver;
directionObserver.setBinding([&] { return animation.direction(); });
animation.setLoopCount(10);
bool currentLoopChanged = false;
auto currentLoopHandler = animation.bindableCurrentLoop().onValueChanged([&] {
QVERIFY(!currentLoopChanged);
QCOMPARE(currLoopObserver, 9);
QCOMPARE(directionObserver, QAbstractAnimation::Backward);
currentLoopChanged = true;
});
bool directionChanged = false;
auto directionHandler = animation.bindableDirection().onValueChanged([&] {
QVERIFY(!directionChanged);
QCOMPARE(currLoopObserver, 9);
QCOMPARE(directionObserver, QAbstractAnimation::Backward);
directionChanged = true;
});
QCOMPARE(animation.direction(), QAbstractAnimation::Forward);
// This will set currentLoop to 9
animation.setDirection(QAbstractAnimation::Backward);
QVERIFY(currentLoopChanged);
QVERIFY(directionChanged);
}
QTEST_MAIN(tst_QAbstractAnimation)
#include "tst_qabstractanimation.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qanimationgroup Test:
#####################################################################
qt_internal_add_test(tst_qanimationgroup
SOURCES
tst_qanimationgroup.cpp
)

View File

@ -0,0 +1,357 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QPauseAnimation>
#include <QVariantAnimation>
#include <QPropertyAnimation>
#include <QSignalSpy>
#include <QtCore/qanimationgroup.h>
#include <QtCore/qsequentialanimationgroup.h>
#include <QtCore/qparallelanimationgroup.h>
Q_DECLARE_METATYPE(QAbstractAnimation::State)
class tst_QAnimationGroup : public QObject
{
Q_OBJECT
public Q_SLOTS:
void initTestCase();
private slots:
void construction();
void emptyGroup();
void setCurrentTime();
void setParentAutoAdd();
void beginNestedGroup();
void addChildTwice();
void loopWithoutStartValue();
};
void tst_QAnimationGroup::initTestCase()
{
qRegisterMetaType<QAbstractAnimation::State>("QAbstractAnimation::State");
}
void tst_QAnimationGroup::construction()
{
QSequentialAnimationGroup animationgroup;
}
class AnimationObject : public QObject
{
Q_OBJECT
Q_PROPERTY(int value READ value WRITE setValue)
public:
AnimationObject(int startValue = 0)
: v(startValue)
{ }
int value() const { return v; }
void setValue(int value) { v = value; }
int v;
};
class TestAnimation : public QVariantAnimation
{
Q_OBJECT
public:
virtual void updateCurrentValue(const QVariant &value) override { Q_UNUSED(value)};
virtual void updateState(QAbstractAnimation::State oldState,
QAbstractAnimation::State newState) override
{
Q_UNUSED(oldState);
Q_UNUSED(newState);
};
};
class UncontrolledAnimation : public QPropertyAnimation
{
Q_OBJECT
public:
UncontrolledAnimation(QObject *target, const QByteArray &propertyName, QObject *parent = nullptr)
: QPropertyAnimation(target, propertyName, parent), id(0)
{
setDuration(250);
}
int duration() const override { return -1; /* not time driven */ }
protected:
void timerEvent(QTimerEvent *event) override
{
if (event->timerId() == id)
stop();
}
void updateRunning(bool running)
{
if (running) {
id = startTimer(500);
} else {
killTimer(id);
id = 0;
}
}
private:
int id;
};
void tst_QAnimationGroup::emptyGroup()
{
QSequentialAnimationGroup group;
QSignalSpy groupStateChangedSpy(&group, &QSequentialAnimationGroup::stateChanged);
QVERIFY(groupStateChangedSpy.isValid());
QCOMPARE(group.state(), QAnimationGroup::Stopped);
group.start();
QCOMPARE(groupStateChangedSpy.size(), 2);
QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(0).first()),
QAnimationGroup::Running);
QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(1).first()),
QAnimationGroup::Stopped);
QCOMPARE(group.state(), QAnimationGroup::Stopped);
QTest::ignoreMessage(QtWarningMsg, "QAbstractAnimation::pause: Cannot pause a stopped animation");
group.pause();
QCOMPARE(groupStateChangedSpy.size(), 2);
QCOMPARE(group.state(), QAnimationGroup::Stopped);
group.start();
QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(2).first()),
QAnimationGroup::Running);
QCOMPARE(qvariant_cast<QAbstractAnimation::State>(groupStateChangedSpy.at(3).first()),
QAnimationGroup::Stopped);
QCOMPARE(group.state(), QAnimationGroup::Stopped);
group.stop();
QCOMPARE(groupStateChangedSpy.size(), 4);
QCOMPARE(group.state(), QAnimationGroup::Stopped);
}
void tst_QAnimationGroup::setCurrentTime()
{
AnimationObject s_o1;
AnimationObject s_o2;
AnimationObject s_o3;
AnimationObject p_o1;
AnimationObject p_o2;
AnimationObject p_o3;
AnimationObject t_o1;
AnimationObject t_o2;
// sequence operating on same object/property
QSequentialAnimationGroup *sequence = new QSequentialAnimationGroup();
QAbstractAnimation *a1_s_o1 = new QPropertyAnimation(&s_o1, "value");
QAbstractAnimation *a2_s_o1 = new QPropertyAnimation(&s_o1, "value");
QAbstractAnimation *a3_s_o1 = new QPropertyAnimation(&s_o1, "value");
a2_s_o1->setLoopCount(3);
sequence->addAnimation(a1_s_o1);
sequence->addAnimation(a2_s_o1);
sequence->addAnimation(a3_s_o1);
// sequence operating on different object/properties
QAnimationGroup *sequence2 = new QSequentialAnimationGroup();
QAbstractAnimation *a1_s_o2 = new QPropertyAnimation(&s_o2, "value");
QAbstractAnimation *a1_s_o3 = new QPropertyAnimation(&s_o3, "value");
sequence2->addAnimation(a1_s_o2);
sequence2->addAnimation(a1_s_o3);
// parallel operating on different object/properties
QAnimationGroup *parallel = new QParallelAnimationGroup();
QAbstractAnimation *a1_p_o1 = new QPropertyAnimation(&p_o1, "value");
QAbstractAnimation *a1_p_o2 = new QPropertyAnimation(&p_o2, "value");
QAbstractAnimation *a1_p_o3 = new QPropertyAnimation(&p_o3, "value");
a1_p_o2->setLoopCount(3);
parallel->addAnimation(a1_p_o1);
parallel->addAnimation(a1_p_o2);
parallel->addAnimation(a1_p_o3);
QAbstractAnimation *notTimeDriven = new UncontrolledAnimation(&t_o1, "value");
QCOMPARE(notTimeDriven->totalDuration(), -1);
QAbstractAnimation *loopsForever = new QPropertyAnimation(&t_o2, "value");
loopsForever->setLoopCount(-1);
QCOMPARE(loopsForever->totalDuration(), -1);
QParallelAnimationGroup group;
group.addAnimation(sequence);
group.addAnimation(sequence2);
group.addAnimation(parallel);
group.addAnimation(notTimeDriven);
group.addAnimation(loopsForever);
// Current time = 1
group.setCurrentTime(1);
QCOMPARE(group.state(), QAnimationGroup::Stopped);
QCOMPARE(sequence->state(), QAnimationGroup::Stopped);
QCOMPARE(a1_s_o1->state(), QAnimationGroup::Stopped);
QCOMPARE(sequence2->state(), QAnimationGroup::Stopped);
QCOMPARE(a1_s_o2->state(), QAnimationGroup::Stopped);
QCOMPARE(parallel->state(), QAnimationGroup::Stopped);
QCOMPARE(a1_p_o1->state(), QAnimationGroup::Stopped);
QCOMPARE(a1_p_o2->state(), QAnimationGroup::Stopped);
QCOMPARE(a1_p_o3->state(), QAnimationGroup::Stopped);
QCOMPARE(notTimeDriven->state(), QAnimationGroup::Stopped);
QCOMPARE(loopsForever->state(), QAnimationGroup::Stopped);
QCOMPARE(group.currentLoopTime(), 1);
QCOMPARE(sequence->currentLoopTime(), 1);
QCOMPARE(a1_s_o1->currentLoopTime(), 1);
QCOMPARE(a2_s_o1->currentLoopTime(), 0);
QCOMPARE(a3_s_o1->currentLoopTime(), 0);
QCOMPARE(a1_s_o2->currentLoopTime(), 1);
QCOMPARE(a1_s_o3->currentLoopTime(), 0);
QCOMPARE(a1_p_o1->currentLoopTime(), 1);
QCOMPARE(a1_p_o2->currentLoopTime(), 1);
QCOMPARE(a1_p_o3->currentLoopTime(), 1);
QCOMPARE(notTimeDriven->currentLoopTime(), 1);
QCOMPARE(loopsForever->currentLoopTime(), 1);
// Current time = 250
group.setCurrentTime(250);
QCOMPARE(group.currentLoopTime(), 250);
QCOMPARE(sequence->currentLoopTime(), 250);
QCOMPARE(a1_s_o1->currentLoopTime(), 250);
QCOMPARE(a2_s_o1->currentLoopTime(), 0);
QCOMPARE(a3_s_o1->currentLoopTime(), 0);
QCOMPARE(a1_s_o2->currentLoopTime(), 250);
QCOMPARE(a1_s_o3->currentLoopTime(), 0);
QCOMPARE(a1_p_o1->currentLoopTime(), 250);
QCOMPARE(a1_p_o2->currentLoopTime(), 0);
QCOMPARE(a1_p_o2->currentLoop(), 1);
QCOMPARE(a1_p_o3->currentLoopTime(), 250);
QCOMPARE(notTimeDriven->currentLoopTime(), 250);
QCOMPARE(loopsForever->currentLoopTime(), 0);
QCOMPARE(loopsForever->currentLoop(), 1);
QCOMPARE(sequence->currentAnimation(), a2_s_o1);
// Current time = 251
group.setCurrentTime(251);
QCOMPARE(group.currentLoopTime(), 251);
QCOMPARE(sequence->currentLoopTime(), 251);
QCOMPARE(a1_s_o1->currentLoopTime(), 250);
QCOMPARE(a2_s_o1->currentLoopTime(), 1);
QCOMPARE(a2_s_o1->currentLoop(), 0);
QCOMPARE(a3_s_o1->currentLoopTime(), 0);
QCOMPARE(sequence2->currentLoopTime(), 251);
QCOMPARE(a1_s_o2->currentLoopTime(), 250);
QCOMPARE(a1_s_o3->currentLoopTime(), 1);
QCOMPARE(a1_p_o1->currentLoopTime(), 250);
QCOMPARE(a1_p_o2->currentLoopTime(), 1);
QCOMPARE(a1_p_o2->currentLoop(), 1);
QCOMPARE(a1_p_o3->currentLoopTime(), 250);
QCOMPARE(notTimeDriven->currentLoopTime(), 251);
QCOMPARE(loopsForever->currentLoopTime(), 1);
QCOMPARE(sequence->currentAnimation(), a2_s_o1);
}
void tst_QAnimationGroup::setParentAutoAdd()
{
QParallelAnimationGroup group;
QVariantAnimation *animation = new QPropertyAnimation(&group);
QCOMPARE(animation->group(), static_cast<QAnimationGroup*>(&group));
}
void tst_QAnimationGroup::beginNestedGroup()
{
QParallelAnimationGroup group;
QAnimationGroup *parent = &group;
for (int i = 0; i < 10; ++i) {
if (i & 1) {
new QParallelAnimationGroup(parent);
} else {
new QSequentialAnimationGroup(parent);
}
QCOMPARE(parent->animationCount(), 1);
QAnimationGroup *child = static_cast<QAnimationGroup *>(parent->animationAt(0));
QCOMPARE(child->parent(), static_cast<QObject *>(parent));
if (i & 1)
QVERIFY(qobject_cast<QParallelAnimationGroup *> (child));
else
QVERIFY(qobject_cast<QSequentialAnimationGroup *> (child));
parent = child;
}
}
void tst_QAnimationGroup::addChildTwice()
{
QAbstractAnimation *subGroup;
QAbstractAnimation *subGroup2;
QAnimationGroup *parent = new QSequentialAnimationGroup();
subGroup = new QPropertyAnimation();
subGroup->setParent(parent);
parent->addAnimation(subGroup);
QCOMPARE(parent->animationCount(), 1);
parent->clear();
QCOMPARE(parent->animationCount(), 0);
// adding the same item twice to a group will remove the item from its current position
// and append it to the end
subGroup = new QPropertyAnimation(parent);
subGroup2 = new QPropertyAnimation(parent);
QCOMPARE(parent->animationCount(), 2);
QCOMPARE(parent->animationAt(0), subGroup);
QCOMPARE(parent->animationAt(1), subGroup2);
parent->addAnimation(subGroup);
QCOMPARE(parent->animationCount(), 2);
QCOMPARE(parent->animationAt(0), subGroup2);
QCOMPARE(parent->animationAt(1), subGroup);
delete parent;
}
void tst_QAnimationGroup::loopWithoutStartValue()
{
QSequentialAnimationGroup group;
QAnimationGroup *parent = &group;
QObject o;
o.setProperty("ole", 0);
QCOMPARE(o.property("ole").toInt(), 0);
QPropertyAnimation anim1(&o, "ole");
anim1.setEndValue(-50);
anim1.setDuration(100);
QPropertyAnimation anim2(&o, "ole");
anim2.setEndValue(50);
anim2.setDuration(100);
parent->addAnimation(&anim1);
parent->addAnimation(&anim2);
parent->setLoopCount(-1);
parent->start();
QVERIFY(anim1.startValue().isNull());
QCOMPARE(anim1.currentValue().toInt(), 0);
QCOMPARE(parent->currentLoop(), 0);
parent->setCurrentTime(200);
QCOMPARE(parent->currentLoop(), 1);
QCOMPARE(anim1.currentValue().toInt(), 50);
parent->stop();
}
QTEST_MAIN(tst_QAnimationGroup)
#include "tst_qanimationgroup.moc"

View File

@ -0,0 +1,2 @@
[deleteChildrenWithRunningGroup]
macos

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qparallelanimationgroup Test:
#####################################################################
qt_internal_add_test(tst_qparallelanimationgroup
SOURCES
tst_qparallelanimationgroup.cpp
)

View File

@ -0,0 +1,6 @@
[pauseAndPropertyAnimations]
macos
[multipleSequentialGroups]
macos
[noTimerUpdates]
macos

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qpauseanimation Test:
#####################################################################
qt_internal_add_test(tst_qpauseanimation
SOURCES
tst_qpauseanimation.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,462 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtCore/qpauseanimation.h>
#include <QtCore/qpropertyanimation.h>
#include <QtCore/qsequentialanimationgroup.h>
#include <QParallelAnimationGroup>
#include <private/qabstractanimation_p.h>
#if defined(Q_OS_WIN) || defined(Q_OS_ANDROID)
# define BAD_TIMER_RESOLUTION
#endif
#ifdef BAD_TIMER_RESOLUTION
static const char timerError[] = "On this platform, consistent timing is not working properly due to bad timer resolution";
# define WAIT_FOR_STOPPED(animation, duration) \
QTest::qWait(duration); \
if (animation.state() != QAbstractAnimation::Stopped) \
QEXPECT_FAIL("", timerError, Abort); \
QCOMPARE(animation.state(), QAbstractAnimation::Stopped)
#else
// Use QTRY_COMPARE with one additional timer tick
# define WAIT_FOR_STOPPED(animation, duration) \
QTRY_COMPARE_WITH_TIMEOUT(animation.state(), QAbstractAnimation::Stopped, (duration))
#endif
class TestablePauseAnimation : public QPauseAnimation
{
Q_OBJECT
public:
TestablePauseAnimation(QObject *parent = nullptr)
: QPauseAnimation(parent),
m_updateCurrentTimeCount(0)
{
}
int m_updateCurrentTimeCount;
protected:
void updateCurrentTime(int currentTime) override
{
QPauseAnimation::updateCurrentTime(currentTime);
++m_updateCurrentTimeCount;
}
};
class EnableConsistentTiming
{
public:
EnableConsistentTiming()
{
QUnifiedTimer *timer = QUnifiedTimer::instance();
timer->setConsistentTiming(true);
}
~EnableConsistentTiming()
{
QUnifiedTimer *timer = QUnifiedTimer::instance();
timer->setConsistentTiming(false);
}
};
class tst_QPauseAnimation : public QObject
{
Q_OBJECT
public Q_SLOTS:
void initTestCase();
private slots:
void changeDirectionWhileRunning();
void noTimerUpdates_data();
void noTimerUpdates();
void multiplePauseAnimations();
void pauseAndPropertyAnimations();
void pauseResume();
void sequentialPauseGroup();
void sequentialGroupWithPause();
void multipleSequentialGroups();
void zeroDuration();
void bindings();
};
void tst_QPauseAnimation::initTestCase()
{
qRegisterMetaType<QAbstractAnimation::State>("QAbstractAnimation::State");
qRegisterMetaType<QAbstractAnimation::DeletionPolicy>("QAbstractAnimation::DeletionPolicy");
}
void tst_QPauseAnimation::changeDirectionWhileRunning()
{
EnableConsistentTiming enabled;
TestablePauseAnimation animation;
animation.setDuration(400);
animation.start();
QTRY_COMPARE(animation.state(), QAbstractAnimation::Running);
animation.setDirection(QAbstractAnimation::Backward);
const int expectedDuration = animation.totalDuration() + 100;
WAIT_FOR_STOPPED(animation, expectedDuration);
}
void tst_QPauseAnimation::noTimerUpdates_data()
{
QTest::addColumn<int>("duration");
QTest::addColumn<int>("loopCount");
QTest::newRow("0") << 200 << 1;
QTest::newRow("1") << 160 << 1;
QTest::newRow("2") << 160 << 2;
QTest::newRow("3") << 200 << 3;
}
void tst_QPauseAnimation::noTimerUpdates()
{
EnableConsistentTiming enabled;
QFETCH(int, duration);
QFETCH(int, loopCount);
TestablePauseAnimation animation;
animation.setDuration(duration);
animation.setLoopCount(loopCount);
animation.start();
const int expectedDuration = animation.totalDuration() + 150;
WAIT_FOR_STOPPED(animation, expectedDuration);
const int expectedLoopCount = 1 + loopCount;
#ifdef BAD_TIMER_RESOLUTION
if (animation.m_updateCurrentTimeCount != expectedLoopCount)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(animation.m_updateCurrentTimeCount, expectedLoopCount);
}
void tst_QPauseAnimation::multiplePauseAnimations()
{
EnableConsistentTiming enabled;
TestablePauseAnimation animation;
animation.setDuration(200);
TestablePauseAnimation animation2;
animation2.setDuration(800);
animation.start();
animation2.start();
const int expectedDuration = animation.totalDuration() + 150;
WAIT_FOR_STOPPED(animation, expectedDuration);
#ifdef BAD_TIMER_RESOLUTION
if (animation2.state() != QAbstractAnimation::Running)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(animation2.state(), QAbstractAnimation::Running);
#ifdef BAD_TIMER_RESOLUTION
if (animation.m_updateCurrentTimeCount != 2)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(animation.m_updateCurrentTimeCount, 2);
#ifdef BAD_TIMER_RESOLUTION
if (animation2.m_updateCurrentTimeCount != 2)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(animation2.m_updateCurrentTimeCount, 2);
WAIT_FOR_STOPPED(animation2, 600);
#ifdef BAD_TIMER_RESOLUTION
if (animation2.m_updateCurrentTimeCount != 3)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(animation2.m_updateCurrentTimeCount, 3);
}
void tst_QPauseAnimation::pauseAndPropertyAnimations()
{
EnableConsistentTiming enabled;
TestablePauseAnimation pause;
pause.setDuration(200);
QObject o;
o.setProperty("ole", 42);
QPropertyAnimation animation(&o, "ole");
animation.setEndValue(43);
pause.start();
QTest::qWait(100);
animation.start();
QCOMPARE(animation.state(), QAbstractAnimation::Running);
QCOMPARE(pause.state(), QAbstractAnimation::Running);
QCOMPARE(pause.m_updateCurrentTimeCount, 2);
const int expectedDuration = animation.totalDuration() + 150;
WAIT_FOR_STOPPED(animation, expectedDuration);
QCOMPARE(pause.state(), QAbstractAnimation::Stopped);
QVERIFY(pause.m_updateCurrentTimeCount > 3);
}
void tst_QPauseAnimation::pauseResume()
{
TestablePauseAnimation animation;
animation.setDuration(400);
animation.start();
QCOMPARE(animation.state(), QAbstractAnimation::Running);
QTest::qWait(200);
animation.pause();
QCOMPARE(animation.state(), QAbstractAnimation::Paused);
animation.start();
QTRY_COMPARE(animation.state(), QAbstractAnimation::Stopped);
#ifdef BAD_TIMER_RESOLUTION
if (animation.m_updateCurrentTimeCount < 3)
QEXPECT_FAIL("", timerError, Abort);
#endif
QVERIFY2(animation.m_updateCurrentTimeCount >= 3, qPrintable(
QString::fromLatin1("animation.m_updateCurrentTimeCount = %1").arg(animation.m_updateCurrentTimeCount)));
}
void tst_QPauseAnimation::sequentialPauseGroup()
{
QSequentialAnimationGroup group;
TestablePauseAnimation animation1(&group);
animation1.setDuration(200);
TestablePauseAnimation animation2(&group);
animation2.setDuration(200);
TestablePauseAnimation animation3(&group);
animation3.setDuration(200);
group.start();
QCOMPARE(animation1.m_updateCurrentTimeCount, 1);
QCOMPARE(animation2.m_updateCurrentTimeCount, 0);
QCOMPARE(animation3.m_updateCurrentTimeCount, 0);
QCOMPARE(group.state(), QAbstractAnimation::Running);
QCOMPARE(animation1.state(), QAbstractAnimation::Running);
QCOMPARE(animation2.state(), QAbstractAnimation::Stopped);
QCOMPARE(animation3.state(), QAbstractAnimation::Stopped);
group.setCurrentTime(250);
QCOMPARE(animation1.m_updateCurrentTimeCount, 2);
QCOMPARE(animation2.m_updateCurrentTimeCount, 1);
QCOMPARE(animation3.m_updateCurrentTimeCount, 0);
QCOMPARE(group.state(), QAbstractAnimation::Running);
QCOMPARE(animation1.state(), QAbstractAnimation::Stopped);
QCOMPARE((QAbstractAnimation*)&animation2, group.currentAnimation());
QCOMPARE(animation2.state(), QAbstractAnimation::Running);
QCOMPARE(animation3.state(), QAbstractAnimation::Stopped);
group.setCurrentTime(500);
QCOMPARE(animation1.m_updateCurrentTimeCount, 2);
QCOMPARE(animation2.m_updateCurrentTimeCount, 2);
QCOMPARE(animation3.m_updateCurrentTimeCount, 1);
QCOMPARE(group.state(), QAbstractAnimation::Running);
QCOMPARE(animation1.state(), QAbstractAnimation::Stopped);
QCOMPARE(animation2.state(), QAbstractAnimation::Stopped);
QCOMPARE((QAbstractAnimation*)&animation3, group.currentAnimation());
QCOMPARE(animation3.state(), QAbstractAnimation::Running);
group.setCurrentTime(750);
QCOMPARE(group.state(), QAbstractAnimation::Stopped);
QCOMPARE(animation1.state(), QAbstractAnimation::Stopped);
QCOMPARE(animation2.state(), QAbstractAnimation::Stopped);
QCOMPARE(animation3.state(), QAbstractAnimation::Stopped);
QCOMPARE(animation1.m_updateCurrentTimeCount, 2);
QCOMPARE(animation2.m_updateCurrentTimeCount, 2);
QCOMPARE(animation3.m_updateCurrentTimeCount, 2);
}
void tst_QPauseAnimation::sequentialGroupWithPause()
{
QSequentialAnimationGroup group;
QObject o;
o.setProperty("ole", 42);
QPropertyAnimation animation(&o, "ole", &group);
animation.setEndValue(43);
TestablePauseAnimation pause(&group);
pause.setDuration(250);
group.start();
QCOMPARE(group.state(), QAbstractAnimation::Running);
QCOMPARE(animation.state(), QAbstractAnimation::Running);
QCOMPARE(pause.state(), QAbstractAnimation::Stopped);
group.setCurrentTime(300);
QCOMPARE(group.state(), QAbstractAnimation::Running);
QCOMPARE(animation.state(), QAbstractAnimation::Stopped);
QCOMPARE((QAbstractAnimation*)&pause, group.currentAnimation());
QCOMPARE(pause.state(), QAbstractAnimation::Running);
group.setCurrentTime(600);
QCOMPARE(group.state(), QAbstractAnimation::Stopped);
QCOMPARE(animation.state(), QAbstractAnimation::Stopped);
QCOMPARE(pause.state(), QAbstractAnimation::Stopped);
QCOMPARE(pause.m_updateCurrentTimeCount, 2);
}
void tst_QPauseAnimation::multipleSequentialGroups()
{
EnableConsistentTiming enabled;
QParallelAnimationGroup group;
group.setLoopCount(2);
QSequentialAnimationGroup subgroup1(&group);
QObject o;
o.setProperty("ole", 42);
QPropertyAnimation animation(&o, "ole", &subgroup1);
animation.setEndValue(43);
animation.setDuration(300);
TestablePauseAnimation pause(&subgroup1);
pause.setDuration(200);
QSequentialAnimationGroup subgroup2(&group);
o.setProperty("ole2", 42);
QPropertyAnimation animation2(&o, "ole2", &subgroup2);
animation2.setEndValue(43);
animation2.setDuration(200);
TestablePauseAnimation pause2(&subgroup2);
pause2.setDuration(250);
QSequentialAnimationGroup subgroup3(&group);
TestablePauseAnimation pause3(&subgroup3);
pause3.setDuration(400);
o.setProperty("ole3", 42);
QPropertyAnimation animation3(&o, "ole3", &subgroup3);
animation3.setEndValue(43);
animation3.setDuration(200);
QSequentialAnimationGroup subgroup4(&group);
TestablePauseAnimation pause4(&subgroup4);
pause4.setDuration(310);
TestablePauseAnimation pause5(&subgroup4);
pause5.setDuration(60);
group.start();
QCOMPARE(group.state(), QAbstractAnimation::Running);
QCOMPARE(subgroup1.state(), QAbstractAnimation::Running);
QCOMPARE(subgroup2.state(), QAbstractAnimation::Running);
QCOMPARE(subgroup3.state(), QAbstractAnimation::Running);
QCOMPARE(subgroup4.state(), QAbstractAnimation::Running);
// This is a pretty long animation so it tends to get rather out of sync
// when using the consistent timer, so run for an extra half second for good
// measure...
const int expectedDuration = group.totalDuration() + 550;
WAIT_FOR_STOPPED(group, expectedDuration);
#ifdef BAD_TIMER_RESOLUTION
if (subgroup1.state() != QAbstractAnimation::Stopped)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(subgroup1.state(), QAbstractAnimation::Stopped);
#ifdef BAD_TIMER_RESOLUTION
if (subgroup2.state() != QAbstractAnimation::Stopped)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(subgroup2.state(), QAbstractAnimation::Stopped);
#ifdef BAD_TIMER_RESOLUTION
if (subgroup3.state() != QAbstractAnimation::Stopped)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(subgroup3.state(), QAbstractAnimation::Stopped);
#ifdef BAD_TIMER_RESOLUTION
if (subgroup4.state() != QAbstractAnimation::Stopped)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(subgroup4.state(), QAbstractAnimation::Stopped);
#ifdef BAD_TIMER_RESOLUTION
if (pause5.m_updateCurrentTimeCount != 4)
QEXPECT_FAIL("", timerError, Abort);
#endif
QCOMPARE(pause5.m_updateCurrentTimeCount, 4);
}
void tst_QPauseAnimation::zeroDuration()
{
TestablePauseAnimation animation;
animation.setDuration(0);
animation.start();
const int expectedDuration = animation.totalDuration() + 150;
WAIT_FOR_STOPPED(animation, expectedDuration);
QCOMPARE(animation.m_updateCurrentTimeCount, 1);
}
void tst_QPauseAnimation::bindings()
{
TestablePauseAnimation animation;
QProperty<int> duration;
animation.bindableDuration().setBinding(Qt::makePropertyBinding(duration));
duration = 42;
QCOMPARE(animation.duration(), 42);
// negative values must be ignored
QTest::ignoreMessage(QtWarningMsg,
"QPauseAnimation::setDuration: cannot set a negative duration");
duration = -1;
QCOMPARE(animation.duration(), 42);
QCOMPARE(duration, -1);
// Setting an invalid value shouldn't clear the binding
QTest::ignoreMessage(QtWarningMsg,
"QPauseAnimation::setDuration: cannot set a negative duration");
animation.setDuration(-1);
QVERIFY(animation.bindableDuration().hasBinding());
QCOMPARE(animation.duration(), 42);
QProperty<int> durationObserver;
durationObserver.setBinding(animation.bindableDuration().makeBinding());
animation.setDuration(46);
QCOMPARE(durationObserver, 46);
// Setting a valid value should clear the binding
QVERIFY(!animation.bindableDuration().hasBinding());
// Setting an invalid value also doesn't affect the observer
QTest::ignoreMessage(QtWarningMsg,
"QPauseAnimation::setDuration: cannot set a negative duration");
animation.setDuration(-1);
QCOMPARE(durationObserver, 46);
}
QTEST_MAIN(tst_QPauseAnimation)
#include "tst_qpauseanimation.moc"

View File

@ -0,0 +1,17 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qpropertyanimation Test:
#####################################################################
qt_internal_add_test(tst_qpropertyanimation
SOURCES
tst_qpropertyanimation.cpp
LIBRARIES
Qt::CorePrivate
Qt::Gui
Qt::Widgets
LIBRARIES
Qt::TestPrivate
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
[finishWithUncontrolledAnimation]
windows-10 msvc-2015
macos
[groupWithZeroDurationAnimations]
macos

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qsequentialanimationgroup Test:
#####################################################################
qt_internal_add_test(tst_qsequentialanimationgroup
SOURCES
tst_qsequentialanimationgroup.cpp
)

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qvariantanimation Test:
#####################################################################
qt_internal_add_test(tst_qvariantanimation
SOURCES
tst_qvariantanimation.cpp
LIBRARIES
Qt::TestPrivate
)

View File

@ -0,0 +1,163 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qvariantanimation.h>
#include <QTest>
#include <QtTest/private/qpropertytesthelper_p.h>
class tst_QVariantAnimation : public QObject
{
Q_OBJECT
private slots:
void construction();
void destruction();
void currentValue();
void easingCurve();
void startValue();
void endValue();
void keyValueAt();
void keyValues();
void duration();
void interpolation();
void durationBindings();
void easingCurveBindings();
};
class TestableQVariantAnimation : public QVariantAnimation
{
Q_OBJECT
public:
void updateCurrentValue(const QVariant&) override {}
};
void tst_QVariantAnimation::construction()
{
TestableQVariantAnimation anim;
}
void tst_QVariantAnimation::destruction()
{
TestableQVariantAnimation *anim = new TestableQVariantAnimation;
delete anim;
}
void tst_QVariantAnimation::currentValue()
{
TestableQVariantAnimation anim;
QVERIFY(!anim.currentValue().isValid());
}
void tst_QVariantAnimation::easingCurve()
{
TestableQVariantAnimation anim;
QCOMPARE(anim.easingCurve().type(), QEasingCurve::Linear);
anim.setEasingCurve(QEasingCurve::InQuad);
QCOMPARE(anim.easingCurve().type(), QEasingCurve::InQuad);
}
void tst_QVariantAnimation::endValue()
{
TestableQVariantAnimation anim;
anim.setEndValue(QVariant(1));
QCOMPARE(anim.endValue().toInt(), 1);
}
void tst_QVariantAnimation::startValue()
{
TestableQVariantAnimation anim;
anim.setStartValue(QVariant(1));
QCOMPARE(anim.startValue().toInt(), 1);
anim.setStartValue(QVariant(-1));
QCOMPARE(anim.startValue().toInt(), -1);
}
void tst_QVariantAnimation::keyValueAt()
{
TestableQVariantAnimation anim;
int i=0;
for (qreal r=0.0; r<1.0; r+=0.1) {
anim.setKeyValueAt(0.1, ++i);
QCOMPARE(anim.keyValueAt(0.1).toInt(), i);
}
}
void tst_QVariantAnimation::keyValues()
{
TestableQVariantAnimation anim;
QVariantAnimation::KeyValues values;
int i=0;
for (qreal r=0.0; r<1.0; r+=0.1) {
values.append(QVariantAnimation::KeyValue(r, i));
}
anim.setKeyValues(values);
QCOMPARE(anim.keyValues(), values);
}
void tst_QVariantAnimation::duration()
{
TestableQVariantAnimation anim;
QCOMPARE(anim.duration(), 250);
anim.setDuration(500);
QCOMPARE(anim.duration(), 500);
QTest::ignoreMessage(QtWarningMsg, "QVariantAnimation::setDuration: cannot set a negative duration");
anim.setDuration(-1);
QCOMPARE(anim.duration(), 500);
}
void tst_QVariantAnimation::interpolation()
{
QVariantAnimation unsignedAnim;
unsignedAnim.setStartValue(100u);
unsignedAnim.setEndValue(0u);
unsignedAnim.setDuration(100);
unsignedAnim.setCurrentTime(50);
QCOMPARE(unsignedAnim.currentValue().toUInt(), 50u);
QVariantAnimation signedAnim;
signedAnim.setStartValue(100);
signedAnim.setEndValue(0);
signedAnim.setDuration(100);
signedAnim.setCurrentTime(50);
QCOMPARE(signedAnim.currentValue().toInt(), 50);
QVariantAnimation pointAnim;
pointAnim.setStartValue(QPoint(100, 100));
pointAnim.setEndValue(QPoint(0, 0));
pointAnim.setDuration(100);
pointAnim.setCurrentTime(50);
QCOMPARE(pointAnim.currentValue().toPoint(), QPoint(50, 50));
}
void tst_QVariantAnimation::durationBindings()
{
QVariantAnimation animation;
// duration property
QProperty<int> duration;
animation.bindableDuration().setBinding(Qt::makePropertyBinding(duration));
// negative values must be ignored
QTest::ignoreMessage(QtWarningMsg,
"QVariantAnimation::setDuration: cannot set a negative duration");
duration = -1;
QVERIFY(animation.duration() != duration);
QTestPrivate::testReadWritePropertyBasics(animation, 42, 43, "duration");
}
void tst_QVariantAnimation::easingCurveBindings()
{
QVariantAnimation animation;
QTestPrivate::testReadWritePropertyBasics(animation, QEasingCurve(QEasingCurve::InQuad),
QEasingCurve(QEasingCurve::BezierSpline),
"easingCurve");
}
QTEST_MAIN(tst_QVariantAnimation)
#include "tst_qvariantanimation.moc"

View File

@ -0,0 +1,23 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
if(NOT INTEGRITY)
add_subdirectory(qcompare)
endif()
add_subdirectory(qflags)
add_subdirectory(q_func_info)
add_subdirectory(qgetputenv)
add_subdirectory(qglobal)
add_subdirectory(qnumeric)
add_subdirectory(qfloat16)
add_subdirectory(qkeycombination)
if(NOT INTEGRITY)
add_subdirectory(qnativeinterface)
endif()
add_subdirectory(qrandomgenerator)
add_subdirectory(qlogging)
add_subdirectory(qtendian)
add_subdirectory(qglobalstatic)
add_subdirectory(qhooks)
add_subdirectory(qoperatingsystemversion)
add_subdirectory(qxp)

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_q_func_info Test:
#####################################################################
qt_internal_add_test(tst_q_func_info
SOURCES
tst_q_func_info.cpp
)

View File

@ -0,0 +1,106 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QString>
#include <QTest>
#include <QtDebug>
class tst_q_func_info : public QObject
{
Q_OBJECT
private slots:
void callFunctions() const;
void isOfTypeConstChar() const;
void availableWithoutDebug() const;
private:
static void staticMember();
void regularMember() const;
void memberWithArguments(const QString &string, int value, const int value2) const;
};
static void staticRegularFunction()
{
qDebug() << Q_FUNC_INFO;
}
void regularFunction()
{
qDebug() << Q_FUNC_INFO;
}
template<typename T>
void templateFunction()
{
qDebug() << Q_FUNC_INFO;
}
template<typename T, const int value>
void valueTemplateFunction()
{
qDebug() << Q_FUNC_INFO;
}
void tst_q_func_info::staticMember()
{
qDebug() << Q_FUNC_INFO;
}
void tst_q_func_info::regularMember() const
{
qDebug() << Q_FUNC_INFO;
}
void tst_q_func_info::memberWithArguments(const QString &, int, const int) const
{
qDebug() << Q_FUNC_INFO;
}
/*! \internal
We don't do much here. We call different kinds of
functions to make sure we don't crash anything or that valgrind
is unhappy.
*/
void tst_q_func_info::callFunctions() const
{
staticRegularFunction();
regularFunction();
templateFunction<char>();
valueTemplateFunction<int, 3>();
staticMember();
regularMember();
memberWithArguments(QString(), 3, 4);
}
void tst_q_func_info::isOfTypeConstChar() const
{
#ifndef QT_NO_DEBUG
QString::fromLatin1(Q_FUNC_INFO);
#endif
}
/* \internal
Ensure that the macro is available even though QT_NO_DEBUG
is defined. If QT_NO_DEBUG is not defined, we define it
just for this function then undef it again afterwards.
*/
void tst_q_func_info::availableWithoutDebug() const
{
#ifndef QT_NO_DEBUG
# define UNDEF_NO_DEBUG
# define QT_NO_DEBUG
#endif
QVERIFY(!QString::fromLatin1(Q_FUNC_INFO).isEmpty());
#ifdef UNDEF_NO_DEBUG
# undef QT_NO_DEBUG
# undef UNDEF_NO_DEBUG
#endif
}
QTEST_MAIN(tst_q_func_info)
#include "tst_q_func_info.moc"

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qcompare Test:
#####################################################################
qt_internal_add_test(tst_qcompare
SOURCES
tst_qcompare.cpp
)
## Scopes:
#####################################################################

View File

@ -0,0 +1,98 @@
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/QtCompare>
#include <QtTest/QTest>
class tst_QCompare: public QObject
{
Q_OBJECT
private slots:
void partialOrdering();
};
void tst_QCompare::partialOrdering()
{
static_assert(QPartialOrdering::Unordered == QPartialOrdering::Unordered);
static_assert(QPartialOrdering::Unordered != QPartialOrdering::Less);
static_assert(QPartialOrdering::Unordered != QPartialOrdering::Equivalent);
static_assert(QPartialOrdering::Unordered != QPartialOrdering::Greater);
static_assert(QPartialOrdering::Less != QPartialOrdering::Unordered);
static_assert(QPartialOrdering::Less == QPartialOrdering::Less);
static_assert(QPartialOrdering::Less != QPartialOrdering::Equivalent);
static_assert(QPartialOrdering::Less != QPartialOrdering::Greater);
static_assert(QPartialOrdering::Equivalent != QPartialOrdering::Unordered);
static_assert(QPartialOrdering::Equivalent != QPartialOrdering::Less);
static_assert(QPartialOrdering::Equivalent == QPartialOrdering::Equivalent);
static_assert(QPartialOrdering::Equivalent != QPartialOrdering::Greater);
static_assert(QPartialOrdering::Greater != QPartialOrdering::Unordered);
static_assert(QPartialOrdering::Greater != QPartialOrdering::Less);
static_assert(QPartialOrdering::Greater != QPartialOrdering::Equivalent);
static_assert(QPartialOrdering::Greater == QPartialOrdering::Greater);
static_assert(!(QPartialOrdering::Unordered == 0));
static_assert(!(QPartialOrdering::Unordered != 0));
static_assert(!(QPartialOrdering::Unordered < 0));
static_assert(!(QPartialOrdering::Unordered <= 0));
static_assert(!(QPartialOrdering::Unordered > 0));
static_assert(!(QPartialOrdering::Unordered >= 0));
static_assert(!(0 == QPartialOrdering::Unordered));
static_assert(!(0 != QPartialOrdering::Unordered));
static_assert(!(0 < QPartialOrdering::Unordered));
static_assert(!(0 <= QPartialOrdering::Unordered));
static_assert(!(0 > QPartialOrdering::Unordered));
static_assert(!(0 >= QPartialOrdering::Unordered));
static_assert(!(QPartialOrdering::Less == 0));
static_assert( (QPartialOrdering::Less != 0));
static_assert( (QPartialOrdering::Less < 0));
static_assert( (QPartialOrdering::Less <= 0));
static_assert(!(QPartialOrdering::Less > 0));
static_assert(!(QPartialOrdering::Less >= 0));
static_assert(!(0 == QPartialOrdering::Less));
static_assert( (0 != QPartialOrdering::Less));
static_assert(!(0 < QPartialOrdering::Less));
static_assert(!(0 <= QPartialOrdering::Less));
static_assert( (0 > QPartialOrdering::Less));
static_assert( (0 >= QPartialOrdering::Less));
static_assert( (QPartialOrdering::Equivalent == 0));
static_assert(!(QPartialOrdering::Equivalent != 0));
static_assert(!(QPartialOrdering::Equivalent < 0));
static_assert( (QPartialOrdering::Equivalent <= 0));
static_assert(!(QPartialOrdering::Equivalent > 0));
static_assert( (QPartialOrdering::Equivalent >= 0));
static_assert( (0 == QPartialOrdering::Equivalent));
static_assert(!(0 != QPartialOrdering::Equivalent));
static_assert(!(0 < QPartialOrdering::Equivalent));
static_assert( (0 <= QPartialOrdering::Equivalent));
static_assert(!(0 > QPartialOrdering::Equivalent));
static_assert( (0 >= QPartialOrdering::Equivalent));
static_assert(!(QPartialOrdering::Greater == 0));
static_assert( (QPartialOrdering::Greater != 0));
static_assert(!(QPartialOrdering::Greater < 0));
static_assert(!(QPartialOrdering::Greater <= 0));
static_assert( (QPartialOrdering::Greater > 0));
static_assert( (QPartialOrdering::Greater >= 0));
static_assert(!(0 == QPartialOrdering::Greater));
static_assert( (0 != QPartialOrdering::Greater));
static_assert( (0 < QPartialOrdering::Greater));
static_assert( (0 <= QPartialOrdering::Greater));
static_assert(!(0 > QPartialOrdering::Greater));
static_assert(!(0 >= QPartialOrdering::Greater));
}
QTEST_MAIN(tst_QCompare)
#include "tst_qcompare.moc"

View File

@ -0,0 +1,18 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qflags Test:
#####################################################################
qt_internal_add_test(tst_qflags
SOURCES
tst_qflags.cpp
)
qt_internal_add_test(tst_qflags_non_typesafe
SOURCES
tst_qflags.cpp
DEFINES
QFLAGS_TEST_NO_TYPESAFE_FLAGS
)

View File

@ -0,0 +1,481 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifdef QFLAGS_TEST_NO_TYPESAFE_FLAGS
# ifdef QT_TYPESAFE_FLAGS
# undef QT_TYPESAFE_FLAGS
# endif
#else
# ifndef QT_TYPESAFE_FLAGS
# define QT_TYPESAFE_FLAGS
# endif
#endif
#include <QTest>
class tst_QFlags: public QObject
{
Q_OBJECT
private slots:
void boolCasts() const;
void operators() const;
void mixingDifferentEnums() const;
void testFlag() const;
void testFlagZeroFlag() const;
void testFlagMultiBits() const;
void testFlags();
void testAnyFlag();
void constExpr();
void signedness();
void classEnum();
void initializerLists();
void testSetFlags();
void adl();
};
void tst_QFlags::boolCasts() const
{
// This tests that the operator overloading is sufficient so that common
// idioms involving flags -> bool casts work as expected:
const Qt::Alignment nonNull = Qt::AlignCenter;
const Qt::Alignment null = {};
// basic premiss:
QVERIFY(bool(nonNull));
QVERIFY(!bool(null));
// The rest is just checking that stuff compiles:
// QVERIFY should compile:
QVERIFY(nonNull);
QVERIFY(!null);
// ifs should compile:
if (null) QFAIL("Can't contextually convert QFlags to bool!");
if (!nonNull) QFAIL("Missing operator! on QFlags (shouldn't be necessary).");
// ternary should compile:
QVERIFY(nonNull ? true : false);
QVERIFY(!null ? true : false);
// logical operators should compile:
QVERIFY(nonNull && true);
QVERIFY(nonNull || false);
QVERIFY(!null && true);
QVERIFY(!null || false);
// ... in both directions:
QVERIFY(true && nonNull);
QVERIFY(false || nonNull);
QVERIFY(true && !null);
QVERIFY(false || !null);
// ... and mixed:
QVERIFY(null || nonNull);
QVERIFY(!(null && nonNull));
}
void tst_QFlags::operators() const
{
#define CHECK(op, LHS, RHS, RES) \
do { \
QCOMPARE((LHS op RHS), (RES)); \
QCOMPARE(( /*CTAD*/ QFlags(LHS) op RHS), (RES)); \
QCOMPARE((LHS op QFlags(RHS)), (RES)); \
QCOMPARE((QFlags(LHS) op QFlags(RHS)), (RES)); \
QCOMPARE((QFlags(LHS) op ## = RHS), (RES)); \
QCOMPARE((QFlags(LHS) op ## = QFlags(RHS)), (RES)); \
} while (false)
CHECK(|, Qt::AlignHCenter, Qt::AlignVCenter, Qt::AlignCenter);
CHECK(|, Qt::AlignHCenter, Qt::AlignHCenter, Qt::AlignHCenter);
CHECK(&, Qt::AlignHCenter, Qt::AlignVCenter, Qt::Alignment());
CHECK(&, Qt::AlignHCenter, Qt::AlignHCenter, Qt::AlignHCenter);
CHECK(^, Qt::AlignHCenter, Qt::AlignVCenter, Qt::AlignCenter);
CHECK(^, Qt::AlignHCenter, Qt::AlignHCenter, Qt::Alignment());
#undef CHECK
}
void tst_QFlags::mixingDifferentEnums() const
{
#define CHECK(op, LHS, RHS, RES) \
/* LHS must be QFlags'able */ \
do { \
QCOMPARE((LHS op RHS), (RES)); \
QCOMPARE((RHS op LHS), (RES)); \
/*QCOMPARE(( / *CTAD* / QFlags(LHS) op RHS), (RES));*/ \
/*QCOMPARE((QFlags(LHS) op ## = RHS), (RES));*/ \
} while (false)
// AlignmentFlags <-> TextFlags
{
CHECK(|, Qt::AlignCenter, Qt::TextSingleLine, 0x0184);
CHECK(&, Qt::AlignCenter, Qt::TextSingleLine, 0x0000);
CHECK(^, Qt::AlignCenter, Qt::TextSingleLine, 0x0184);
}
// QFlags<AlignmentFlags> <-> TextFlags
{
#ifndef QT_TYPESAFE_FLAGS // QTBUG-101344
Qt::Alignment MyAlignCenter = Qt::AlignCenter; // convert enum to QFlags
CHECK(|, MyAlignCenter, Qt::TextSingleLine, 0x0184U); // yes, unsigned!
CHECK(&, MyAlignCenter, Qt::TextSingleLine, 0x0000U); // yes, unsigned!
CHECK(^, MyAlignCenter, Qt::TextSingleLine, 0x0184U); // yes, unsigned!
#endif
}
// TextElideMode <-> TextFlags
{
CHECK(|, Qt::ElideNone, Qt::TextSingleLine, 0x0103);
CHECK(&, Qt::ElideNone, Qt::TextSingleLine, 0x0000);
CHECK(^, Qt::ElideNone, Qt::TextSingleLine, 0x0103);
}
#undef CHECK
}
void tst_QFlags::testFlag() const
{
Qt::MouseButtons btn = Qt::LeftButton | Qt::RightButton;
QVERIFY(btn.testFlag(Qt::LeftButton));
QVERIFY(!btn.testFlag(Qt::MiddleButton));
btn = { };
QVERIFY(!btn.testFlag(Qt::LeftButton));
}
void tst_QFlags::testFlagZeroFlag() const
{
{
Qt::MouseButtons btn = Qt::LeftButton | Qt::RightButton;
/* Qt::NoButton has the value 0. */
QVERIFY(!btn.testFlag(Qt::NoButton));
}
{
/* A zero enum set should test true with zero. */
QVERIFY(Qt::MouseButtons().testFlag(Qt::NoButton));
}
{
Qt::MouseButtons btn = Qt::NoButton;
QVERIFY(btn.testFlag(Qt::NoButton));
}
}
void tst_QFlags::testFlagMultiBits() const
{
/* Qt::Window is 0x00000001
* Qt::Dialog is 0x00000002 | Window
*/
{
const Qt::WindowFlags onlyWindow(Qt::Window);
QVERIFY(!onlyWindow.testFlag(Qt::Dialog));
}
{
const Qt::WindowFlags hasDialog(Qt::Dialog);
QVERIFY(hasDialog.testFlag(Qt::Dialog));
}
}
void tst_QFlags::testFlags()
{
using Int = Qt::TextInteractionFlags::Int;
constexpr Int Zero(0);
Qt::TextInteractionFlags flags;
QCOMPARE(flags.toInt(), Zero);
QVERIFY(flags.testFlags(flags));
QVERIFY(Qt::TextInteractionFlags::fromInt(Zero).testFlags(flags));
QVERIFY(!flags.testFlags(Qt::TextSelectableByMouse));
QVERIFY(!flags.testFlags(Qt::TextSelectableByKeyboard));
QVERIFY(!flags.testFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard));
QVERIFY(flags.testFlags(Qt::TextInteractionFlags::fromInt(Zero)));
QVERIFY(flags.testFlags(Qt::TextInteractionFlags(Qt::TextSelectableByMouse) & ~Qt::TextSelectableByMouse));
flags = Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard;
QVERIFY(flags.toInt() != Zero);
QVERIFY(flags.testFlags(flags));
QVERIFY(flags.testFlags(Qt::TextSelectableByMouse));
QVERIFY(flags.testFlags(Qt::TextSelectableByKeyboard));
QVERIFY(flags.testFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse));
QVERIFY(!flags.testFlags(Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse | Qt::TextEditable));
QVERIFY(!flags.testFlags(Qt::TextInteractionFlags()));
QVERIFY(!flags.testFlags(Qt::TextInteractionFlags::fromInt(Zero)));
QVERIFY(!flags.testFlags(Qt::TextEditable));
QVERIFY(!flags.testFlags(Qt::TextSelectableByMouse | Qt::TextEditable));
}
void tst_QFlags::testAnyFlag()
{
Qt::TextInteractionFlags flags;
QVERIFY(!flags.testAnyFlags(Qt::NoTextInteraction));
QVERIFY(!flags.testAnyFlags(Qt::TextSelectableByMouse));
QVERIFY(!flags.testAnyFlags(Qt::TextSelectableByKeyboard));
QVERIFY(!flags.testAnyFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard));
QVERIFY(!flags.testAnyFlag(Qt::TextEditorInteraction));
QVERIFY(!flags.testAnyFlag(Qt::TextBrowserInteraction));
flags = Qt::TextSelectableByMouse;
QVERIFY(!flags.testAnyFlags(Qt::NoTextInteraction));
QVERIFY(flags.testAnyFlags(Qt::TextSelectableByMouse));
QVERIFY(!flags.testAnyFlags(Qt::TextSelectableByKeyboard));
QVERIFY(flags.testAnyFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard));
QVERIFY(flags.testAnyFlag(Qt::TextEditorInteraction));
QVERIFY(flags.testAnyFlag(Qt::TextBrowserInteraction));
flags = Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard;
QVERIFY(!flags.testAnyFlags(Qt::NoTextInteraction));
QVERIFY(flags.testAnyFlags(Qt::TextSelectableByMouse));
QVERIFY(flags.testAnyFlags(Qt::TextSelectableByKeyboard));
QVERIFY(flags.testAnyFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard));
QVERIFY(flags.testAnyFlag(Qt::TextEditorInteraction));
QVERIFY(flags.testAnyFlag(Qt::TextEditorInteraction));
QVERIFY(flags.testAnyFlag(Qt::TextBrowserInteraction));
}
template <unsigned int N, typename T> bool verifyConstExpr(T n) { return n == N; }
template <unsigned int N, typename T> bool verifyConstExpr(QFlags<T> n) { return n.toInt() == N; }
constexpr Qt::MouseButtons testRelaxedConstExpr()
{
Qt::MouseButtons value;
value = Qt::LeftButton | Qt::RightButton;
value |= Qt::MiddleButton;
value &= ~Qt::LeftButton;
value ^= Qt::RightButton;
return value;
}
void tst_QFlags::constExpr()
{
Qt::MouseButtons btn = Qt::LeftButton | Qt::RightButton;
switch (btn.toInt()) {
case Qt::LeftButton: QVERIFY(false); break;
case Qt::RightButton: QVERIFY(false); break;
case (Qt::LeftButton | Qt::RightButton).toInt(): QVERIFY(true); break;
default: QFAIL(qPrintable(QStringLiteral("Unexpected button: %1").arg(btn.toInt())));
}
#define VERIFY_CONSTEXPR(expression, expected) \
QVERIFY(verifyConstExpr<(expression).toInt()>(expected))
VERIFY_CONSTEXPR((Qt::LeftButton | Qt::RightButton) & Qt::LeftButton, Qt::LeftButton);
VERIFY_CONSTEXPR((Qt::LeftButton | Qt::RightButton) & Qt::MiddleButton, 0);
VERIFY_CONSTEXPR((Qt::LeftButton | Qt::RightButton) | Qt::MiddleButton, Qt::LeftButton | Qt::RightButton | Qt::MiddleButton);
VERIFY_CONSTEXPR(~(Qt::LeftButton | Qt::RightButton), ~(Qt::LeftButton | Qt::RightButton));
VERIFY_CONSTEXPR(Qt::MouseButtons(Qt::LeftButton) ^ Qt::RightButton, Qt::LeftButton ^ Qt::RightButton);
VERIFY_CONSTEXPR(Qt::MouseButtons(0), 0);
#ifndef QT_TYPESAFE_FLAGS
QVERIFY(verifyConstExpr<(Qt::MouseButtons(Qt::RightButton) & 0xff)>(Qt::RightButton));
QVERIFY(verifyConstExpr<(Qt::MouseButtons(Qt::RightButton) | 0xff)>(0xff));
#endif
QVERIFY(!verifyConstExpr<Qt::RightButton>(~Qt::MouseButtons(Qt::LeftButton)));
VERIFY_CONSTEXPR(testRelaxedConstExpr(), Qt::MiddleButton);
#undef VERIFY_CONSTEXPR
}
void tst_QFlags::signedness()
{
// these are all 'true' on GCC, but since the std says the
// underlying type is implementation-defined, we need to allow for
// a different signedness, so we only check that the relative
// signedness of the types matches:
static_assert((std::is_unsigned<typename std::underlying_type<Qt::MouseButton>::type>::value ==
std::is_unsigned<Qt::MouseButtons::Int>::value));
static_assert((std::is_signed<typename std::underlying_type<Qt::AlignmentFlag>::type>::value ==
std::is_signed<Qt::Alignment::Int>::value));
}
enum class MyStrictEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 };
Q_DECLARE_FLAGS( MyStrictFlags, MyStrictEnum )
Q_DECLARE_OPERATORS_FOR_FLAGS( MyStrictFlags )
enum class MyStrictNoOpEnum { StrictZero, StrictOne, StrictTwo, StrictFour=4 };
Q_DECLARE_FLAGS( MyStrictNoOpFlags, MyStrictNoOpEnum )
static_assert( !QTypeInfo<MyStrictFlags>::isComplex );
static_assert( QTypeInfo<MyStrictFlags>::isRelocatable );
static_assert( !QTypeInfo<MyStrictFlags>::isPointer );
void tst_QFlags::classEnum()
{
// The main aim of the test is making sure it compiles
// The QCOMPARE are there as an extra
MyStrictEnum e1 = MyStrictEnum::StrictOne;
MyStrictEnum e2 = MyStrictEnum::StrictTwo;
MyStrictFlags f1(MyStrictEnum::StrictOne);
QCOMPARE(f1, 1);
MyStrictFlags f2(e2);
QCOMPARE(f2, 2);
MyStrictFlags f0;
QCOMPARE(f0, 0);
MyStrictFlags f3(e2 | e1);
QCOMPARE(f3, 3);
QVERIFY(f3.testFlag(MyStrictEnum::StrictOne));
QVERIFY(!f1.testFlag(MyStrictEnum::StrictTwo));
QVERIFY(!f0);
#ifndef QT_TYPESAFE_FLAGS
QCOMPARE(f3 & int(1), 1);
QCOMPARE(f3 & uint(1), 1);
#endif
QCOMPARE(f3 & MyStrictEnum::StrictOne, 1);
MyStrictFlags aux;
#ifndef QT_TYPESAFE_FLAGS
aux = f3;
aux &= int(1);
QCOMPARE(aux, 1);
aux = f3;
aux &= uint(1);
QCOMPARE(aux, 1);
#endif
aux = f3;
aux &= MyStrictEnum::StrictOne;
QCOMPARE(aux, 1);
aux = f3;
aux &= f1;
QCOMPARE(aux, 1);
aux = f3 ^ f3;
QCOMPARE(aux, 0);
aux = f3 ^ f1;
QCOMPARE(aux, 2);
aux = f3 ^ f0;
QCOMPARE(aux, 3);
aux = f3 ^ MyStrictEnum::StrictOne;
QCOMPARE(aux, 2);
aux = f3 ^ MyStrictEnum::StrictZero;
QCOMPARE(aux, 3);
aux = f3;
aux ^= f3;
QCOMPARE(aux, 0);
aux = f3;
aux ^= f1;
QCOMPARE(aux, 2);
aux = f3;
aux ^= f0;
QCOMPARE(aux, 3);
aux = f3;
aux ^= MyStrictEnum::StrictOne;
QCOMPARE(aux, 2);
aux = f3;
aux ^= MyStrictEnum::StrictZero;
QCOMPARE(aux, 3);
aux = f1 | f2;
QCOMPARE(aux, 3);
aux = MyStrictEnum::StrictOne | MyStrictEnum::StrictTwo;
QCOMPARE(aux, 3);
aux = f1;
aux |= f2;
QCOMPARE(aux, 3);
aux = MyStrictEnum::StrictOne;
aux |= MyStrictEnum::StrictTwo;
QCOMPARE(aux, 3);
aux = ~f1;
QCOMPARE(aux, -2);
// Just to make sure it compiles
if (false)
qDebug() << f3;
}
void tst_QFlags::initializerLists()
{
Qt::MouseButtons bts = { Qt::LeftButton, Qt::RightButton };
QVERIFY(bts.testFlag(Qt::LeftButton));
QVERIFY(bts.testFlag(Qt::RightButton));
QVERIFY(!bts.testFlag(Qt::MiddleButton));
MyStrictNoOpFlags flags = { MyStrictNoOpEnum::StrictOne, MyStrictNoOpEnum::StrictFour };
QVERIFY(flags.testFlag(MyStrictNoOpEnum::StrictOne));
QVERIFY(flags.testFlag(MyStrictNoOpEnum::StrictFour));
QVERIFY(!flags.testFlag(MyStrictNoOpEnum::StrictTwo));
}
void tst_QFlags::testSetFlags()
{
Qt::MouseButtons btn = Qt::NoButton;
btn.setFlag(Qt::LeftButton);
QVERIFY(btn.testFlag(Qt::LeftButton));
QVERIFY(!btn.testFlag(Qt::MiddleButton));
btn.setFlag(Qt::LeftButton, false);
QVERIFY(!btn.testFlag(Qt::LeftButton));
QVERIFY(!btn.testFlag(Qt::MiddleButton));
MyStrictFlags flags;
flags.setFlag(MyStrictEnum::StrictOne);
flags.setFlag(MyStrictEnum::StrictTwo, true);
QVERIFY(flags.testFlag(MyStrictEnum::StrictOne));
QVERIFY(flags.testFlag(MyStrictEnum::StrictTwo));
QVERIFY(!flags.testFlag(MyStrictEnum::StrictFour));
flags.setFlag(MyStrictEnum::StrictTwo, false);
QVERIFY(flags.testFlag(MyStrictEnum::StrictOne));
QVERIFY(!flags.testFlag(MyStrictEnum::StrictTwo));
QVERIFY(!flags.testFlag(MyStrictEnum::StrictFour));
}
namespace SomeNS {
enum Foo { Foo_A = 1 << 0, Foo_B = 1 << 1, Foo_C = 1 << 2 };
Q_DECLARE_FLAGS(Foos, Foo)
Q_DECLARE_OPERATORS_FOR_FLAGS(Foos);
Qt::Alignment alignment()
{
// Checks that the operator| works, despite there is another operator| in this namespace.
return Qt::AlignLeft | Qt::AlignTop;
}
}
void tst_QFlags::adl()
{
SomeNS::Foos fl = SomeNS::Foo_B | SomeNS::Foo_C;
QVERIFY(fl & SomeNS::Foo_B);
QVERIFY(!(fl & SomeNS::Foo_A));
QCOMPARE(SomeNS::alignment(), Qt::AlignLeft | Qt::AlignTop);
}
// (statically) check QTypeInfo for QFlags instantiations:
enum MyEnum { Zero, One, Two, Four=4 };
Q_DECLARE_FLAGS( MyFlags, MyEnum )
Q_DECLARE_OPERATORS_FOR_FLAGS( MyFlags )
static_assert( !QTypeInfo<MyFlags>::isComplex );
static_assert( QTypeInfo<MyFlags>::isRelocatable );
static_assert( !QTypeInfo<MyFlags>::isPointer );
QTEST_MAIN(tst_QFlags)
#include "tst_qflags.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qfloat16 Test:
#####################################################################
qt_internal_add_test(tst_qfloat16
SOURCES
tst_qfloat16.cpp
LIBRARIES
Qt::TestPrivate
)

View File

@ -0,0 +1,694 @@
// Copyright (C) 2021 The Qt Company Ltd.
// Copyright (C) 2016 by Southwest Research Institute (R)
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QFloat16>
#include <QMetaType>
#include <QTextStream>
#include <math.h>
//#define DO_FULL_TEST
static_assert(sizeof(float) == sizeof(quint32), "Float not 32-bit");
class tst_qfloat16: public QObject
{
Q_OBJECT
private slots:
void fuzzyCompare_data();
void fuzzyCompare();
void fuzzyIsNull_data();
void fuzzyIsNull();
void ltgt_data();
void ltgt();
void qNaN();
void infinity();
void float_cast();
void float_cast_data();
void promotionTests();
void arithOps_data();
void arithOps();
#if defined DO_FULL_TEST
void floatToFloat16Full_data();
void floatToFloat16Full();
void floatFromFloat16Full();
#endif
void floatToFloat16();
void floatFromFloat16();
void finite_data();
void finite();
void properties();
void limits();
void mantissaOverflow();
void dataStream();
void textStream();
};
void tst_qfloat16::fuzzyCompare_data()
{
QTest::addColumn<qfloat16>("val1");
QTest::addColumn<qfloat16>("val2");
QTest::addColumn<bool>("fuzEqual");
QTest::addColumn<bool>("isEqual");
QTest::newRow("zero") << qfloat16(0.0f) << qfloat16(0.0f) << true << true;
QTest::newRow("ten") << qfloat16(1e1f) << qfloat16(1e1f) << true << true;
QTest::newRow("large") << qfloat16(1e4f) << qfloat16(1e4f) << true << true;
QTest::newRow("small") << qfloat16(1e-5f) << qfloat16(1e-5f) << true << true;
QTest::newRow("eps") << qfloat16(10.01f) << qfloat16(10.02f) << true << false;
QTest::newRow("eps2") << qfloat16(1024.f) << qfloat16(1033.f) << true << false;
QTest::newRow("mis1") << qfloat16(0.0f) << qfloat16(1.0f) << false << false;
QTest::newRow("mis2") << qfloat16(0.0f) << qfloat16(1e7f) << false << false;
QTest::newRow("mis3") << qfloat16(0.0f) << qfloat16(1e-4f) << false << false;
QTest::newRow("mis4") << qfloat16(1e8f) << qfloat16(1e-8f) << false << false;
QTest::newRow("mis5") << qfloat16(1e-4f) << qfloat16(1e-5) << false << false;
QTest::newRow("mis6") << qfloat16(1024.f) << qfloat16(1034.f) << false << false;
}
void tst_qfloat16::fuzzyCompare()
{
QFETCH(qfloat16, val1);
QFETCH(qfloat16, val2);
QFETCH(bool, fuzEqual);
QFETCH(bool, isEqual);
if (!isEqual && (val1==val2))
qWarning() << "Identical arguments provided unintentionally!";
if (fuzEqual) {
QVERIFY(::qFuzzyCompare(val1, val2));
QVERIFY(::qFuzzyCompare(val2, val1));
QVERIFY(::qFuzzyCompare(-val1, -val2));
QVERIFY(::qFuzzyCompare(-val2, -val1));
} else {
QVERIFY(!::qFuzzyCompare(val1, val2));
QVERIFY(!::qFuzzyCompare(val2, val1));
QVERIFY(!::qFuzzyCompare(-val1, -val2));
QVERIFY(!::qFuzzyCompare(-val2, -val1));
}
}
void tst_qfloat16::fuzzyIsNull_data()
{
QTest::addColumn<qfloat16>("value");
QTest::addColumn<bool>("isNull");
using Bounds = std::numeric_limits<qfloat16>;
const qfloat16 one(1), huge(1000), tiny(0.00976f);
QTest::newRow("zero") << qfloat16(0.0f) << true;
QTest::newRow("min") << Bounds::min() << true;
QTest::newRow("denorm_min") << Bounds::denorm_min() << true;
QTest::newRow("tiny") << tiny << true;
QTest::newRow("deci") << qfloat16(.1) << false;
QTest::newRow("one") << one << false;
QTest::newRow("ten") << qfloat16(10) << false;
QTest::newRow("huge") << huge << false;
}
void tst_qfloat16::fuzzyIsNull()
{
QFETCH(qfloat16, value);
QFETCH(bool, isNull);
QCOMPARE(::qFuzzyIsNull(value), isNull);
QCOMPARE(::qFuzzyIsNull(-value), isNull);
}
void tst_qfloat16::ltgt_data()
{
QTest::addColumn<float>("val1");
QTest::addColumn<float>("val2");
QTest::newRow("zero") << 0.0f << 0.0f;
QTest::newRow("-zero") << -0.0f << 0.0f;
QTest::newRow("ten") << 10.0f << 10.0f;
QTest::newRow("large") << 100000.0f << 100000.0f;
QTest::newRow("small") << 0.0000001f << 0.0000001f;
QTest::newRow("eps") << 10.000000000000001f << 10.00000000000002f;
QTest::newRow("eps2") << 10.000000000000001f << 10.000000000000009f;
QTest::newRow("mis1") << 0.0f << 1.0f;
QTest::newRow("mis2") << 0.0f << 10000000.0f;
QTest::newRow("mis3") << 0.0f << 0.0001f;
QTest::newRow("mis4") << 100000000.0f << 0.000000001f;
QTest::newRow("mis5") << 0.0001f << 0.00001f;
QTest::newRow("45,23") << 45.f << 23.f;
QTest::newRow("1000,76") << 1000.f << 76.f;
}
void tst_qfloat16::ltgt()
{
QFETCH(float, val1);
QFETCH(float, val2);
QCOMPARE(qfloat16(val1) == qfloat16(val2), val1 == val2);
QCOMPARE(qfloat16(val1) < qfloat16(val2), val1 < val2);
QCOMPARE(qfloat16(val1) <= qfloat16(val2), val1 <= val2);
QCOMPARE(qfloat16(val1) > qfloat16(val2), val1 > val2);
QCOMPARE(qfloat16(val1) >= qfloat16(val2), val1 >= val2);
QCOMPARE(qfloat16(val1) == qfloat16(-val2), val1 == -val2);
QCOMPARE(qfloat16(val1) < qfloat16(-val2), val1 < -val2);
QCOMPARE(qfloat16(val1) <= qfloat16(-val2), val1 <= -val2);
QCOMPARE(qfloat16(val1) > qfloat16(-val2), val1 > -val2);
QCOMPARE(qfloat16(val1) >= qfloat16(-val2), val1 >= -val2);
QCOMPARE(qfloat16(-val1) == qfloat16(val2), -val1 == val2);
QCOMPARE(qfloat16(-val1) < qfloat16(val2), -val1 < val2);
QCOMPARE(qfloat16(-val1) <= qfloat16(val2), -val1 <= val2);
QCOMPARE(qfloat16(-val1) > qfloat16(val2), -val1 > val2);
QCOMPARE(qfloat16(-val1) >= qfloat16(val2), -val1 >= val2);
QCOMPARE(qfloat16(-val1) == qfloat16(-val2), -val1 == -val2);
QCOMPARE(qfloat16(-val1) < qfloat16(-val2), -val1 < -val2);
QCOMPARE(qfloat16(-val1) <= qfloat16(-val2), -val1 <= -val2);
QCOMPARE(qfloat16(-val1) > qfloat16(-val2), -val1 > -val2);
QCOMPARE(qfloat16(-val1) >= qfloat16(-val2), -val1 >= -val2);
}
#if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)
// turn -ffast-math off
# pragma GCC optimize "no-fast-math"
#endif
void tst_qfloat16::qNaN()
{
#if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ < 404)
QSKIP("Non-conformant fast math mode is enabled, cannot run test");
#endif
using Bounds = std::numeric_limits<qfloat16>;
const qfloat16 nan = Bounds::quiet_NaN();
const qfloat16 zero(0), one(1);
QVERIFY(!(zero > nan));
QVERIFY(!(zero < nan));
QVERIFY(!(zero == nan));
QVERIFY(!qIsInf(nan));
QVERIFY(qIsNaN(nan));
QVERIFY(qIsNaN(nan + one));
QVERIFY(qIsNaN(-nan));
QVERIFY(qIsNaN(nan * zero));
QVERIFY(qIsNaN(Bounds::infinity() * zero));
QVERIFY(!nan.isNormal());
QVERIFY(!qIsFinite(nan));
QVERIFY(!(nan == nan));
QCOMPARE(nan, nan); // Despite the preceding
QCOMPARE(qFpClassify(nan), FP_NAN);
}
void tst_qfloat16::infinity()
{
const qfloat16 huge = std::numeric_limits<qfloat16>::infinity();
const qfloat16 zero(0), one(1), two(2);
QVERIFY(huge > -huge);
QVERIFY(huge > zero);
QVERIFY(-huge < zero);
QCOMPARE(huge, huge);
QCOMPARE(-huge, -huge);
QCOMPARE(one / huge, zero);
QVERIFY(qFuzzyCompare(one / huge, zero)); // (same thing)
QVERIFY(qIsInf(huge));
QVERIFY(qIsInf(-huge));
QVERIFY(qIsInf(two * huge));
QVERIFY(qIsInf(huge * two));
QVERIFY(!huge.isNormal());
QVERIFY(!(-huge).isNormal());
QVERIFY(!qIsNaN(huge));
QVERIFY(!qIsNaN(-huge));
QVERIFY(!qIsFinite(huge));
QVERIFY(!qIsFinite(-huge));
QCOMPARE(qFpClassify(huge), FP_INFINITE);
QCOMPARE(qFpClassify(-huge), FP_INFINITE);
}
void tst_qfloat16::float_cast_data()
{
QTest::addColumn<float>("val");
QTest::newRow("zero") << 0.f;
QTest::newRow("one") << 1e0f;
QTest::newRow("ten") << 1e1f;
QTest::newRow("hund") << 1e2f;
QTest::newRow("thou") << 1e3f;
QTest::newRow("tthou") << 1e4f;
//QTest::newRow("hthou") << 1e5f;
//QTest::newRow("mil") << 1e6f;
//QTest::newRow("tmil") << 1e7f;
//QTest::newRow("hmil") << 1e8f;
}
void tst_qfloat16::float_cast()
{
QFETCH(float, val);
QVERIFY(qFuzzyCompare((float)(qfloat16(val)),val));
QVERIFY(qFuzzyCompare((float)(qfloat16(-val)),-val));
QVERIFY(qFuzzyCompare((double)(qfloat16(val)),(double)(val)));
QVERIFY(qFuzzyCompare((double)(qfloat16(-val)),(double)(-val)));
//QVERIFY(qFuzzyCompare((long double)(qfloat16(val)),(long double)(val)));
//QVERIFY(qFuzzyCompare((long double)(qfloat16(-val)),(long double)(-val)));
}
void tst_qfloat16::promotionTests()
{
QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)+qfloat16(1.f)));
QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)-qfloat16(1.f)));
QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)*qfloat16(1.f)));
QCOMPARE(sizeof(qfloat16),sizeof(qfloat16(1.f)/qfloat16(1.f)));
QCOMPARE(sizeof(float),sizeof(1.f+qfloat16(1.f)));
QCOMPARE(sizeof(float),sizeof(1.f-qfloat16(1.f)));
QCOMPARE(sizeof(float),sizeof(1.f*qfloat16(1.f)));
QCOMPARE(sizeof(float),sizeof(1.f/qfloat16(1.f)));
QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)+1.f));
QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)-1.f));
QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)*1.f));
QCOMPARE(sizeof(float),sizeof(qfloat16(1.f)/1.f));
QCOMPARE(sizeof(double),sizeof(1.+qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(1.-qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(1.*qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(1./qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)+1.));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)-1.));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)*1.));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)/1.));
QCOMPARE(sizeof(long double),sizeof((long double)(1.)+qfloat16(1.f)));
QCOMPARE(sizeof(long double),sizeof((long double)(1.)-qfloat16(1.f)));
QCOMPARE(sizeof(long double),sizeof((long double)(1.)*qfloat16(1.f)));
QCOMPARE(sizeof(long double),sizeof((long double)(1.)/qfloat16(1.f)));
QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)+(long double)(1.)));
QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)-(long double)(1.)));
QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)*(long double)(1.)));
QCOMPARE(sizeof(long double),sizeof(qfloat16(1.f)/(long double)(1.)));
QCOMPARE(sizeof(double),sizeof(1+qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(1-qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(1*qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(1/qfloat16(1.f)));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)+1));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)-1));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)*1));
QCOMPARE(sizeof(double),sizeof(qfloat16(1.f)/1));
QCOMPARE(QString::number(1.f),QString::number(double(qfloat16(1.f))));
}
void tst_qfloat16::arithOps_data()
{
QTest::addColumn<float>("val1");
QTest::addColumn<float>("val2");
QTest::newRow("zero") << 0.0f << 2.0f;
QTest::newRow("one") << 1.0f << 4.0f;
QTest::newRow("ten") << 10.0f << 20.0f;
}
void tst_qfloat16::arithOps()
{
QFETCH(float, val1);
QFETCH(float, val2);
QVERIFY(qFuzzyCompare(float(qfloat16(val1) + qfloat16(val2)), val1 + val2));
QVERIFY(qFuzzyCompare(float(qfloat16(val1) - qfloat16(val2)), val1 - val2));
QVERIFY(qFuzzyCompare(float(qfloat16(val1) * qfloat16(val2)), val1 * val2));
QVERIFY(qFuzzyCompare(float(qfloat16(val1) / qfloat16(val2)), val1 / val2));
QVERIFY(qFuzzyCompare(qfloat16(val1) + val2, val1 + val2));
QVERIFY(qFuzzyCompare(qfloat16(val1) - val2, val1 - val2));
QVERIFY(qFuzzyCompare(qfloat16(val1) * val2, val1 * val2));
QVERIFY(qFuzzyCompare(qfloat16(val1) / val2, val1 / val2));
QVERIFY(qFuzzyCompare(val1 + qfloat16(val2), val1 + val2));
QVERIFY(qFuzzyCompare(val1 - qfloat16(val2), val1 - val2));
QVERIFY(qFuzzyCompare(val1 * qfloat16(val2), val1 * val2));
QVERIFY(qFuzzyCompare(val1 / qfloat16(val2), val1 / val2));
float r1 = 0.f;
r1 += qfloat16(val2);
QVERIFY(qFuzzyCompare(r1,val2));
float r2 = 0.f;
r2 -= qfloat16(val2);
QVERIFY(qFuzzyCompare(r2,-val2));
float r3 = 1.f;
r3 *= qfloat16(val2);
QVERIFY(qFuzzyCompare(r3,val2));
float r4 = 1.f;
r4 /= qfloat16(val2);
QVERIFY(qFuzzyCompare(r4,1.f/val2));
}
#if defined DO_FULL_TEST
void tst_qfloat16::floatToFloat16Full_data()
{
QTest::addColumn<quint32>("group");
for (quint32 j = 0x00; j < 0x100; ++j)
QTest::addRow("%02x", j) << j;
}
void tst_qfloat16::floatToFloat16Full()
{
QFETCH(quint32, group);
for (quint32 j = 0x00; j < 0x100; ++j) {
quint32 data[1<<16];
qfloat16 out[1<<16];
qfloat16 expected[1<<16];
float in[1<<16];
for (int i = 0; i < (1<<16); ++i)
data[i] = (group << 24) | (j << 16) | i;
memcpy(in, data, (1<<16)*sizeof(float));
for (int i = 0; i < (1<<16); ++i)
expected[i] = qfloat16(in[i]);
qFloatToFloat16(out, in, 1<<16);
for (int i = 0; i < (1<<16); ++i) {
if (out[i] != expected[i])
QVERIFY(qIsNaN(out[i]) && qIsNaN(expected[i]));
}
}
}
void tst_qfloat16::floatFromFloat16Full()
{
quint16 data[1<<16];
float out[1<<16];
float expected[1<<16];
for (int i = 0; i < (1<<16); ++i)
data[i] = i;
const qfloat16 *in = reinterpret_cast<const qfloat16 *>(data);
for (int i = 0; i < (1<<16); ++i)
expected[i] = float(in[i]);
qFloatFromFloat16(out, in, 1<<16);
for (int i = 0; i < (1<<16); ++i)
if (out[i] != expected[i])
QVERIFY(qIsNaN(out[i]) && qIsNaN(expected[i]));
}
#endif
void tst_qfloat16::floatToFloat16()
{
constexpr int count = 10000;
float in[count];
qfloat16 out[count];
qfloat16 expected[count];
for (int i = 0; i < count; ++i)
in[i] = (i - count/2) * (1/13.f);
for (int i = 0; i < count; ++i)
expected[i] = qfloat16(in[i]);
qFloatToFloat16(out, in, count);
for (int i = 0; i < count; ++i)
QVERIFY(out[i] == expected[i]);
}
void tst_qfloat16::floatFromFloat16()
{
qfloat16 in[35];
float out[35];
float expected[35];
for (int i = 0; i < 35; ++i)
in[i] = qfloat16(i * (17.f / 3));
for (int i = 0; i < 35; ++i)
expected[i] = float(in[i]);
qFloatFromFloat16(out, in, 35);
for (int i = 0; i < 35; ++i)
QCOMPARE(out[i], expected[i]);
}
static qfloat16 powf16(qfloat16 base, int raise)
{
const qfloat16 one(1.f);
if (raise < 0) {
raise = -raise;
base = one / base;
}
qfloat16 answer = (raise & 1) ? base : one;
while (raise > 0) {
raise >>= 1;
base *= base;
if (raise & 1)
answer *= base;
}
return answer;
}
void tst_qfloat16::finite_data()
{
using Bounds = std::numeric_limits<qfloat16>;
QTest::addColumn<qfloat16>("value");
QTest::addColumn<int>("mode");
QTest::newRow("zero") << qfloat16(0) << FP_ZERO;
QTest::newRow("-zero") << -qfloat16(0) << FP_ZERO;
QTest::newRow("one") << qfloat16(1) << FP_NORMAL;
QTest::newRow("-one") << qfloat16(-1) << FP_NORMAL;
QTest::newRow("ten") << qfloat16(10) << FP_NORMAL;
QTest::newRow("-ten") << qfloat16(-10) << FP_NORMAL;
QTest::newRow("max") << Bounds::max() << FP_NORMAL;
QTest::newRow("lowest") << Bounds::lowest() << FP_NORMAL;
QTest::newRow("min") << Bounds::min() << FP_NORMAL;
QTest::newRow("-min") << -Bounds::min() << FP_NORMAL;
QTest::newRow("denorm_min") << Bounds::denorm_min() << FP_SUBNORMAL;
QTest::newRow("-denorm_min") << -Bounds::denorm_min() << FP_SUBNORMAL;
}
void tst_qfloat16::finite()
{
QFETCH(qfloat16, value);
QFETCH(int, mode);
QCOMPARE(value.isNormal(), mode == FP_NORMAL);
QCOMPARE(value, value); // Fuzzy
QVERIFY(value == value); // Exact
QVERIFY(qIsFinite(value));
QVERIFY(!qIsInf(value));
QVERIFY(!qIsNaN(value));
QCOMPARE(qFpClassify(value), mode);
// *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy
// comparison, and we need exact here.
const qfloat16 zero(0), plus(+1), minus(-1);
const qfloat16 magnitude = (value < zero) ? -value : value;
QVERIFY(value.copySign(plus) == magnitude);
QVERIFY(value.copySign(minus) == -magnitude);
}
void tst_qfloat16::properties()
{
using Bounds = std::numeric_limits<qfloat16>;
QVERIFY(Bounds::is_specialized);
QVERIFY(Bounds::is_signed);
QVERIFY(!Bounds::is_integer);
QVERIFY(!Bounds::is_exact);
// While we'd like to check for __STDC_IEC_559__, as per ISO/IEC 9899:2011
// Annex F (C11, normative for C++11), there are a few corner cases regarding
// denormals where GHS compiler is relying hardware behavior that is not IEC
// 559 compliant.
// On GHS the compiler reports std::numeric_limits<float>::is_iec559 as false.
// and the same supposed to be for qfloat16.
#if !defined(Q_CC_GHS)
QVERIFY(Bounds::is_iec559);
#endif //Q_CC_GHS
#if QT_CONFIG(signaling_nan)
// Technically, presence of NaN and infinities are implied from the above check, but that checkings GHS compiler complies.
QVERIFY(Bounds::has_infinity && Bounds::has_quiet_NaN && Bounds::has_signaling_NaN);
#endif
QVERIFY(Bounds::is_bounded);
QVERIFY(!Bounds::is_modulo);
QVERIFY(!Bounds::traps);
QVERIFY(Bounds::has_infinity);
QVERIFY(Bounds::has_quiet_NaN);
#if QT_CONFIG(signaling_nan)
QVERIFY(Bounds::has_signaling_NaN);
#endif
#if !defined(Q_CC_GHS)
QCOMPARE(Bounds::has_denorm, std::denorm_present);
#else
// For GHS compiler the "denorm_indeterminite" is the expected return value.
QCOMPARE(Bounds::has_denorm, std::denorm_indeterminate);
#endif // Q_CC_GHS
QCOMPARE(Bounds::round_style, std::round_to_nearest);
QCOMPARE(Bounds::radix, 2);
// Untested: has_denorm_loss
}
void tst_qfloat16::limits() // See also: qNaN() and infinity()
{
// *NOT* using QCOMPARE() on finite qfloat16 values, since that uses fuzzy
// comparison, and we need exact here.
using Bounds = std::numeric_limits<qfloat16>;
// A few useful values:
const qfloat16 zero(0), one(1), ten(10);
// The specifics of minus zero:
// (IEEE 754 seems to want -zero < zero, but -0. == 0. and -0.f == 0.f in C++.)
QVERIFY(-zero <= zero);
QVERIFY(-zero == zero);
QVERIFY(!(-zero > zero));
// digits in the mantissa, including the implicit 1 before the binary dot at its left:
QVERIFY(qfloat16(1 << (Bounds::digits - 1)) + one > qfloat16(1 << (Bounds::digits - 1)));
QVERIFY(qfloat16(1 << Bounds::digits) + one == qfloat16(1 << Bounds::digits));
// There is a wilful of-by-one in how m(ax|in)_exponent are defined; they're
// the lowest and highest n for which radix^{n-1} are normal and finite.
const qfloat16 two(Bounds::radix);
qfloat16 bit = powf16(two, Bounds::max_exponent - 1);
QVERIFY(qIsFinite(bit));
QVERIFY(qIsInf(bit * two));
bit = powf16(two, Bounds::min_exponent - 1);
QVERIFY(bit.isNormal());
QCOMPARE(qFpClassify(bit), FP_NORMAL);
QVERIFY(!(bit / two).isNormal());
QCOMPARE(qFpClassify(bit / two), FP_SUBNORMAL);
QVERIFY(bit / two > zero);
// Base ten (with no matching off-by-one idiocy):
// the lowest negative number n such that 10^n is a valid normalized value
qfloat16 low10(powf16(ten, Bounds::min_exponent10));
QVERIFY(low10 > zero);
QVERIFY(low10.isNormal());
low10 /= ten;
QVERIFY(low10 == zero || !low10.isNormal());
// the largest positive number n such that 10^n is a representable finite value
qfloat16 high10(powf16(ten, Bounds::max_exponent10));
QVERIFY(high10 > zero);
QVERIFY(qIsFinite(high10));
QVERIFY(!qIsFinite(high10 * ten));
QCOMPARE(qFpClassify(high10), FP_NORMAL);
// How many digits are significant ? (Casts avoid linker errors ...)
QCOMPARE(int(Bounds::digits10), 3); // ~9.88e-4 has enough sigificant digits:
qfloat16 below(9.876e-4f), above(9.884e-4f); // both round to ~9.88e-4
QVERIFY(below == above);
QCOMPARE(int(Bounds::max_digits10), 5); // we need 5 to distinguish these two:
QVERIFY(qfloat16(1000.5f) != qfloat16(1001.4f));
// Actual limiting values of the type:
const qfloat16 rose(one + Bounds::epsilon());
QVERIFY(rose > one);
QVERIFY(one + Bounds::epsilon() / two == one);
QVERIFY(Bounds::max() > zero);
QVERIFY(qIsInf(Bounds::max() * rose));
QVERIFY(Bounds::lowest() < zero);
QVERIFY(qIsInf(Bounds::lowest() * rose));
QVERIFY(Bounds::min() > zero);
QVERIFY(!(Bounds::min() / rose).isNormal());
QVERIFY(Bounds::denorm_min() > zero);
QVERIFY(Bounds::denorm_min() / two == zero);
const qfloat16 under = (-Bounds::denorm_min()) / two;
QVERIFY(under == -zero);
QCOMPARE(qfloat16(1).copySign(under), qfloat16(-1));
}
void tst_qfloat16::mantissaOverflow()
{
// Test we don't change category due to mantissa overflow when rounding.
quint32 in = 0x7fffffff;
float f;
memcpy(&f, &in, 4);
qfloat16 f16 = qfloat16(f);
qfloat16 f16s[1];
qFloatToFloat16(f16s, &f, 1);
QCOMPARE(f16, f16s[0]);
QVERIFY(qIsNaN(f16));
}
void tst_qfloat16::dataStream()
{
QByteArray ba;
QDataStream ds(&ba, QIODevice::ReadWrite);
ds << qfloat16(1.5) << qfloat16(-1);
QCOMPARE(ba.size(), 4);
QCOMPARE(ds.status(), QDataStream::Ok);
QCOMPARE(ba, QByteArray("\x3e\0\xbc\0", 4));
ds.device()->seek(0);
ds.resetStatus();
ds.setByteOrder(QDataStream::LittleEndian);
ds << qfloat16(0) << qfloat16(-1);
QCOMPARE(ds.status(), QDataStream::Ok);
QCOMPARE(ba, QByteArray("\0\0\0\xbc", 4));
ds.device()->seek(0);
ds.resetStatus();
qfloat16 zero = 1;
ds >> zero;
QCOMPARE(ds.status(), QDataStream::Ok);
QCOMPARE(zero, qfloat16(0));
ds.device()->seek(0);
ds.resetStatus();
QMetaType mt = QMetaType(QMetaType::Float16);
QVERIFY(mt.save(ds, &zero));
ds.device()->seek(0);
ds.resetStatus();
zero = -1;
QVERIFY(mt.load(ds, &zero));
QCOMPARE(zero, qfloat16(0));
}
void tst_qfloat16::textStream()
{
QString buffer;
{
QTextStream ts(&buffer);
ts << qfloat16(0) << Qt::endl << qfloat16(1.5);
QCOMPARE(ts.status(), QTextStream::Ok);
}
QCOMPARE(buffer, "0\n1.5");
{
QTextStream ts(&buffer);
qfloat16 zero = qfloat16(-2.5), threehalves = 1234;
ts >> zero >> threehalves;
QCOMPARE(ts.status(), QTextStream::Ok);
QCOMPARE(zero, qfloat16(0));
QCOMPARE(threehalves, 1.5);
}
}
QTEST_APPLESS_MAIN(tst_qfloat16)
#include "tst_qfloat16.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qgetputenv Test:
#####################################################################
qt_internal_add_test(tst_qgetputenv
SOURCES
tst_qgetputenv.cpp
)

View File

@ -0,0 +1,207 @@
// Copyright (C) 2016 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qdebug.h>
#include <QTest>
#include <qglobal.h>
#ifdef Q_OS_WIN
#include <qt_windows.h>
#endif
class tst_QGetPutEnv : public QObject
{
Q_OBJECT
private slots:
void getSetCheck();
void encoding();
void intValue_data();
void intValue();
};
void tst_QGetPutEnv::getSetCheck()
{
const char varName[] = "should_not_exist";
bool ok;
QVERIFY(!qEnvironmentVariableIsSet(varName));
QVERIFY(qEnvironmentVariableIsEmpty(varName));
ok = true;
QCOMPARE(qEnvironmentVariableIntValue(varName), 0);
QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0);
QVERIFY(!ok);
QByteArray result = qgetenv(varName);
QVERIFY(result.isNull());
QString sresult = qEnvironmentVariable(varName);
QVERIFY(sresult.isNull());
sresult = qEnvironmentVariable(varName, "hello");
QCOMPARE(sresult, QString("hello"));
#ifndef Q_OS_WIN
QVERIFY(qputenv(varName, "")); // deletes varName instead of making it empty, on Windows
QVERIFY(qEnvironmentVariableIsSet(varName));
QVERIFY(qEnvironmentVariableIsEmpty(varName));
ok = true;
QCOMPARE(qEnvironmentVariableIntValue(varName), 0);
QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0);
QVERIFY(!ok);
result = qgetenv(varName);
QVERIFY(!result.isNull());
QCOMPARE(result, QByteArray());
sresult = qEnvironmentVariable(varName);
QVERIFY(!sresult.isNull());
QCOMPARE(sresult, QString());
sresult = qEnvironmentVariable(varName, "hello");
QVERIFY(!sresult.isNull());
QCOMPARE(sresult, QString());
#endif
constexpr char varValueFullString[] = "supervalue123";
const auto varValueQBA = QByteArray::fromRawData(varValueFullString, sizeof varValueFullString - 4);
QCOMPARE_EQ(varValueQBA, "supervalue");
QVERIFY(qputenv(varName, varValueQBA));
QVERIFY(qEnvironmentVariableIsSet(varName));
QVERIFY(!qEnvironmentVariableIsEmpty(varName));
ok = true;
QCOMPARE(qEnvironmentVariableIntValue(varName), 0);
QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0);
QVERIFY(!ok);
result = qgetenv(varName);
QCOMPARE(result, QByteArrayLiteral("supervalue"));
sresult = qEnvironmentVariable(varName);
QCOMPARE(sresult, QString("supervalue"));
sresult = qEnvironmentVariable(varName, "hello");
QCOMPARE(sresult, QString("supervalue"));
qputenv(varName,QByteArray());
// Now test qunsetenv
QVERIFY(qunsetenv(varName));
QVERIFY(!qEnvironmentVariableIsSet(varName)); // note: might fail on some systems!
QVERIFY(qEnvironmentVariableIsEmpty(varName));
ok = true;
QCOMPARE(qEnvironmentVariableIntValue(varName), 0);
QCOMPARE(qEnvironmentVariableIntValue(varName, &ok), 0);
QVERIFY(!ok);
result = qgetenv(varName);
QVERIFY(result.isNull());
sresult = qEnvironmentVariable(varName);
QVERIFY(sresult.isNull());
sresult = qEnvironmentVariable(varName, "hello");
QCOMPARE(sresult, QString("hello"));
}
void tst_QGetPutEnv::encoding()
{
// The test string is:
// U+0061 LATIN SMALL LETTER A
// U+00E1 LATIN SMALL LETTER A WITH ACUTE
// U+03B1 GREEK SMALL LETTER ALPHA
// U+0430 CYRILLIC SMALL LETTER A
// This has letters in three different scripts, so no locale besides
// UTF-8 is able handle them all.
// The LATIN SMALL LETTER A WITH ACUTE is NFC for NFD:
// U+0061 U+0301 LATIN SMALL LETTER A + COMBINING ACUTE ACCENT
const char varName[] = "should_not_exist";
static const wchar_t rawvalue[] = { 'a', 0x00E1, 0x03B1, 0x0430, 0 };
QString value = QString::fromWCharArray(rawvalue);
#if defined(Q_OS_WIN)
const wchar_t wvarName[] = L"should_not_exist";
_wputenv_s(wvarName, rawvalue);
#else
// confirm the locale is UTF-8
if (value.toLocal8Bit() != "a\xc3\xa1\xce\xb1\xd0\xb0")
QSKIP("Locale is not UTF-8, cannot test");
qputenv(varName, QFile::encodeName(value));
#endif
QVERIFY(qEnvironmentVariableIsSet(varName));
QCOMPARE(qEnvironmentVariable(varName), value);
}
void tst_QGetPutEnv::intValue_data()
{
QTest::addColumn<QByteArray>("value");
QTest::addColumn<int>("expected");
QTest::addColumn<bool>("ok");
// some repetition from what is tested in getSetCheck()
QTest::newRow("empty") << QByteArray() << 0 << false;
QTest::newRow("spaces-heading") << QByteArray(" \n\r\t1") << 1 << true;
QTest::newRow("spaces-trailing") << QByteArray("1 \n\r\t") << 1 << true;
QTest::newRow("junk-heading") << QByteArray("x1") << 0 << false;
QTest::newRow("junk-trailing") << QByteArray("1x") << 0 << false;
#define ROW(x, i, b) \
QTest::newRow(#x) << QByteArray(#x) << (i) << (b)
ROW(auto, 0, false);
ROW(1auto, 0, false);
ROW(0, 0, true);
ROW(+0, 0, true);
ROW(1, 1, true);
ROW(+1, 1, true);
ROW(09, 0, false);
ROW(010, 8, true);
ROW(0x10, 16, true);
ROW(0x, 0, false);
ROW(0xg, 0, false);
ROW(0x1g, 0, false);
ROW(000000000000000000000000000000000000000000000000001, 0, false);
ROW(+000000000000000000000000000000000000000000000000001, 0, false);
ROW(000000000000000000000000000000000000000000000000001g, 0, false);
ROW(-0, 0, true);
ROW(-1, -1, true);
ROW(-010, -8, true);
ROW(-000000000000000000000000000000000000000000000000001, 0, false);
ROW(2147483648, 0, false);
// ROW(0xffffffff, -1, true); // could be expected, but not how QByteArray::toInt() works
ROW(0xffffffff, 0, false);
const int bases[] = {10, 8, 16};
for (size_t i = 0; i < sizeof bases / sizeof *bases; ++i) {
QTest::addRow("INT_MAX, base %d", bases[i])
<< QByteArray::number(INT_MAX) << INT_MAX << true;
QTest::addRow("INT_MAX+1, base %d", bases[i])
<< QByteArray::number(qlonglong(INT_MAX) + 1) << 0 << false;
QTest::addRow("INT_MIN, base %d", bases[i])
<< QByteArray::number(INT_MIN) << INT_MIN << true;
QTest::addRow("INT_MIN-1, base %d", bases[i])
<< QByteArray::number(qlonglong(INT_MIN) - 1) << 0 << false;
};
}
void tst_QGetPutEnv::intValue()
{
const int maxlen = (sizeof(int) * CHAR_BIT + 2) / 3;
const char varName[] = "should_not_exist";
QFETCH(QByteArray, value);
QFETCH(int, expected);
QFETCH(bool, ok);
bool actualOk = !ok;
// Self-test: confirm that it was like the docs said it should be
if (value.size() < maxlen) {
QCOMPARE(value.toInt(&actualOk, 0), expected);
QCOMPARE(actualOk, ok);
}
actualOk = !ok;
QVERIFY(qputenv(varName, value));
QCOMPARE(qEnvironmentVariableIntValue(varName), expected);
QCOMPARE(qEnvironmentVariableIntValue(varName, &actualOk), expected);
QCOMPARE(actualOk, ok);
}
QTEST_MAIN(tst_QGetPutEnv)
#include "tst_qgetputenv.moc"

View File

@ -0,0 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qglobal Test:
#####################################################################
qt_internal_add_test(tst_qglobal
SOURCES
qglobal.c
tst_qglobal.cpp
)
## Scopes:
#####################################################################

View File

@ -0,0 +1,92 @@
// Copyright (C) 2017 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qglobal.h>
#include <QtCore/qtversion.h>
#ifdef Q_COMPILER_THREAD_LOCAL
# include <threads.h>
#endif
/*
* Certain features of qglobal.h must work in C mode too. We test that
* everything works.
*/
/* Types and Q_UNUSED */
void tst_GlobalTypes()
{
qint8 s8;
qint16 s16;
qint32 s32;
qint64 s64;
qlonglong sll;
Q_UNUSED(s8); Q_UNUSED(s16); Q_UNUSED(s32); Q_UNUSED(s64); Q_UNUSED(sll);
quint8 u8;
quint16 u16;
quint32 u32;
quint64 u64;
qulonglong ull;
Q_UNUSED(u8); Q_UNUSED(u16); Q_UNUSED(u32); Q_UNUSED(u64); Q_UNUSED(ull);
uchar uc;
ushort us;
uint ui;
ulong ul;
Q_UNUSED(uc); Q_UNUSED(us); Q_UNUSED(ui); Q_UNUSED(ul);
qreal qr;
Q_UNUSED(qr);
qsizetype qs;
qptrdiff qp;
qintptr qip;
quintptr qup;
Q_UNUSED(qs); Q_UNUSED(qp); Q_UNUSED(qip); Q_UNUSED(qup);
}
/* Qt version */
int tst_QtVersion()
{
return QT_VERSION;
}
const char *tst_qVersion() Q_DECL_NOEXCEPT;
const char *tst_qVersion()
{
#if !defined(QT_NAMESPACE)
return qVersion();
#else
return NULL;
#endif
}
/* Static assertion */
Q_STATIC_ASSERT(true);
Q_STATIC_ASSERT(1);
Q_STATIC_ASSERT_X(true, "Message");
Q_STATIC_ASSERT_X(1, "Message");
Q_STATIC_ASSERT(!false);
Q_STATIC_ASSERT(!0);
Q_STATIC_ASSERT(!!true);
Q_STATIC_ASSERT(!!1);
#ifdef __COUNTER__
// if the compiler supports __COUNTER__, multiple
// Q_STATIC_ASSERT's on a single line should compile:
Q_STATIC_ASSERT(true); Q_STATIC_ASSERT_X(!false, "");
#endif // __COUNTER__
#ifdef Q_COMPILER_THREAD_LOCAL
static thread_local int gt_var;
void thread_local_test()
{
static thread_local int t_var;
t_var = gt_var;
Q_UNUSED(t_var);
}
#endif

View File

@ -0,0 +1,695 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QPair>
#include <QSysInfo>
#include <QLatin1String>
#include <QString>
#include <QtVersion>
#include <cmath>
class tst_QGlobal: public QObject
{
Q_OBJECT
private slots:
void cMode();
void qIsNull();
void for_each();
void qassert();
void qtry();
void checkptr();
void qstaticassert();
void qConstructorFunction();
void qCoreAppStartupFunction();
void qCoreAppStartupFunctionRestart();
void integerForSize();
void buildAbiEndianness();
void testqOverload();
void testqMinMax();
void qRoundFloats_data();
void qRoundFloats();
void qRoundDoubles_data();
void qRoundDoubles();
void PRImacros();
void testqToUnderlying();
};
extern "C" { // functions in qglobal.c
void tst_GlobalTypes();
int tst_QtVersion();
const char *tst_qVersion();
}
void tst_QGlobal::cMode()
{
tst_GlobalTypes();
QCOMPARE(tst_QtVersion(), QT_VERSION);
#ifndef QT_NAMESPACE
QCOMPARE(tst_qVersion(), qVersion());
#endif
}
void tst_QGlobal::qIsNull()
{
double d = 0.0;
float f = 0.0f;
QVERIFY(::qIsNull(d));
QVERIFY(::qIsNull(f));
d += 0.000000001;
f += 0.0000001f;
QVERIFY(!::qIsNull(d));
QVERIFY(!::qIsNull(f));
d = -0.0;
f = -0.0f;
QVERIFY(::qIsNull(d));
QVERIFY(::qIsNull(f));
}
void tst_QGlobal::for_each()
{
QList<int> list;
list << 0 << 1 << 2 << 3 << 4 << 5;
int counter = 0;
foreach(int i, list) {
QCOMPARE(i, counter++);
}
QCOMPARE(counter, list.size());
// do it again, to make sure we don't have any for-scoping
// problems with older compilers
counter = 0;
foreach(int i, list) {
QCOMPARE(i, counter++);
}
QCOMPARE(counter, list.size());
// check whether we can pass a constructor as container argument
counter = 0;
foreach (int i, QList<int>(list)) {
QCOMPARE(i, counter++);
}
QCOMPARE(counter, list.size());
// check whether we can use a lambda
counter = 0;
foreach (int i, [&](){ return list; }()) {
QCOMPARE(i, counter++);
}
QCOMPARE(counter, list.size());
// Should also work with an existing variable
int local = 0;
counter = 0;
foreach (local, list) {
QCOMPARE(local, counter++);
}
QCOMPARE(counter, list.size());
QCOMPARE(local, counter - 1);
// Test the macro does not mess if/else conditions
counter = 0;
if (true)
foreach (int i, list)
QCOMPARE(i, counter++);
else
QFAIL("If/Else mismatch");
QCOMPARE(counter, list.size());
counter = 0;
if (false)
foreach (int i, list)
if (i) QFAIL("If/Else mismatch");
else QFAIL("If/Else mismatch");
else
foreach (int i, list)
if (false) { }
else QCOMPARE(i, counter++);
QCOMPARE(counter, list.size());
// break and continue
counter = 0;
foreach (int i, list) {
if (i == 0)
continue;
QCOMPARE(i, (counter++) + 1);
if (i == 3)
break;
}
QCOMPARE(counter, 3);
}
void tst_QGlobal::qassert()
{
bool passed = false;
if (false) {
Q_ASSERT(false);
} else {
passed = true;
}
QVERIFY(passed);
passed = false;
if (false) {
Q_ASSERT_X(false, "tst_QGlobal", "qassert");
} else {
passed = true;
}
QVERIFY(passed);
passed = false;
if (false)
Q_ASSERT(false);
else
passed = true;
QVERIFY(passed);
passed = false;
if (false)
Q_ASSERT_X(false, "tst_QGlobal", "qassert");
else
passed = true;
QVERIFY(passed);
}
void tst_QGlobal::qtry()
{
int i = 0;
QT_TRY {
i = 1;
QT_THROW(42);
i = 2;
} QT_CATCH(int) {
QCOMPARE(i, 1);
i = 7;
}
#ifdef QT_NO_EXCEPTIONS
QCOMPARE(i, 2);
#else
QCOMPARE(i, 7);
#endif
// check propper if/else scoping
i = 0;
if (true) {
QT_TRY {
i = 2;
QT_THROW(42);
i = 4;
} QT_CATCH(int) {
QCOMPARE(i, 2);
i = 4;
}
} else {
QCOMPARE(i, 0);
}
QCOMPARE(i, 4);
i = 0;
if (false) {
QT_TRY {
i = 2;
QT_THROW(42);
i = 4;
} QT_CATCH(int) {
QCOMPARE(i, 2);
i = 2;
}
} else {
i = 8;
}
QCOMPARE(i, 8);
i = 0;
if (false) {
i = 42;
} else {
QT_TRY {
i = 2;
QT_THROW(42);
i = 4;
} QT_CATCH(int) {
QCOMPARE(i, 2);
i = 4;
}
}
QCOMPARE(i, 4);
}
void tst_QGlobal::checkptr()
{
int i;
QCOMPARE(q_check_ptr(&i), &i);
const char *c = "hello";
QCOMPARE(q_check_ptr(c), c);
}
// Check Q_STATIC_ASSERT, It should compile
// note that, we are not able to test Q_STATIC_ASSERT(false), to do it manually someone has
// to replace expressions (in the asserts) one by one to false, and check if it breaks build.
class MyTrue
{
public:
MyTrue()
{
Q_STATIC_ASSERT(true);
Q_STATIC_ASSERT(!false);
Q_STATIC_ASSERT_X(true,"");
Q_STATIC_ASSERT_X(!false,"");
}
~MyTrue()
{
Q_STATIC_ASSERT(true);
Q_STATIC_ASSERT(!false);
Q_STATIC_ASSERT_X(true,"");
Q_STATIC_ASSERT_X(!false,"");
}
Q_STATIC_ASSERT(true);
Q_STATIC_ASSERT(!false);
Q_STATIC_ASSERT_X(true,"");
Q_STATIC_ASSERT_X(!false,"");
};
struct MyExpresion
{
void foo()
{
Q_STATIC_ASSERT(sizeof(MyTrue) > 0);
Q_STATIC_ASSERT(sizeof(MyTrue) > 0);
Q_STATIC_ASSERT_X(sizeof(MyTrue) > 0,"");
Q_STATIC_ASSERT_X(sizeof(MyTrue) > 0,"");
}
private:
Q_STATIC_ASSERT(sizeof(MyTrue) > 0);
Q_STATIC_ASSERT(sizeof(MyTrue) > 0);
Q_STATIC_ASSERT_X(sizeof(MyTrue) > 0, "");
Q_STATIC_ASSERT_X(sizeof(MyTrue) > 0, "");
};
struct TypeDef
{
typedef int T;
Q_STATIC_ASSERT(sizeof(T));
Q_STATIC_ASSERT_X(sizeof(T), "");
};
template<typename T1, typename T2>
struct Template
{
static const bool True = true;
typedef typename T1::T DependentType;
Q_STATIC_ASSERT(True);
Q_STATIC_ASSERT(!!True);
Q_STATIC_ASSERT(sizeof(DependentType));
Q_STATIC_ASSERT(!!sizeof(DependentType));
Q_STATIC_ASSERT_X(True, "");
Q_STATIC_ASSERT_X(!!True, "");
Q_STATIC_ASSERT_X(sizeof(DependentType), "");
Q_STATIC_ASSERT_X(!!sizeof(DependentType), "");
};
struct MyTemplate
{
static const bool Value = Template<TypeDef, int>::True;
Q_STATIC_ASSERT(Value);
Q_STATIC_ASSERT(!!Value);
Q_STATIC_ASSERT_X(Value, "");
Q_STATIC_ASSERT_X(!!Value, "");
};
void tst_QGlobal::qstaticassert()
{
// Test multiple Q_STATIC_ASSERT on a single line
Q_STATIC_ASSERT(true); Q_STATIC_ASSERT_X(!false, "");
// Force compilation of these classes
MyTrue tmp1;
MyExpresion tmp2;
MyTemplate tmp3;
Q_UNUSED(tmp1);
Q_UNUSED(tmp2);
Q_UNUSED(tmp3);
QVERIFY(true); // if the test compiles it has passed.
}
static int qConstructorFunctionValue;
static void qConstructorFunctionCtor()
{
qConstructorFunctionValue = 123;
}
Q_CONSTRUCTOR_FUNCTION(qConstructorFunctionCtor);
void tst_QGlobal::qConstructorFunction()
{
QCOMPARE(qConstructorFunctionValue, 123);
}
static int qStartupFunctionValue;
static void myStartupFunc()
{
Q_ASSERT(QCoreApplication::instance());
if (QCoreApplication::instance())
qStartupFunctionValue += 124;
}
Q_COREAPP_STARTUP_FUNCTION(myStartupFunc)
void tst_QGlobal::qCoreAppStartupFunction()
{
QCOMPARE(qStartupFunctionValue, 0);
int argc = 1;
char *argv[] = { const_cast<char*>(QTest::currentAppName()) };
QCoreApplication app(argc, argv);
QCOMPARE(qStartupFunctionValue, 124);
}
void tst_QGlobal::qCoreAppStartupFunctionRestart()
{
qStartupFunctionValue = 0;
qCoreAppStartupFunction();
qStartupFunctionValue = 0;
qCoreAppStartupFunction();
}
struct isEnum_A {
int n_;
};
enum isEnum_B_Byte { isEnum_B_Byte_x = 63 };
enum isEnum_B_Short { isEnum_B_Short_x = 1024 };
enum isEnum_B_Int { isEnum_B_Int_x = 1 << 20 };
union isEnum_C {};
class isEnum_D {
public:
operator int() const;
};
class isEnum_E {
private:
operator int() const;
};
class isEnum_F {
public:
enum AnEnum {};
};
struct Empty {};
template <class T> struct AlignmentInStruct { T dummy; };
typedef int (*fun) ();
typedef int (Empty::*memFun) ();
void tst_QGlobal::integerForSize()
{
// compile-only test:
static_assert(sizeof(QIntegerForSize<1>::Signed) == 1);
static_assert(sizeof(QIntegerForSize<2>::Signed) == 2);
static_assert(sizeof(QIntegerForSize<4>::Signed) == 4);
static_assert(sizeof(QIntegerForSize<8>::Signed) == 8);
static_assert(sizeof(QIntegerForSize<1>::Unsigned) == 1);
static_assert(sizeof(QIntegerForSize<2>::Unsigned) == 2);
static_assert(sizeof(QIntegerForSize<4>::Unsigned) == 4);
static_assert(sizeof(QIntegerForSize<8>::Unsigned) == 8);
}
typedef QPair<const char *, const char *> stringpair;
Q_DECLARE_METATYPE(stringpair)
void tst_QGlobal::buildAbiEndianness()
{
#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
QLatin1String endian("little_endian");
#elif Q_BYTE_ORDER == Q_BIG_ENDIAN
QLatin1String endian("big_endian");
#endif
QVERIFY(QSysInfo::buildAbi().contains(endian));
}
struct Overloaded
{
void foo() {}
void foo(QByteArray) {}
void foo(QByteArray, const QString &) {}
void constFoo() const {}
void constFoo(QByteArray) const {}
void constFoo(QByteArray, const QString &) const {}
void mixedFoo() {}
void mixedFoo(QByteArray) const {}
};
void freeOverloaded() {}
void freeOverloaded(QByteArray) {}
void freeOverloaded(QByteArray, const QString &) {}
void freeOverloadedGet(QByteArray) {}
QByteArray freeOverloadedGet() { return QByteArray(); }
void tst_QGlobal::testqOverload()
{
#ifdef Q_COMPILER_VARIADIC_TEMPLATES
// void returning free overloaded functions
QVERIFY(QOverload<>::of(&freeOverloaded) ==
static_cast<void (*)()>(&freeOverloaded));
QVERIFY(QOverload<QByteArray>::of(&freeOverloaded) ==
static_cast<void (*)(QByteArray)>(&freeOverloaded));
QVERIFY((QOverload<QByteArray, const QString &>::of(&freeOverloaded)) ==
static_cast<void (*)(QByteArray, const QString &)>(&freeOverloaded));
// value returning free overloaded functions
QVERIFY(QOverload<>::of(&freeOverloadedGet) ==
static_cast<QByteArray (*)()>(&freeOverloadedGet));
QVERIFY(QOverload<QByteArray>::of(&freeOverloadedGet) ==
static_cast<void (*)(QByteArray)>(&freeOverloadedGet));
// void returning overloaded member functions
QVERIFY(QOverload<>::of(&Overloaded::foo) ==
static_cast<void (Overloaded::*)()>(&Overloaded::foo));
QVERIFY(QOverload<QByteArray>::of(&Overloaded::foo) ==
static_cast<void (Overloaded::*)(QByteArray)>(&Overloaded::foo));
QVERIFY((QOverload<QByteArray, const QString &>::of(&Overloaded::foo)) ==
static_cast<void (Overloaded::*)(QByteArray, const QString &)>(&Overloaded::foo));
// void returning overloaded const member functions
QVERIFY(QOverload<>::of(&Overloaded::constFoo) ==
static_cast<void (Overloaded::*)() const>(&Overloaded::constFoo));
QVERIFY(QOverload<QByteArray>::of(&Overloaded::constFoo) ==
static_cast<void (Overloaded::*)(QByteArray) const>(&Overloaded::constFoo));
QVERIFY((QOverload<QByteArray, const QString &>::of(&Overloaded::constFoo)) ==
static_cast<void (Overloaded::*)(QByteArray, const QString &) const>(&Overloaded::constFoo));
// void returning overloaded const AND non-const member functions
QVERIFY(QNonConstOverload<>::of(&Overloaded::mixedFoo) ==
static_cast<void (Overloaded::*)()>(&Overloaded::mixedFoo));
QVERIFY(QConstOverload<QByteArray>::of(&Overloaded::mixedFoo) ==
static_cast<void (Overloaded::*)(QByteArray) const>(&Overloaded::mixedFoo));
// void returning free overloaded functions
QVERIFY(qOverload<>(&freeOverloaded) ==
static_cast<void (*)()>(&freeOverloaded));
QVERIFY(qOverload<QByteArray>(&freeOverloaded) ==
static_cast<void (*)(QByteArray)>(&freeOverloaded));
QVERIFY((qOverload<QByteArray, const QString &>(&freeOverloaded) ==
static_cast<void (*)(QByteArray, const QString &)>(&freeOverloaded)));
// value returning free overloaded functions
QVERIFY(qOverload<>(&freeOverloadedGet) ==
static_cast<QByteArray (*)()>(&freeOverloadedGet));
QVERIFY(qOverload<QByteArray>(&freeOverloadedGet) ==
static_cast<void (*)(QByteArray)>(&freeOverloadedGet));
// void returning overloaded member functions
QVERIFY(qOverload<>(&Overloaded::foo) ==
static_cast<void (Overloaded::*)()>(&Overloaded::foo));
QVERIFY(qOverload<QByteArray>(&Overloaded::foo) ==
static_cast<void (Overloaded::*)(QByteArray)>(&Overloaded::foo));
QVERIFY((qOverload<QByteArray, const QString &>(&Overloaded::foo)) ==
static_cast<void (Overloaded::*)(QByteArray, const QString &)>(&Overloaded::foo));
// void returning overloaded const member functions
QVERIFY(qOverload<>(&Overloaded::constFoo) ==
static_cast<void (Overloaded::*)() const>(&Overloaded::constFoo));
QVERIFY(qOverload<QByteArray>(&Overloaded::constFoo) ==
static_cast<void (Overloaded::*)(QByteArray) const>(&Overloaded::constFoo));
QVERIFY((qOverload<QByteArray, const QString &>(&Overloaded::constFoo)) ==
static_cast<void (Overloaded::*)(QByteArray, const QString &) const>(&Overloaded::constFoo));
// void returning overloaded const AND non-const member functions
QVERIFY(qNonConstOverload<>(&Overloaded::mixedFoo) ==
static_cast<void (Overloaded::*)()>(&Overloaded::mixedFoo));
QVERIFY(qConstOverload<QByteArray>(&Overloaded::mixedFoo) ==
static_cast<void (Overloaded::*)(QByteArray) const>(&Overloaded::mixedFoo));
#endif
}
// enforce that types are identical when comparing
template<typename T>
void compare(T a, T b)
{ QCOMPARE(a, b); }
void tst_QGlobal::testqMinMax()
{
// signed types
compare(qMin(float(1), double(-1)), double(-1));
compare(qMin(double(1), float(-1)), double(-1));
compare(qMin(short(1), int(-1)), int(-1));
compare(qMin(short(1), long(-1)), long(-1));
compare(qMin(qint64(1), short(-1)), qint64(-1));
compare(qMax(float(1), double(-1)), double(1));
compare(qMax(short(1), long(-1)), long(1));
compare(qMax(qint64(1), short(-1)), qint64(1));
// unsigned types
compare(qMin(ushort(1), ulong(2)), ulong(1));
compare(qMin(quint64(1), ushort(2)), quint64(1));
compare(qMax(ushort(1), ulong(2)), ulong(2));
compare(qMax(quint64(1), ushort(2)), quint64(2));
}
void tst_QGlobal::qRoundFloats_data()
{
QTest::addColumn<float>("actual");
QTest::addColumn<float>("expected");
QTest::newRow("round half") << 0.5f << 1.0f;
QTest::newRow("round negative half") << -0.5f << -1.0f;
QTest::newRow("round negative") << -1.4f << -1.0f;
QTest::newRow("round largest representable float less than 0.5") << std::nextafter(0.5f, 0.0f) << 0.0f;
}
void tst_QGlobal::qRoundFloats() {
QFETCH(float, actual);
QFETCH(float, expected);
#if !(defined(Q_PROCESSOR_ARM_64) && (__has_builtin(__builtin_round) || defined(Q_CC_GNU)) && !defined(Q_CC_CLANG))
QEXPECT_FAIL("round largest representable float less than 0.5",
"We know qRound fails in this case, but decided that we value simplicity over correctness",
Continue);
#endif
QCOMPARE(qRound(actual), expected);
#if !(defined(Q_PROCESSOR_ARM_64) && (__has_builtin(__builtin_round) || defined(Q_CC_GNU)) && !defined(Q_CC_CLANG))
QEXPECT_FAIL("round largest representable float less than 0.5",
"We know qRound fails in this case, but decided that we value simplicity over correctness",
Continue);
#endif
QCOMPARE(qRound64(actual), expected);
}
void tst_QGlobal::qRoundDoubles_data() {
QTest::addColumn<double>("actual");
QTest::addColumn<double>("expected");
QTest::newRow("round half") << 0.5 << 1.0;
QTest::newRow("round negative half") << -0.5 << -1.0;
QTest::newRow("round negative") << -1.4 << -1.0;
QTest::newRow("round largest representable double less than 0.5") << std::nextafter(0.5, 0.0) << 0.0;
}
void tst_QGlobal::qRoundDoubles() {
QFETCH(double, actual);
QFETCH(double, expected);
#if !(defined(Q_PROCESSOR_ARM_64) && (__has_builtin(__builtin_round) || defined(Q_CC_GNU)) && !defined(Q_CC_CLANG))
QEXPECT_FAIL("round largest representable double less than 0.5",
"We know qRound fails in this case, but decided that we value simplicity over correctness",
Continue);
#endif
QCOMPARE(qRound(actual), expected);
#if !(defined(Q_PROCESSOR_ARM_64) && (__has_builtin(__builtin_round) || defined(Q_CC_GNU)) && !defined(Q_CC_CLANG))
QEXPECT_FAIL("round largest representable double less than 0.5",
"We know qRound fails in this case, but decided that we value simplicity over correctness",
Continue);
#endif
QCOMPARE(qRound64(actual), expected);
}
void tst_QGlobal::PRImacros()
{
// none of these calls must generate a -Wformat warning
{
quintptr p = 123u;
QCOMPARE(QString::asprintf("The value %" PRIuQUINTPTR " is nice", p), "The value 123 is nice");
QCOMPARE(QString::asprintf("The value %" PRIoQUINTPTR " is nice", p), "The value 173 is nice");
QCOMPARE(QString::asprintf("The value %" PRIxQUINTPTR " is nice", p), "The value 7b is nice");
QCOMPARE(QString::asprintf("The value %" PRIXQUINTPTR " is nice", p), "The value 7B is nice");
}
{
qintptr p = 123;
QCOMPARE(QString::asprintf("The value %" PRIdQINTPTR " is nice", p), "The value 123 is nice");
QCOMPARE(QString::asprintf("The value %" PRIiQINTPTR " is nice", p), "The value 123 is nice");
}
{
qptrdiff d = 123;
QCOMPARE(QString::asprintf("The value %" PRIdQPTRDIFF " is nice", d), "The value 123 is nice");
QCOMPARE(QString::asprintf("The value %" PRIiQPTRDIFF " is nice", d), "The value 123 is nice");
}
{
qsizetype s = 123;
QCOMPARE(QString::asprintf("The value %" PRIdQSIZETYPE " is nice", s), "The value 123 is nice");
QCOMPARE(QString::asprintf("The value %" PRIiQSIZETYPE " is nice", s), "The value 123 is nice");
}
}
void tst_QGlobal::testqToUnderlying()
{
enum class E {
E1 = 123,
E2 = 456,
};
static_assert(std::is_same_v<decltype(qToUnderlying(E::E1)), int>);
QCOMPARE(qToUnderlying(E::E1), 123);
QCOMPARE(qToUnderlying(E::E2), 456);
enum EE : unsigned long {
EE1 = 123,
EE2 = 456,
};
static_assert(std::is_same_v<decltype(qToUnderlying(EE1)), unsigned long>);
QCOMPARE(qToUnderlying(EE1), 123UL);
QCOMPARE(qToUnderlying(EE2), 456UL);
}
QTEST_APPLESS_MAIN(tst_QGlobal)
#include "tst_qglobal.moc"

View File

@ -0,0 +1,15 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qglobalstatic Test:
#####################################################################
qt_internal_add_test(tst_qglobalstatic
EXCEPTIONS
SOURCES
tst_qglobalstatic.cpp
LIBRARIES
Qt::CorePrivate
Qt::TestPrivate
)

View File

@ -0,0 +1,233 @@
// Copyright (C) 2016 Thiago Macieira <thiago@kde.org>
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/QThread>
#include <QTest>
#include <QReadWriteLock>
#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
#include <sys/resource.h>
#endif
#include <QtTest/private/qemulationdetector_p.h>
class tst_QGlobalStatic : public QObject
{
Q_OBJECT
public Q_SLOTS:
void initTestCase();
private Q_SLOTS:
void beforeInitialization();
void api();
void constVolatile();
void exception();
void catchExceptionAndRetry();
void threadStressTest();
void afterDestruction();
};
void tst_QGlobalStatic::initTestCase()
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_INTEGRITY)
// The tests create a lot of threads, which require file descriptors. On systems like
// OS X low defaults such as 256 as the limit for the number of simultaneously
// open files is not sufficient.
struct rlimit numFiles;
if (getrlimit(RLIMIT_NOFILE, &numFiles) == 0 && numFiles.rlim_cur < 1024) {
numFiles.rlim_cur = qMin(rlim_t(1024), numFiles.rlim_max);
setrlimit(RLIMIT_NOFILE, &numFiles);
}
#endif
}
Q_GLOBAL_STATIC_WITH_ARGS(const int, constInt, (42))
Q_GLOBAL_STATIC_WITH_ARGS(volatile int, volatileInt, (-47))
void otherFunction()
{
// never called
constInt();
volatileInt();
}
// do not initialize the following Q_GLOBAL_STATIC
Q_GLOBAL_STATIC(int, checkedBeforeInitialization)
void tst_QGlobalStatic::beforeInitialization()
{
QVERIFY(!checkedBeforeInitialization.exists());
QVERIFY(!checkedBeforeInitialization.isDestroyed());
}
struct Type {
int i;
};
Q_GLOBAL_STATIC(Type, checkedAfterInitialization)
void tst_QGlobalStatic::api()
{
// check the API
QVERIFY((Type *)checkedAfterInitialization);
QVERIFY(checkedAfterInitialization());
*checkedAfterInitialization = Type();
*checkedAfterInitialization() = Type();
checkedAfterInitialization()->i = 47;
checkedAfterInitialization->i = 42;
QCOMPARE(checkedAfterInitialization()->i, 42);
checkedAfterInitialization()->i = 47;
QCOMPARE(checkedAfterInitialization->i, 47);
QVERIFY(checkedAfterInitialization.exists());
QVERIFY(!checkedAfterInitialization.isDestroyed());
}
void tst_QGlobalStatic::constVolatile()
{
QCOMPARE(*constInt(), 42);
QCOMPARE((int)*volatileInt(), -47);
QCOMPARE(*constInt(), 42);
QCOMPARE((int)*volatileInt(), -47);
}
struct ThrowingType
{
static QBasicAtomicInt constructedCount;
static QBasicAtomicInt destructedCount;
ThrowingType()
{
throw 0;
}
ThrowingType(QBasicAtomicInt &throwControl)
{
constructedCount.ref();
if (throwControl.fetchAndAddRelaxed(-1) != 0)
throw 0;
}
~ThrowingType() { destructedCount.ref(); }
};
QBasicAtomicInt ThrowingType::constructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
QBasicAtomicInt ThrowingType::destructedCount = Q_BASIC_ATOMIC_INITIALIZER(0);
Q_GLOBAL_STATIC(ThrowingType, throwingGS)
void tst_QGlobalStatic::exception()
{
bool exceptionCaught = false;
try {
throwingGS();
} catch (int) {
exceptionCaught = true;
}
QVERIFY(exceptionCaught);
QCOMPARE(QtGlobalStatic::Holder<Q_QGS_throwingGS>::guard.loadRelaxed(), 0);
QVERIFY(!throwingGS.exists());
QVERIFY(!throwingGS.isDestroyed());
}
QBasicAtomicInt exceptionControlVar = Q_BASIC_ATOMIC_INITIALIZER(1);
Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, exceptionGS, (exceptionControlVar))
void tst_QGlobalStatic::catchExceptionAndRetry()
{
if (exceptionControlVar.loadRelaxed() != 1)
QSKIP("This test cannot be run more than once");
ThrowingType::constructedCount.storeRelaxed(0);
ThrowingType::destructedCount.storeRelaxed(0);
bool exceptionCaught = false;
try {
exceptionGS();
} catch (int) {
exceptionCaught = true;
}
QCOMPARE(ThrowingType::constructedCount.loadRelaxed(), 1);
QVERIFY(exceptionCaught);
exceptionGS();
QCOMPARE(ThrowingType::constructedCount.loadRelaxed(), 2);
}
QBasicAtomicInt threadStressTestControlVar = Q_BASIC_ATOMIC_INITIALIZER(5);
Q_GLOBAL_STATIC_WITH_ARGS(ThrowingType, threadStressTestGS, (threadStressTestControlVar))
void tst_QGlobalStatic::threadStressTest()
{
if (QTestPrivate::isRunningArmOnX86())
QSKIP("Frequently hangs on QEMU, QTBUG-91423");
class ThreadStressTestThread: public QThread
{
public:
QReadWriteLock *lock;
void run() override
{
QReadLocker l(lock);
//usleep(QRandomGenerator::global()->generate(200));
// thundering herd
try {
threadStressTestGS();
} catch (int) {
}
}
};
ThrowingType::constructedCount.storeRelaxed(0);
ThrowingType::destructedCount.storeRelaxed(0);
int expectedConstructionCount = threadStressTestControlVar.loadRelaxed() + 1;
if (expectedConstructionCount <= 0)
QSKIP("This test cannot be run more than once");
#ifdef Q_OS_INTEGRITY
// OPEN_REALTIME_THREADS = 123 on current INTEGRITY environment
// if try to create more, app is halted
const int numThreads = 122;
#else
const int numThreads = 200;
#endif
ThreadStressTestThread threads[numThreads];
QReadWriteLock lock;
lock.lockForWrite();
for (int i = 0; i < numThreads; ++i) {
threads[i].lock = &lock;
threads[i].start();
}
// wait for all threads
// release the herd
lock.unlock();
for (int i = 0; i < numThreads; ++i)
threads[i].wait();
QCOMPARE(ThrowingType::constructedCount.loadAcquire(), expectedConstructionCount);
QCOMPARE(ThrowingType::destructedCount.loadAcquire(), 0);
}
Q_GLOBAL_STATIC(int, checkedAfterDestruction)
void tst_QGlobalStatic::afterDestruction()
{
// this test will not produce results now
// it will simply run some code on destruction (after the global statics have been deleted)
// if that fails, this will cause a crash
// static destruction is LIFO: so we must add our exit-time code before the
// global static is used for the first time
static struct RunAtExit {
~RunAtExit() {
int *ptr = checkedAfterDestruction();
if (ptr)
qFatal("Global static is not null as was expected");
}
} runAtExit;
(void) runAtExit;
*checkedAfterDestruction = 42;
}
QTEST_APPLESS_MAIN(tst_QGlobalStatic);
#include "tst_qglobalstatic.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qhooks Test:
#####################################################################
qt_internal_add_test(tst_qhooks
SOURCES
tst_qhooks.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,122 @@
// Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Volker Krause <volker.krause@kdab.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtCore/private/qhooks_p.h>
class tst_QHooks: public QObject
{
Q_OBJECT
private slots:
void cleanup();
void testVersion();
void testAddRemoveObject();
void testChaining();
};
void tst_QHooks::cleanup()
{
qtHookData[QHooks::AddQObject] = 0;
qtHookData[QHooks::RemoveQObject] = 0;
}
void tst_QHooks::testVersion()
{
QVERIFY(qtHookData[QHooks::HookDataVersion] >= 3);
QCOMPARE(qtHookData[QHooks::HookDataSize], (quintptr)QHooks::LastHookIndex);
QCOMPARE(qtHookData[QHooks::QtVersion], (quintptr)QT_VERSION);
}
static int objectCount = 0;
static void objectAddHook(QObject*)
{
++objectCount;
}
static void objectRemoveHook(QObject*)
{
--objectCount;
}
void tst_QHooks::testAddRemoveObject()
{
QCOMPARE(qtHookData[QHooks::AddQObject], (quintptr)0);
QCOMPARE(qtHookData[QHooks::RemoveQObject], (quintptr)0);
qtHookData[QHooks::AddQObject] = (quintptr)&objectAddHook;
qtHookData[QHooks::RemoveQObject] = (quintptr)&objectRemoveHook;
QCOMPARE(objectCount, 0);
QObject *obj = new QObject;
QVERIFY(objectCount > 0);
delete obj;
QCOMPARE(objectCount, 0);
}
static QList<QString> hookOrder;
static QHooks::AddQObjectCallback existingAddHook = 0;
static QHooks::RemoveQObjectCallback existingRemoveHook = 0;
static void firstAddHook(QObject *)
{
hookOrder.append(QLatin1String("firstAddHook"));
}
static void firstRemoveHook(QObject *)
{
hookOrder.append(QLatin1String("firstRemoveHook"));
}
static void secondAddHook(QObject *object)
{
if (existingAddHook)
existingAddHook(object);
hookOrder.append(QLatin1String("secondAddHook"));
}
static void secondRemoveHook(QObject *object)
{
if (existingRemoveHook)
existingRemoveHook(object);
hookOrder.append(QLatin1String("secondRemoveHook"));
}
// Tests that it's possible to "chain" hooks together (i.e. have multiple hooks)
void tst_QHooks::testChaining()
{
QCOMPARE(qtHookData[QHooks::AddQObject], (quintptr)0);
QCOMPARE(qtHookData[QHooks::RemoveQObject], (quintptr)0);
// Set the add and remove hooks (could just skip this and go straight to the next step,
// but it's for illustrative purposes).
qtHookData[QHooks::AddQObject] = (quintptr)&firstAddHook;
qtHookData[QHooks::RemoveQObject] = (quintptr)&firstRemoveHook;
// Store them so that we can call them later.
existingAddHook = reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject]);
existingRemoveHook = reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject]);
// Overide them with hooks that call them first.
qtHookData[QHooks::AddQObject] = (quintptr)&secondAddHook;
qtHookData[QHooks::RemoveQObject] = (quintptr)&secondRemoveHook;
QObject *obj = new QObject;
QCOMPARE(hookOrder.size(), 2);
QCOMPARE(hookOrder.at(0), QLatin1String("firstAddHook"));
QCOMPARE(hookOrder.at(1), QLatin1String("secondAddHook"));
delete obj;
QCOMPARE(hookOrder.size(), 4);
QCOMPARE(hookOrder.at(2), QLatin1String("firstRemoveHook"));
QCOMPARE(hookOrder.at(3), QLatin1String("secondRemoveHook"));
hookOrder.clear();
}
QTEST_APPLESS_MAIN(tst_QHooks)
#include "tst_qhooks.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qkeycombination Test:
#####################################################################
qt_internal_add_test(tst_qkeycombination
SOURCES
tst_qkeycombination.cpp
)

View File

@ -0,0 +1,292 @@
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QObject>
#include <QTest>
class tst_QKeyCombination : public QObject
{
Q_OBJECT
private slots:
void construction();
void operator_eq();
void operator_or();
};
constexpr int bitwiseOr()
{
return 0;
}
template <typename ...T>
constexpr auto bitwiseOr(T ... args)
{
return (... | ((int)args));
}
void tst_QKeyCombination::construction()
{
{
QKeyCombination combination;
QCOMPARE(combination.key(), Qt::Key_unknown);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers{});
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::Key_unknown));
}
{
QKeyCombination combination(Qt::CTRL); // explicit
QCOMPARE(combination.key(), Qt::Key_unknown);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::ControlModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::ControlModifier, Qt::Key_unknown));
}
{
QKeyCombination combination(Qt::SHIFT); // explicit
QCOMPARE(combination.key(), Qt::Key_unknown);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::ShiftModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::ShiftModifier, Qt::Key_unknown));
}
{
QKeyCombination combination(Qt::AltModifier); // explicit
QCOMPARE(combination.key(), Qt::Key_unknown);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::AltModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::AltModifier, Qt::Key_unknown));
}
{
QKeyCombination combination(Qt::AltModifier | Qt::ControlModifier); // explicit
QCOMPARE(combination.key(), Qt::Key_unknown);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::AltModifier | Qt::ControlModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::AltModifier, Qt::ControlModifier, Qt::Key_unknown));
}
{
QKeyCombination combination = Qt::Key_A; // implicit
QCOMPARE(combination.key(), Qt::Key_A);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers{});
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::Key_A));
}
{
QKeyCombination combination = Qt::Key_F1; // implicit
QCOMPARE(combination.key(), Qt::Key_F1);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers{});
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::Key_F1));
}
{
QKeyCombination combination(Qt::SHIFT, Qt::Key_F1);
QCOMPARE(combination.key(), Qt::Key_F1);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::ShiftModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::SHIFT, Qt::Key_F1));
}
{
QKeyCombination combination(Qt::SHIFT | Qt::CTRL, Qt::Key_F1);
QCOMPARE(combination.key(), Qt::Key_F1);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::ShiftModifier | Qt::ControlModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::SHIFT, Qt::CTRL, Qt::Key_F1));
}
{
QKeyCombination combination(Qt::AltModifier, Qt::Key_F1);
QCOMPARE(combination.key(), Qt::Key_F1);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::AltModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::AltModifier, Qt::Key_F1));
}
// corner cases
{
QKeyCombination combination = Qt::Key_Alt;
QCOMPARE(combination.key(), Qt::Key_Alt);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers{});
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::Key_Alt));
}
{
QKeyCombination combination(Qt::ALT, Qt::Key_Alt);
QCOMPARE(combination.key(), Qt::Key_Alt);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::AltModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::ALT, Qt::Key_Alt));
}
{
QKeyCombination combination(Qt::Key_unknown);
QCOMPARE(combination.key(), Qt::Key_unknown);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers{});
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::Key_unknown));
}
{
QKeyCombination combination(Qt::CTRL | Qt::SHIFT, Qt::Key_unknown);
QCOMPARE(combination.key(), Qt::Key_unknown);
QCOMPARE(combination.keyboardModifiers(), Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier));
QCOMPARE(combination.toCombined(), bitwiseOr(Qt::CTRL, Qt::SHIFT, Qt::Key_unknown));
}
}
void tst_QKeyCombination::operator_eq()
{
// default
{
QKeyCombination a, b;
QVERIFY(a == b);
QVERIFY(!(a != b));
}
// key only
{
QKeyCombination a;
QKeyCombination b(Qt::Key_X);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::Key_Y);
QKeyCombination b;
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::Key_Y);
QKeyCombination b(Qt::Key_X);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::Key_F1);
QKeyCombination b(Qt::Key_F1);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
// modifier only
{
QKeyCombination a;
QKeyCombination b(Qt::CTRL);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::CTRL);
QKeyCombination b;
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::CTRL);
QKeyCombination b(Qt::SHIFT);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::CTRL);
QKeyCombination b(Qt::CTRL);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
{
QKeyCombination a(Qt::CTRL);
QKeyCombination b(Qt::ControlModifier);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
{
QKeyCombination a(Qt::ControlModifier);
QKeyCombination b(Qt::CTRL);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
{
QKeyCombination a(Qt::ControlModifier);
QKeyCombination b(Qt::ControlModifier);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
// key and modifier
{
QKeyCombination a(Qt::Key_A);
QKeyCombination b(Qt::SHIFT, Qt::Key_A);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::CTRL, Qt::Key_A);
QKeyCombination b(Qt::SHIFT, Qt::Key_A);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::SHIFT, Qt::Key_A);
QKeyCombination b(Qt::SHIFT, Qt::Key_A);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
{
QKeyCombination a(Qt::SHIFT, Qt::Key_A);
QKeyCombination b(Qt::SHIFT, Qt::Key_Escape);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::SHIFT, Qt::Key_A);
QKeyCombination b(Qt::ShiftModifier, Qt::Key_A);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
{
QKeyCombination a(Qt::SHIFT | Qt::CTRL, Qt::Key_A);
QKeyCombination b(Qt::ControlModifier | Qt::ShiftModifier, Qt::Key_A);
QVERIFY(a == b);
QVERIFY(!(a != b));
}
// corner cases
{
QKeyCombination a(Qt::CTRL);
QKeyCombination b(Qt::Key_Control);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
{
QKeyCombination a(Qt::ALT);
QKeyCombination b(Qt::Key_Alt);
QVERIFY(a != b);
QVERIFY(!(a == b));
}
}
void tst_QKeyCombination::operator_or()
{
// note tha operator+ between enumerators of the same enumeration
// yields int, so one can't do
// Qt::SHIFT + Qt::CTRL + Qt::Key_A
// but one can do
// Qt::SHIFT | Qt::CTRL | Qt::Key_A
QCOMPARE(Qt::SHIFT | Qt::Key_A, QKeyCombination(Qt::SHIFT, Qt::Key_A));
QCOMPARE(Qt::AltModifier | Qt::Key_F1, QKeyCombination(Qt::AltModifier, Qt::Key_F1));
QCOMPARE(Qt::SHIFT | Qt::ALT | Qt::Key_F1, QKeyCombination(Qt::SHIFT | Qt::ALT, Qt::Key_F1));
QCOMPARE(Qt::ControlModifier | Qt::Key_Escape, QKeyCombination(Qt::ControlModifier, Qt::Key_Escape));
}
QTEST_APPLESS_MAIN(tst_QKeyCombination)
#include "tst_qkeycombination.moc"

View File

@ -0,0 +1,9 @@
[qMessagePattern:backtrace]
# QTBUG-63915
b2qt 64bit
[qMessagePattern:backtrace depth,separator]
# QTBUG-63915
b2qt 64bit
# QTBUG-85364
b2qt cmake

View File

@ -0,0 +1,26 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_executable(qlogging_helper
NO_INSTALL
OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
SOURCES app/main.cpp
DEFINES QT_MESSAGELOGCONTEXT
LIBRARIES Qt::Core)
# Fixes required for the backtrace stack to be correct
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU" AND NOT MINGW)
target_link_options(qlogging_helper PRIVATE -rdynamic)
endif()
set_target_properties(qlogging_helper PROPERTIES CXX_VISIBILITY_PRESET default)
qt_internal_add_test(tst_qlogging SOURCES tst_qlogging.cpp
DEFINES
QT_MESSAGELOGCONTEXT
HELPER_BINARY="${CMAKE_CURRENT_BINARY_DIR}/qlogging_helper"
)
qt_internal_add_test(tst_qmessagelogger SOURCES tst_qmessagelogger.cpp
DEFINES
QT_MESSAGELOGCONTEXT
)

View File

@ -0,0 +1,63 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QCoreApplication>
#include <QLoggingCategory>
#ifdef Q_CC_GNU
#define NEVER_INLINE __attribute__((__noinline__))
#else
#define NEVER_INLINE
#endif
struct T {
T() { qDebug("static constructor"); }
~T() { qDebug("static destructor"); }
} t;
class MyClass : public QObject
{
Q_OBJECT
public slots:
virtual NEVER_INLINE QString mySlot1();
private:
virtual NEVER_INLINE void myFunction(int a);
};
QString MyClass::mySlot1()
{
myFunction(34);
return QString();
}
void MyClass::myFunction(int a)
{
qDebug() << "from_a_function" << a;
}
int main(int argc, char **argv)
{
QCoreApplication app(argc, argv);
app.setApplicationName("tst_qlogging");
qSetMessagePattern("[%{type}] %{message}");
qDebug("qDebug");
qInfo("qInfo");
qWarning("qWarning");
qCritical("qCritical");
QLoggingCategory cat("category");
qCWarning(cat) << "qDebug with category";
qSetMessagePattern(QString());
qDebug("qDebug2");
MyClass cl;
QMetaObject::invokeMethod(&cl, "mySlot1");
return 0;
}
#include "main.moc"

View File

@ -0,0 +1,988 @@
// Copyright (C) 2022 The Qt Company Ltd.
// Copyright (C) 2022 Intel Corporation.
// Copyright (C) 2014 Olivier Goffart <ogoffart@woboq.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <qdebug.h>
#include <qglobal.h>
#if QT_CONFIG(process)
# include <QtCore/QProcess>
#endif
#include <QtTest/QTest>
#include <QList>
#include <QMap>
class tst_qmessagehandler : public QObject
{
Q_OBJECT
public:
tst_qmessagehandler();
public slots:
void initTestCase();
private slots:
void cleanup();
void defaultHandler();
void installMessageHandler();
#ifdef QT_BUILD_INTERNAL
void cleanupFuncinfo_data();
void cleanupFuncinfo();
void cleanupFuncinfoBad_data();
void cleanupFuncinfoBad();
#endif
void qMessagePattern_data();
void qMessagePattern();
void setMessagePattern();
void formatLogMessage_data();
void formatLogMessage();
private:
QString backtraceHelperPath();
#if QT_CONFIG(process)
QProcessEnvironment m_baseEnvironment;
#endif
};
static QtMsgType s_type;
const char *s_file;
int s_line;
const char *s_function;
static QString s_message;
void customMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg)
{
s_type = type;
s_file = context.file;
s_line = context.line;
s_function = context.function;
s_message = msg;
}
tst_qmessagehandler::tst_qmessagehandler()
{
// ensure it's unset, otherwise we'll have trouble
qputenv("QT_MESSAGE_PATTERN", "");
}
void tst_qmessagehandler::initTestCase()
{
#if QT_CONFIG(process)
m_baseEnvironment = QProcessEnvironment::systemEnvironment();
m_baseEnvironment.remove("QT_MESSAGE_PATTERN");
m_baseEnvironment.insert("QT_FORCE_STDERR_LOGGING", "1");
#endif // QT_CONFIG(process)
}
void tst_qmessagehandler::cleanup()
{
qInstallMessageHandler((QtMessageHandler)0);
s_type = QtFatalMsg;
s_file = 0;
s_line = 0;
s_function = 0;
}
void tst_qmessagehandler::defaultHandler()
{
// check that the default works
QTest::ignoreMessage(QtDebugMsg, "defaultHandler");
qDebug("defaultHandler");
}
void tst_qmessagehandler::installMessageHandler()
{
QtMessageHandler oldHandler = qInstallMessageHandler(customMessageHandler);
qDebug("installMessageHandler"); int line = __LINE__;
QCOMPARE(s_type, QtDebugMsg);
QCOMPARE(s_message, QString::fromLocal8Bit("installMessageHandler"));
QCOMPARE(s_file, __FILE__);
QCOMPARE(s_function, Q_FUNC_INFO);
QCOMPARE(s_line, line);
QtMessageHandler myHandler = qInstallMessageHandler(oldHandler);
QCOMPARE((void*)myHandler, (void*)customMessageHandler);
}
# define ADD(x) QTest::newRow(x) << Q_FUNC_INFO << x;
class TestClass1
{
public:
enum Something { foo };
char c;
void func_void() { ADD("TestClass1::func_void"); }
int func_int() { ADD("TestClass1::func_int"); return 0; }
unsigned func_unsigned() { ADD("TestClass1::func_unsigned"); return 0; }
long func_long() { ADD("TestClass1::func_long"); return 0; }
long long func_ll() { ADD("TestClass1::func_ll"); return 0; }
unsigned long long func_ull() { ADD("TestClass1::func_ull"); return 0; }
char func_char() { ADD("TestClass1::func_char"); return 0; }
signed char func_schar() { ADD("TestClass1::func_schar"); return 0; }
unsigned char func_uchar() { ADD("TestClass1::func_uchar"); return 0; }
char &func_Rchar() { ADD("TestClass1::func_Rchar"); return c; }
char *func_Pchar() { ADD("TestClass1::func_Pchar"); return 0; }
const char *func_KPchar() { ADD("TestClass1::func_KPchar"); return 0; }
const volatile char *func_VKPchar() { ADD("TestClass1::func_VKPchar"); return 0; }
const volatile unsigned long long * func_KVPull() { ADD("TestClass1::func_KVPull"); return 0; }
const void * const volatile *func_KPKVvoid() { ADD("TestClass1::func_KPKVvoid"); return 0; }
QList<int> func_ai() { ADD("TestClass1::func_ai"); return QList<int>(); }
QList<unsigned long long const volatile*> func_aptr() { ADD("TestClass1::func_aptr"); return QList<unsigned long long const volatile*>(); }
QList<Something> func_aenum() { ADD("TestClass1::func_aenum"); return QList<Something>(); }
QList<QList<const void *> > func_aaptr() { ADD("TestClass1::func_aaptr"); return QList<QList<const void *> >(); }
QMap<int, Something> func_ienummap() { ADD("TestClass1::func_ienummap"); return QMap<int, Something>(); }
template<typename T>
T* func_template1() { ADD("TestClass1::func_template1"); return 0; }
template<Something val>
long func_template2() { ADD("TestClass1::func_template2"); return long(val); }
typedef unsigned long long * ( *fptr)();
typedef unsigned long long * (TestClass1::* pmf)();
typedef fptr (TestClass1::* uglypmf)();
fptr func_fptr() { ADD("TestClass1::func_fptr"); return 0; }
pmf func_pmf() { ADD("TestClass1::func_pmf"); return 0; }
uglypmf func_uglypmf(uglypmf = 0) { ADD("TestClass1::func_uglypmf"); return 0; }
QMap<QString, uglypmf> func_uglypmf2() { ADD("TestClass1::func_uglypmf2"); return QMap<QString, uglypmf>(); }
void operator()() { ADD("TestClass1::operator()"); }
int operator<(int) { ADD("TestClass1::operator<"); return 0; }
int operator>(int) { ADD("TestClass1::operator>"); return 0; }
int operator<=(int) { ADD("TestClass1::operator<="); return 0; }
int operator>=(int) { ADD("TestClass1::operator>="); return 0; }
int operator=(int) { ADD("TestClass1::operator="); return 0; }
int operator+(int) { ADD("TestClass1::operator+"); return 0; }
int operator-(int) { ADD("TestClass1::operator-"); return 0; }
int operator*(int) { ADD("TestClass1::operator*"); return 0; }
int operator/(int) { ADD("TestClass1::operator/"); return 0; }
int operator%(int) { ADD("TestClass1::operator%"); return 0; }
int x;
int &operator++() { ADD("TestClass1::operator++"); return x; }
int &operator--() { ADD("TestClass1::operator--"); return x; }
// slightly different to avoid duplicate test rows
#define ADD2(x) QTest::newRow(x ".postfix") << Q_FUNC_INFO << x;
int operator++(int) { ADD2("TestClass1::operator++"); return 0; }
int operator--(int) { ADD2("TestClass1::operator--"); return 0; }
#undef ADD2
int nested_struct()
{
struct Nested { void nested() { ADD("TestClass1::nested_struct"); } };
Nested().nested();
return 0;
}
int nested_struct_const() const
{
struct Nested { void nested() { ADD("TestClass1::nested_struct_const"); } };
Nested().nested();
return 0;
}
#ifdef Q_COMPILER_REF_QUALIFIERS
int lvalue() & { ADD("TestClass1::lvalue"); return 0; }
int const_lvalue() const & { ADD("TestClass1::const_lvalue"); return 0; }
int rvalue() && { ADD("TestClass1::rvalue"); return 0; }
int const_rvalue() const && { ADD("TestClass1::const_rvalue"); return 0; }
#endif
int decltype_param(int x = 0, decltype(x) = 0) { ADD("TestClass1::decltype_param"); return x; }
template<typename T> int decltype_template_param(T x = 0, decltype(x) = 0)
{ ADD("TestClass1::decltype_template_param"); return x; }
template<typename T> void decltype_template_param2(T x, decltype(x + QString()))
{ ADD("TestClass1::decltype_template_param2"); }
auto decltype_return(int x = 0) -> decltype(x)
{ ADD("TestClass1::decltype_return"); return x; }
template <typename T> auto decltype_template_return(T x = 0) -> decltype(x)
{ ADD("TestClass1::decltype_template_return"); return x; }
public:
TestClass1()
{
// instantiate
func_void();
func_int();
func_unsigned();
func_long();
func_ll();
func_ull();
func_char();
func_schar();
func_uchar();
func_Rchar();
func_Pchar();
func_KPchar();
func_VKPchar();
func_KVPull();
func_KPKVvoid();
func_ai();
func_aptr();
func_aenum();
func_aaptr();
func_ienummap();
func_template1<TestClass1>();
func_template2<foo>();
func_fptr();
func_pmf();
func_uglypmf();
func_uglypmf2();
operator()();
operator<(0);
operator>(0);
operator<=(0);
operator>=(0);
operator=(0);
operator+(0);
operator-(0);
operator*(0);
operator/(0);
operator%(0);
operator++();
operator++(0);
operator--();
operator--(0);
nested_struct();
nested_struct_const();
#ifdef Q_COMPILER_REF_QUALIFIERS
lvalue();
const_lvalue();
std::move(*this).rvalue();
std::move(*this).const_rvalue();
#endif
decltype_param();
decltype_template_param(0);
decltype_template_param2(QByteArray(), QString());
decltype_return();
decltype_template_return(0);
}
};
template<typename T> class TestClass2
{
long func_long() { ADD("TestClass2::func_long"); return 0; }
template<typename S>
T* func_template1() { ADD("TestClass2::func_template1"); return 0; }
template<TestClass1::Something val>
long func_template2() { ADD("TestClass2::func_template2"); return long(val); }
public:
TestClass2()
{
func_long();
func_template1<TestClass2>();
func_template2<TestClass1::foo>();
}
};
template<typename T, TestClass1::Something v> class TestClass3
{
long func_long() { ADD("TestClass3::func_long"); return 0; }
template<typename S>
S* func_template1() { ADD("TestClass3::func_template1"); return 0; }
template<TestClass1::Something val>
long func_template2() { ADD("TestClass3::func_template2"); return long(val); }
public:
struct Foo { TestClass3 foo; };
TestClass3()
{
func_long();
func_template1<TestClass2<T> >();
func_template2<TestClass1::foo>();
}
};
class TestClass4
{
TestClass1 c1;
TestClass2<std::map<long, const void *> > func2()
{ ADD("TestClass4::func2"); return TestClass2<std::map<long, const void *> >(); }
TestClass3<std::map<std::list<int>, const void *>, TestClass1::foo>::Foo func3()
{ ADD("TestClass4::func3"); return TestClass3<std::map<std::list<int>, const void *>, TestClass1::foo>::Foo(); }
public:
TestClass4()
{
func2();
func3();
ADD("TestClass4::TestClass4");
}
~TestClass4()
{
ADD("TestClass4::~TestClass4");
}
};
#ifdef QT_BUILD_INTERNAL
void tst_qmessagehandler::cleanupFuncinfo_data()
{
QTest::addColumn<QString>("funcinfo");
QTest::addColumn<QString>("expected");
TestClass4 c4;
QTest::newRow("msvc_01")
<< "void __thiscall TestClass1::func_void(void)"
<< "TestClass1::func_void";
QTest::newRow("gcc_01")
<< "void TestClass1::func_void()"
<< "TestClass1::func_void";
QTest::newRow("msvc_02")
<< "int __thiscall TestClass1::func_int(void)"
<< "TestClass1::func_int";
QTest::newRow("gcc_02")
<< "int TestClass1::func_int()"
<< "TestClass1::func_int";
QTest::newRow("msvc_03")
<< "unsigned int __thiscall TestClass1::func_unsigned(void)"
<< "TestClass1::func_unsigned";
QTest::newRow("gcc_03")
<< "unsigned int TestClass1::func_unsigned()"
<< "TestClass1::func_unsigned";
QTest::newRow("msvc_04")
<< "long __thiscall TestClass1::func_long(void)"
<< "TestClass1::func_long";
QTest::newRow("gcc_04")
<< "long int TestClass1::func_long()"
<< "TestClass1::func_long";
QTest::newRow("msvc_05")
<< "__int64 __thiscall TestClass1::func_ll(void)"
<< "TestClass1::func_ll";
QTest::newRow("gcc_05")
<< "long long int TestClass1::func_ll()"
<< "TestClass1::func_ll";
QTest::newRow("msvc_06")
<< "unsigned __int64 __thiscall TestClass1::func_ull(void)"
<< "TestClass1::func_ull";
QTest::newRow("gcc_06")
<< "long long unsigned int TestClass1::func_ull()"
<< "TestClass1::func_ull";
QTest::newRow("msvc_07")
<< "char __thiscall TestClass1::func_char(void)"
<< "TestClass1::func_char";
QTest::newRow("gcc_07")
<< "char TestClass1::func_char()"
<< "TestClass1::func_char";
QTest::newRow("msvc_08")
<< "signed char __thiscall TestClass1::func_schar(void)"
<< "TestClass1::func_schar";
QTest::newRow("gcc_08")
<< "signed char TestClass1::func_schar()"
<< "TestClass1::func_schar";
QTest::newRow("msvc_09")
<< "unsigned char __thiscall TestClass1::func_uchar(void)"
<< "TestClass1::func_uchar";
QTest::newRow("gcc_09")
<< "unsigned char TestClass1::func_uchar()"
<< "TestClass1::func_uchar";
QTest::newRow("msvc_09a")
<< "char &__thiscall TestClass1::func_Rchar(void)"
<< "TestClass1::func_Rchar";
QTest::newRow("gcc_09a")
<< "char& TestClass1::func_Rchar()"
<< "TestClass1::func_Rchar";
QTest::newRow("clang_09a")
<< "char &TestClass1::func_Rchar()"
<< "TestClass1::func_Rchar";
QTest::newRow("msvc_10")
<< "char *__thiscall TestClass1::func_Pchar(void)"
<< "TestClass1::func_Pchar";
QTest::newRow("gcc_10")
<< "char* TestClass1::func_Pchar()"
<< "TestClass1::func_Pchar";
QTest::newRow("clang_10")
<< "char *TestClass1::func_Pchar()"
<< "TestClass1::func_Pchar";
QTest::newRow("msvc_11")
<< "const char *__thiscall TestClass1::func_KPchar(void)"
<< "TestClass1::func_KPchar";
QTest::newRow("gcc_11")
<< "const char* TestClass1::func_KPchar()"
<< "TestClass1::func_KPchar";
QTest::newRow("msvc_12")
<< "volatile const char *__thiscall TestClass1::func_VKPchar(void)"
<< "TestClass1::func_VKPchar";
QTest::newRow("gcc_12")
<< "const volatile char* TestClass1::func_VKPchar()"
<< "TestClass1::func_VKPchar";
QTest::newRow("msvc_13")
<< "volatile const unsigned __int64 *__thiscall TestClass1::func_KVPull(void)"
<< "TestClass1::func_KVPull";
QTest::newRow("gcc_13")
<< "const volatile long long unsigned int* TestClass1::func_KVPull()"
<< "TestClass1::func_KVPull";
QTest::newRow("msvc_14")
<< "const void *volatile const *__thiscall TestClass1::func_KPKVvoid(void)"
<< "TestClass1::func_KPKVvoid";
QTest::newRow("gcc_14")
<< "const void* const volatile* TestClass1::func_KPKVvoid()"
<< "TestClass1::func_KPKVvoid";
QTest::newRow("msvc_15")
<< "class QList<int> __thiscall TestClass1::func_ai(void)"
<< "TestClass1::func_ai";
QTest::newRow("gcc_15")
<< "QList<int> TestClass1::func_ai()"
<< "TestClass1::func_ai";
QTest::newRow("msvc_16")
<< "class QList<unsigned __int64 const volatile *> __thiscall TestClass1::func_aptr(void)"
<< "TestClass1::func_aptr";
QTest::newRow("gcc_16")
<< "QList<const volatile long long unsigned int*> TestClass1::func_aptr()"
<< "TestClass1::func_aptr";
QTest::newRow("msvc_17")
<< "class QList<enum TestClass1::Something> __thiscall TestClass1::func_aenum(void)"
<< "TestClass1::func_aenum";
QTest::newRow("gcc_17")
<< "QList<TestClass1::Something> TestClass1::func_aenum()"
<< "TestClass1::func_aenum";
QTest::newRow("msvc_18")
<< "class QList<class QList<void const *> > __thiscall TestClass1::func_aaptr(void)"
<< "TestClass1::func_aaptr";
QTest::newRow("gcc_18")
<< "QList<QList<const void*> > TestClass1::func_aaptr()"
<< "TestClass1::func_aaptr";
QTest::newRow("msvc_19")
<< "class QMap<int,enum TestClass1::Something> __thiscall TestClass1::func_ienummap(void)"
<< "TestClass1::func_ienummap";
QTest::newRow("gcc_19")
<< "QMap<int, TestClass1::Something> TestClass1::func_ienummap()"
<< "TestClass1::func_ienummap";
QTest::newRow("msvc_20")
<< "class TestClass1 *__thiscall TestClass1::func_template1<class TestClass1>(void)"
<< "TestClass1::func_template1";
QTest::newRow("gcc_20")
<< "T* TestClass1::func_template1() [with T = TestClass1]"
<< "TestClass1::func_template1";
QTest::newRow("msvc_21")
<< "long __thiscall TestClass1::func_template2<foo>(void)"
<< "TestClass1::func_template2";
QTest::newRow("gcc_21")
<< "long int TestClass1::func_template2() [with TestClass1::Something val = foo]"
<< "TestClass1::func_template2";
QTest::newRow("msvc_22")
<< "unsigned __int64 *(__cdecl *__thiscall TestClass1::func_fptr(void))(void)"
<< "TestClass1::func_fptr";
QTest::newRow("gcc_22")
<< "long long unsigned int* (* TestClass1::func_fptr())()"
<< "TestClass1::func_fptr";
QTest::newRow("msvc_23")
<< "unsigned __int64 *(__thiscall TestClass1::* __thiscall TestClass1::func_pmf(void))(void)"
<< "TestClass1::func_pmf";
QTest::newRow("gcc_23")
<< "long long unsigned int* (TestClass1::* TestClass1::func_pmf())()"
<< "TestClass1::func_pmf";
QTest::newRow("msvc_24")
<< "unsigned __int64 *(__cdecl *(__thiscall TestClass1::* __thiscall TestClass1::func_uglypmf(unsigned __int64 *(__cdecl *(__thiscall TestClass1::* )(void))(void)))(void))(void)"
<< "TestClass1::func_uglypmf";
QTest::newRow("gcc_24")
<< "long long unsigned int* (* (TestClass1::* TestClass1::func_uglypmf(long long unsigned int* (* (TestClass1::*)())()))())()"
<< "TestClass1::func_uglypmf";
QTest::newRow("msvc_25")
<< "class QMap<class QString,unsigned __int64 * (__cdecl*(__thiscall TestClass1::*)(void))(void)> __thiscall TestClass1::func_uglypmf2(void)"
<< "TestClass1::func_uglypmf2";
QTest::newRow("gcc_25")
<< "QMap<QString, long long unsigned int* (* (TestClass1::*)())()> TestClass1::func_uglypmf2()"
<< "TestClass1::func_uglypmf2";
QTest::newRow("msvc_26")
<< "class TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > > __thiscall TestClass4::func2(void)"
<< "TestClass4::func2";
QTest::newRow("gcc_26")
<< "TestClass2<std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > > > TestClass4::func2()"
<< "TestClass4::func2";
QTest::newRow("msvc_27")
<< "long __thiscall TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >::func_long(void)"
<< "TestClass2::func_long";
QTest::newRow("gcc_27")
<< "long int TestClass2<T>::func_long() [with T = std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > >]"
<< "TestClass2::func_long";
QTest::newRow("msvc_28")
<< "class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > *__thiscall TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >::func_template1<class TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >>(void)"
<< "TestClass2::func_template1";
QTest::newRow("gcc_28")
<< "T* TestClass2<T>::func_template1() [with S = TestClass2<std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > > >, T = std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > >]"
<< "TestClass2::func_template1";
QTest::newRow("msvc_29")
<< "long __thiscall TestClass2<class std::map<long,void const *,struct std::less<long>,class std::allocator<struct std::pair<long const ,void const *> > > >::func_template2<foo>(void)"
<< "TestClass2::func_template2";
QTest::newRow("gcc_29")
<< "long int TestClass2<T>::func_template2() [with TestClass1::Something val = foo, T = std::map<long int, const void*, std::less<long int>, std::allocator<std::pair<const long int, const void*> > >]"
<< "TestClass2::func_template2";
QTest::newRow("msvc_30")
<< "struct TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::Foo __thiscall TestClass4::func3(void)"
<< "TestClass4::func3";
QTest::newRow("gcc_30")
<< "TestClass3<std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, foo>::Foo TestClass4::func3()"
<< "TestClass4::func3";
QTest::newRow("msvc_31")
<< "long __thiscall TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::func_long(void)"
<< "TestClass3::func_long";
QTest::newRow("gcc_31")
<< "long int TestClass3<T, v>::func_long() [with T = std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, TestClass1::Something v = foo]"
<< "TestClass3::func_long";
QTest::newRow("msvc_32")
<< "class TestClass2<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > > > *__thiscall TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::func_template1<class TestClass2<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > > >>(void)"
<< "TestClass3::func_template1";
QTest::newRow("gcc_32")
<< "S* TestClass3<T, v>::func_template1() [with S = TestClass2<std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > > >, T = std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, TestClass1::Something v = foo]"
<< "TestClass3::func_template1";
QTest::newRow("msvc_33")
<< "long __thiscall TestClass3<class std::map<class std::list<int,class std::allocator<int> >,void const *,struct std::less<class std::list<int,class std::allocator<int> > >,class std::allocator<struct std::pair<class std::list<int,class std::allocator<int> > const ,void const *> > >,0>::func_template2<foo>(void)"
<< "TestClass3::func_template2";
QTest::newRow("gcc_33")
<< "long int TestClass3<T, v>::func_template2() [with TestClass1::Something val = foo, T = std::map<std::list<int, std::allocator<int> >, const void*, std::less<std::list<int, std::allocator<int> > >, std::allocator<std::pair<const std::list<int, std::allocator<int> >, const void*> > >, TestClass1::Something v = foo]"
<< "TestClass3::func_template2";
QTest::newRow("msvc_34")
<< "__thiscall TestClass4::TestClass4(void)"
<< "TestClass4::TestClass4";
QTest::newRow("gcc_34")
<< "TestClass4::TestClass4()"
<< "TestClass4::TestClass4";
QTest::newRow("msvc_35")
<< "__thiscall TestClass4::~TestClass4(void)"
<< "TestClass4::~TestClass4";
QTest::newRow("gcc_35")
<< "TestClass4::~TestClass4()"
<< "TestClass4::~TestClass4";
QTest::newRow("gcc_36")
<< "void TestClass1::operator()()"
<< "TestClass1::operator()";
QTest::newRow("gcc_37")
<< "long int TestClass1::func_template2() [with TestClass1::Something val = (TestClass1::Something)0u]"
<< "TestClass1::func_template2";
QTest::newRow("gcc_38")
<< "int TestClass1::operator<(int)"
<< "TestClass1::operator<";
QTest::newRow("gcc_39")
<< "int TestClass1::operator>(int)"
<< "TestClass1::operator>";
QTest::newRow("gcc_40")
<< "Polymorphic<void (*)(int)>::~Polymorphic()"
<< "Polymorphic::~Polymorphic";
QTest::newRow("gcc_41")
<< "function<void (int*)>()::S::f()"
<< "function()::S::f";
QTest::newRow("msvc_41")
<< "void `void function<void __cdecl(int *)>(void)'::`2'::S::f(void)"
<< "function(void)'::`2'::S::f";
QTest::newRow("gcc_42")
<< "function<Polymorphic<void (int*)> >()::S::f(Polymorphic<void (int*)>*)"
<< "function()::S::f";
QTest::newRow("msvc_42")
<< "void `void function<Polymorphic<void __cdecl(int *)> >(void)'::`2'::S::f(Polymorphic<void __cdecl(int *)> *)"
<< "function(void)'::`2'::S::f";
QTest::newRow("objc_1")
<< "-[SomeClass someMethod:withArguments:]"
<< "-[SomeClass someMethod:withArguments:]";
QTest::newRow("objc_2")
<< "+[SomeClass withClassMethod:withArguments:]"
<< "+[SomeClass withClassMethod:withArguments:]";
QTest::newRow("objc_3")
<< "-[SomeClass someMethodWithoutArguments]"
<< "-[SomeClass someMethodWithoutArguments]";
QTest::newRow("objc_4")
<< "__31-[SomeClass someMethodSchedulingBlock]_block_invoke"
<< "__31-[SomeClass someMethodSchedulingBlock]_block_invoke";
QTest::newRow("thunk-1")
<< "non-virtual thunk to QFutureWatcherBasePrivate::postCallOutEvent(QFutureCallOutEvent const&)"
<< "QFutureWatcherBasePrivate::postCallOutEvent";
QTest::newRow("thunk-2")
<< "virtual thunk to std::basic_iostream<char, std::char_traits<char> >::~basic_iostream()"
<< "std::basic_iostream::~basic_iostream";
}
#endif
#ifdef QT_BUILD_INTERNAL
QT_BEGIN_NAMESPACE
extern QByteArray qCleanupFuncinfo(QByteArray);
QT_END_NAMESPACE
#endif
#ifdef QT_BUILD_INTERNAL
void tst_qmessagehandler::cleanupFuncinfo()
{
QFETCH(QString, funcinfo);
// qDebug() << funcinfo.toLatin1();
QByteArray result = qCleanupFuncinfo(funcinfo.toLatin1());
QEXPECT_FAIL("TestClass1::nested_struct", "Nested function processing is broken", Continue);
QEXPECT_FAIL("TestClass1::nested_struct_const", "Nested function processing is broken", Continue);
QTEST(QString::fromLatin1(result), "expected");
}
void tst_qmessagehandler::cleanupFuncinfoBad_data()
{
QTest::addColumn<QByteArray>("funcinfo");
auto addBadFrame = [i = 0](const char *symbol) mutable {
QTest::addRow("%d", ++i) << QByteArray(symbol);
};
addBadFrame("typeinfo for QEventLoop");
addBadFrame("typeinfo name for QtPrivate::ResultStoreBase");
addBadFrame("typeinfo name for ._anon_476");
addBadFrame("typeinfo name for std::__1::__function::__base<bool (void*, void*)>");
addBadFrame("vtable for BezierEase");
addBadFrame("vtable for Polymorphic<void ()>");
addBadFrame("vtable for Polymorphic<void (*)(int)>");
addBadFrame("TLS wrapper function for (anonymous namespace)::jitStacks");
addBadFrame("lcCheckIndex()::category");
addBadFrame("guard variable for lcEPDetach()::category");
addBadFrame("guard variable for QImageReader::read(QImage*)::disableNxImageLoading");
addBadFrame("VTT for std::__1::ostrstream");
addBadFrame("qIsRelocatable<(anonymous namespace)::Data>");
addBadFrame("qt_incomplete_metaTypeArray<(anonymous namespace)::qt_meta_stringdata_CLASSQNonContiguousByteDeviceIoDeviceImplENDCLASS_t, QtPrivate::TypeAndForceComplete<void, std::integral_constant<bool, true> > >");
addBadFrame("f()::i");
}
void tst_qmessagehandler::cleanupFuncinfoBad()
{
QFETCH(QByteArray, funcinfo);
// A corrupted stack trace may find non-sensical symbols that aren't
// functions. The result doesn't matter, so long as we don't crash or hang.
QByteArray result = qCleanupFuncinfo(funcinfo);
qDebug() << "Decode of" << funcinfo << "produced" << result;
}
#endif
void tst_qmessagehandler::qMessagePattern_data()
{
QTest::addColumn<QString>("pattern");
QTest::addColumn<bool>("valid");
QTest::addColumn<QList<QByteArray> >("expected");
// %{file} is tricky because of shadow builds
QTest::newRow("basic") << "%{type} %{appname} %{line} %{function} %{message}" << true << (QList<QByteArray>()
<< "debug 14 T::T static constructor"
// we can't be sure whether the QT_MESSAGE_PATTERN is already destructed
<< "static destructor"
<< "debug tst_qlogging 35 MyClass::myFunction from_a_function 34"
<< "debug tst_qlogging 45 main qDebug"
<< "info tst_qlogging 46 main qInfo"
<< "warning tst_qlogging 47 main qWarning"
<< "critical tst_qlogging 48 main qCritical"
<< "warning tst_qlogging 51 main qDebug with category"
<< "debug tst_qlogging 55 main qDebug2");
QTest::newRow("invalid") << "PREFIX: %{unknown} %{message}" << false << (QList<QByteArray>()
<< "QT_MESSAGE_PATTERN: Unknown placeholder %{unknown}"
<< "PREFIX: qDebug");
// test the if condition
QTest::newRow("ifs") << "[%{if-debug}D%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{if-category}%{category}: %{endif}%{message}"
<< true << (QList<QByteArray>()
<< "[D] static constructor"
// we can't be sure whether the QT_MESSAGE_PATTERN is already destructed
<< "static destructor"
<< "[D] qDebug"
<< "[W] qWarning"
<< "[C] qCritical"
<< "[W] category: qDebug with category"
<< "[D] qDebug2");
// test few errors cases
QTest::newRow("ifs-invalid1") << "PREFIX: %{unknown} %{endif} %{if-warning}"
<< false << (QList<QByteArray>()
<< "QT_MESSAGE_PATTERN: Unknown placeholder %{unknown}"
<< "QT_MESSAGE_PATTERN: %{endif} without an %{if-*}"
<< "QT_MESSAGE_PATTERN: missing %{endif}");
QTest::newRow("ifs-invalid2") << "A %{if-debug}DEBUG%{if-warning}WARNING%{endif} %{message} "
<< false << (QList<QByteArray>()
<< "QT_MESSAGE_PATTERN: %{if-*} cannot be nested"
<< "A DEBUG qDebug "
<< "A qWarning ");
QTest::newRow("pid-tid") << "%{pid}/%{threadid}: %{message}"
<< true << QList<QByteArray>(); // can't match anything, just test validity
QTest::newRow("qthreadptr") << "ThreadId:%{qthreadptr}: %{message}"
<< true << (QList<QByteArray>()
<< "ThreadId:0x");
// This test won't work when midnight is too close... wait a bit
while (QTime::currentTime() > QTime(23, 59, 30))
QTest::qWait(10000);
QTest::newRow("time") << "/%{time yyyy - MM - d}/%{message}"
<< true << (QList<QByteArray>()
<< ('/' + QDateTime::currentDateTime().toString("yyyy - MM - d").toLocal8Bit() + "/qDebug"));
QTest::newRow("time-time") << "/%{time yyyy - MM - d}/%{time dd-MM-yy}/%{message}"
<< true << (QList<QByteArray>()
<< ('/' + QDateTime::currentDateTime().toString("yyyy - MM - d").toLocal8Bit()
+ '/' + QDateTime::currentDateTime().toString("dd-MM-yy").toLocal8Bit()
+ "/qDebug"));
QTest::newRow("skipped-time-shown-time")
<< "/%{if-warning}%{time yyyy - MM - d}%{endif}%{if-debug}%{time dd-MM-yy}%{endif}/%{message}"
<< true << (QList<QByteArray>()
<< ('/' + QDateTime::currentDateTime().toString("dd-MM-yy").toLocal8Bit() + "/qDebug"));
// %{time} should have a padding of 6 so if it takes less than 10 seconds to show
// the first message, there should be 5 spaces
QTest::newRow("time-process") << "<%{time process}>%{message}" << true << (QList<QByteArray>()
<< "< ");
#define BACKTRACE_HELPER_NAME "qlogging_helper"
#ifdef QT_NAMESPACE
#define QT_NAMESPACE_STR QT_STRINGIFY(QT_NAMESPACE::)
#else
#define QT_NAMESPACE_STR ""
#endif
#ifdef __GLIBC__
# if QT_CONFIG(static)
// These test cases don't work with static Qt builds
# elif defined(QT_ASAN_ENABLED)
// These tests produce far more call frames under ASan
# else
# ifndef QT_NO_DEBUG
QList<QByteArray> expectedBacktrace = {
// MyClass::qt_static_metacall is explicitly marked as hidden in the
// Q_OBJECT macro hence the ?helper? frame
"[MyClass::myFunction|MyClass::mySlot1|?" BACKTRACE_HELPER_NAME "?|",
// QMetaObject::invokeMethodImpl calls internal function
// (QMetaMethodPrivate::invokeImpl, at the tims of this writing), which
// will usually show only as ?libQt6Core.so? or equivalent, so we skip
// end of backtrace, actual message
"|" QT_NAMESPACE_STR "QMetaObject::invokeMethodImpl] from_a_function 34"
};
QTest::newRow("backtrace") << "[%{backtrace}] %{message}" << true << expectedBacktrace;
# endif
QTest::newRow("backtrace depth,separator") << "[%{backtrace depth=2 separator=\"\n\"}] %{message}" << true << (QList<QByteArray>()
<< "[MyClass::myFunction\nMyClass::mySlot1] from_a_function 34"
<< "[T::T\n");
# endif // #if !QT_CONFIG(static)
#endif // #ifdef __GLIBC__
}
void tst_qmessagehandler::qMessagePattern()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
#ifdef Q_OS_ANDROID
QSKIP("This test crashes on Android");
#endif
QFETCH(QString, pattern);
QFETCH(bool, valid);
QFETCH(QList<QByteArray>, expected);
QProcess process;
const QString appExe(backtraceHelperPath());
//
// test QT_MESSAGE_PATTERN
//
QProcessEnvironment environment = m_baseEnvironment;
environment.insert("QT_MESSAGE_PATTERN", pattern);
process.setProcessEnvironment(environment);
process.start(appExe);
QVERIFY2(process.waitForStarted(), qPrintable(
QString::fromLatin1("Could not start %1: %2").arg(appExe, process.errorString())));
QByteArray pid = QByteArray::number(process.processId());
process.waitForFinished();
QByteArray output = process.readAllStandardError();
// qDebug() << output;
QVERIFY(!output.isEmpty());
QCOMPARE(!output.contains("QT_MESSAGE_PATTERN"), valid);
for (const QByteArray &e : std::as_const(expected)) {
if (!output.contains(e)) {
// use QDebug so we get proper string escaping for the newlines
QString buf;
QDebug(&buf) << "Got:" << output << "; Expected:" << e;
QVERIFY2(output.contains(e), qPrintable(buf));
}
}
if (pattern.startsWith("%{pid}"))
QVERIFY2(output.startsWith(pid), "PID: " + pid + "\noutput:\n" + output);
#endif
}
void tst_qmessagehandler::setMessagePattern()
{
#if !QT_CONFIG(process)
QSKIP("This test requires QProcess support");
#else
#ifdef Q_OS_ANDROID
QSKIP("This test crashes on Android");
#endif
//
// test qSetMessagePattern
//
QProcess process;
const QString appExe(backtraceHelperPath());
// make sure there is no QT_MESSAGE_PATTERN in the environment
process.setProcessEnvironment(m_baseEnvironment);
process.start(appExe);
QVERIFY2(process.waitForStarted(), qPrintable(
QString::fromLatin1("Could not start %1: %2").arg(appExe, process.errorString())));
process.waitForFinished();
QByteArray output = process.readAllStandardError();
//qDebug() << output;
QByteArray expected = "static constructor\n"
"[debug] qDebug\n"
"[info] qInfo\n"
"[warning] qWarning\n"
"[critical] qCritical\n"
"[warning] qDebug with category\n";
#ifdef Q_OS_WIN
output.replace("\r\n", "\n");
#endif
QCOMPARE(QString::fromLatin1(output), QString::fromLatin1(expected));
#endif // QT_CONFIG(process)
}
Q_DECLARE_METATYPE(QtMsgType)
void tst_qmessagehandler::formatLogMessage_data()
{
QTest::addColumn<QString>("pattern");
QTest::addColumn<QString>("result");
QTest::addColumn<QtMsgType>("type");
QTest::addColumn<QByteArray>("file");
QTest::addColumn<int>("line");
QTest::addColumn<QByteArray>("function");
QTest::addColumn<QByteArray>("category");
QTest::addColumn<QString>("message");
#define BA QByteArrayLiteral
QTest::newRow("basic") << "%{type} %{file} %{line} %{function} %{message}"
<< "debug main.cpp 1 func msg"
<< QtDebugMsg << BA("main.cpp") << 1 << BA("func") << BA("") << "msg";
// test the if conditions
QString format = "[%{if-debug}D%{endif}%{if-info}I%{endif}%{if-warning}W%{endif}%{if-critical}C%{endif}%{if-fatal}F%{endif}] %{if-category}%{category}: %{endif}%{message}";
QTest::newRow("if-debug")
<< format << "[D] msg"
<< QtDebugMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
QTest::newRow("if_info")
<< format << "[I] msg"
<< QtInfoMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
QTest::newRow("if_warning")
<< format << "[W] msg"
<< QtWarningMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
QTest::newRow("if_critical")
<< format << "[C] msg"
<< QtCriticalMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
QTest::newRow("if_fatal")
<< format << "[F] msg"
<< QtFatalMsg << BA("") << 0 << BA("func") << QByteArray() << "msg";
QTest::newRow("if_cat")
#ifndef Q_OS_ANDROID
<< format << "[F] cat: msg"
#else
<< format << "[F] : msg"
#endif
<< QtFatalMsg << BA("") << 0 << BA("func") << BA("cat") << "msg";
}
void tst_qmessagehandler::formatLogMessage()
{
QFETCH(QString, pattern);
QFETCH(QString, result);
QFETCH(QtMsgType, type);
QFETCH(QByteArray, file);
QFETCH(int, line);
QFETCH(QByteArray, function);
QFETCH(QByteArray, category);
QFETCH(QString, message);
qSetMessagePattern(pattern);
QMessageLogContext ctxt(file, line, function, category.isEmpty() ? 0 : category.data());
QString r = qFormatLogMessage(type, ctxt, message);
QCOMPARE(r, result);
}
QString tst_qmessagehandler::backtraceHelperPath()
{
#ifdef Q_OS_ANDROID
QString appExe(QCoreApplication::applicationDirPath()
+ QLatin1String("/lib" BACKTRACE_HELPER_NAME ".so"));
#elif defined(Q_OS_WEBOS)
QString appExe(QCoreApplication::applicationDirPath()
+ QLatin1String("/" BACKTRACE_HELPER_NAME));
#else
QString appExe(QLatin1String(HELPER_BINARY));
#endif
return appExe;
}
QTEST_MAIN(tst_qmessagehandler)
#include "tst_qlogging.moc"

View File

@ -0,0 +1,334 @@
// 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 <qlogging.h>
#include <qloggingcategory.h>
#include <QtTest/QTest>
Q_LOGGING_CATEGORY(debugTestCategory, "debug", QtDebugMsg)
Q_LOGGING_CATEGORY(infoTestCategory, "info", QtInfoMsg)
Q_LOGGING_CATEGORY(warningTestCategory, "warning", QtWarningMsg)
Q_LOGGING_CATEGORY(criticalTestCategory, "critical", QtCriticalMsg)
struct LoggerMessageInfo
{
QtMsgType messageType { QtFatalMsg };
QString message;
const char *file { nullptr };
int line { 0 };
const char *function { nullptr };
const char *category { nullptr };
};
LoggerMessageInfo messageInfo;
static void customMessageHandler(QtMsgType type, const QMessageLogContext &context,
const QString &message)
{
messageInfo.messageType = type;
messageInfo.message = message;
messageInfo.file = context.file;
messageInfo.line = context.line;
messageInfo.function = context.function;
messageInfo.category = context.category;
}
class tst_QMessageLogger : public QObject
{
Q_OBJECT
private slots:
void initTestCase_data();
void init();
void cleanup();
void logMessage();
void logMessageWithLoggingCategory();
void logMessageWithLoggingCategoryDisabled();
void logMessageWithCategoryFunction();
void logMessageWithNoDebug();
private:
void logWithLoggingCategoryHelper(bool messageTypeEnabled);
};
void tst_QMessageLogger::initTestCase_data()
{
QTest::addColumn<QtMsgType>("messageType");
QTest::addColumn<QByteArray>("categoryName");
QTest::addColumn<QByteArray>("messageText");
QTest::addColumn<bool>("useDebugStream");
// not testing QtFatalMsg, as it terminates the application
QTest::newRow("debug") << QtDebugMsg << QByteArray("categoryDebug")
<< QByteArray("debug message") << false;
QTest::newRow("info") << QtInfoMsg << QByteArray("categoryInfo") << QByteArray("info message")
<< false;
QTest::newRow("warning") << QtWarningMsg << QByteArray("categoryWarning")
<< QByteArray("warning message") << false;
QTest::newRow("critical") << QtCriticalMsg << QByteArray("categoryCritical")
<< QByteArray("critical message") << false;
#ifndef QT_NO_DEBUG_STREAM
QTest::newRow("stream debug") << QtDebugMsg << QByteArray("categoryDebug")
<< QByteArray("debug message") << true;
QTest::newRow("stream info") << QtInfoMsg << QByteArray("categoryInfo")
<< QByteArray("info message") << true;
QTest::newRow("stream warning") << QtWarningMsg << QByteArray("categoryWarning")
<< QByteArray("warning message") << true;
QTest::newRow("stream critical") << QtCriticalMsg << QByteArray("categoryCritical")
<< QByteArray("critical message") << true;
#endif
}
void tst_QMessageLogger::init()
{
qInstallMessageHandler(customMessageHandler);
}
void tst_QMessageLogger::cleanup()
{
qInstallMessageHandler((QtMessageHandler)0);
messageInfo.messageType = QtFatalMsg;
messageInfo.message.clear();
messageInfo.file = nullptr;
messageInfo.line = 0;
messageInfo.function = nullptr;
messageInfo.category = nullptr;
}
void tst_QMessageLogger::logMessage()
{
const int line = QT_MESSAGELOG_LINE;
QMessageLogger logger(QT_MESSAGELOG_FILE, line, QT_MESSAGELOG_FUNC);
QFETCH_GLOBAL(QtMsgType, messageType);
QFETCH_GLOBAL(QByteArray, messageText);
QFETCH_GLOBAL(bool, useDebugStream);
if (useDebugStream) {
#ifndef QT_NO_DEBUG_STREAM
switch (messageType) {
case QtDebugMsg:
logger.debug().noquote() << messageText;
break;
case QtInfoMsg:
logger.info().noquote() << messageText;
break;
case QtWarningMsg:
logger.warning().noquote() << messageText;
break;
case QtCriticalMsg:
logger.critical().noquote() << messageText;
break;
default:
QFAIL("Invalid message type");
break;
}
#else
QSKIP("Qt debug stream disabled");
#endif
} else {
switch (messageType) {
case QtDebugMsg:
logger.debug("%s", messageText.constData());
break;
case QtInfoMsg:
logger.info("%s", messageText.constData());
break;
case QtWarningMsg:
logger.warning("%s", messageText.constData());
break;
case QtCriticalMsg:
logger.critical("%s", messageText.constData());
break;
default:
QFAIL("Invalid message type");
break;
}
}
QCOMPARE(messageInfo.messageType, messageType);
QCOMPARE(messageInfo.message, messageText);
QCOMPARE(messageInfo.file, __FILE__);
QCOMPARE(messageInfo.line, line);
QCOMPARE(messageInfo.function, Q_FUNC_INFO);
}
void tst_QMessageLogger::logMessageWithLoggingCategory()
{
logWithLoggingCategoryHelper(true);
}
void tst_QMessageLogger::logMessageWithLoggingCategoryDisabled()
{
logWithLoggingCategoryHelper(false);
}
void tst_QMessageLogger::logMessageWithCategoryFunction()
{
const int line = QT_MESSAGELOG_LINE;
QMessageLogger logger(QT_MESSAGELOG_FILE, line, QT_MESSAGELOG_FUNC);
const QLoggingCategory *category = nullptr;
QFETCH_GLOBAL(QtMsgType, messageType);
QFETCH_GLOBAL(QByteArray, messageText);
QFETCH_GLOBAL(bool, useDebugStream);
if (useDebugStream) {
#ifndef QT_NO_DEBUG_STREAM
switch (messageType) {
case QtDebugMsg:
logger.debug(debugTestCategory()).noquote() << messageText;
category = &debugTestCategory();
break;
case QtInfoMsg:
logger.info(infoTestCategory()).noquote() << messageText;
category = &infoTestCategory();
break;
case QtWarningMsg:
logger.warning(warningTestCategory()).noquote() << messageText;
category = &warningTestCategory();
break;
case QtCriticalMsg:
logger.critical(criticalTestCategory()).noquote() << messageText;
category = &criticalTestCategory();
break;
default:
QFAIL("Invalid message type");
break;
}
#else
QSKIP("Qt debug stream disabled");
#endif
} else {
switch (messageType) {
case QtDebugMsg:
logger.debug(debugTestCategory(), "%s", messageText.constData());
category = &debugTestCategory();
break;
case QtInfoMsg:
logger.info(infoTestCategory(), "%s", messageText.constData());
category = &infoTestCategory();
break;
case QtWarningMsg:
logger.warning(warningTestCategory(), "%s", messageText.constData());
category = &warningTestCategory();
break;
case QtCriticalMsg:
logger.critical(criticalTestCategory(), "%s", messageText.constData());
category = &criticalTestCategory();
break;
default:
QFAIL("Invalid message type");
break;
}
}
QCOMPARE(messageInfo.messageType, messageType);
QCOMPARE(messageInfo.message, messageText);
QCOMPARE(messageInfo.file, __FILE__);
QCOMPARE(messageInfo.line, line);
QCOMPARE(messageInfo.function, Q_FUNC_INFO);
QCOMPARE(messageInfo.category, category->categoryName());
}
void tst_QMessageLogger::logMessageWithNoDebug()
{
const int line = QT_MESSAGELOG_LINE;
QMessageLogger logger(QT_MESSAGELOG_FILE, line, QT_MESSAGELOG_FUNC);
QFETCH_GLOBAL(QByteArray, messageText);
QFETCH_GLOBAL(bool, useDebugStream);
if (useDebugStream) {
#ifndef QT_NO_DEBUG_STREAM
logger.noDebug().noquote() << messageText;
#else
QSKIP("Qt debug stream disabled");
#endif
} else {
logger.noDebug("%s", messageText.constData());
}
// the callback was not called
QVERIFY(messageInfo.messageType == QtFatalMsg);
QVERIFY(messageInfo.message.isEmpty());
QVERIFY(messageInfo.file == nullptr);
QVERIFY(messageInfo.line == 0);
QVERIFY(messageInfo.function == nullptr);
QVERIFY(messageInfo.category == nullptr);
}
void tst_QMessageLogger::logWithLoggingCategoryHelper(bool messageTypeEnabled)
{
QFETCH_GLOBAL(QtMsgType, messageType);
QFETCH_GLOBAL(QByteArray, categoryName);
QLoggingCategory category(categoryName.constData(), messageType);
if (!messageTypeEnabled)
category.setEnabled(messageType, false);
const int line = QT_MESSAGELOG_LINE;
QMessageLogger logger(QT_MESSAGELOG_FILE, line, QT_MESSAGELOG_FUNC);
QFETCH_GLOBAL(QByteArray, messageText);
QFETCH_GLOBAL(bool, useDebugStream);
if (useDebugStream) {
#ifndef QT_NO_DEBUG_STREAM
switch (messageType) {
case QtDebugMsg:
logger.debug(category).noquote() << messageText;
break;
case QtInfoMsg:
logger.info(category).noquote() << messageText;
break;
case QtWarningMsg:
logger.warning(category).noquote() << messageText;
break;
case QtCriticalMsg:
logger.critical(category).noquote() << messageText;
break;
default:
QFAIL("Invalid message type");
break;
}
#else
QSKIP("Qt debug stream disabled");
#endif
} else {
switch (messageType) {
case QtDebugMsg:
logger.debug(category, "%s", messageText.constData());
break;
case QtInfoMsg:
logger.info(category, "%s", messageText.constData());
break;
case QtWarningMsg:
logger.warning(category, "%s", messageText.constData());
break;
case QtCriticalMsg:
logger.critical(category, "%s", messageText.constData());
break;
default:
QFAIL("Invalid message type");
break;
}
}
if (messageTypeEnabled) {
QCOMPARE(messageInfo.messageType, messageType);
QCOMPARE(messageInfo.message, messageText);
QCOMPARE(messageInfo.file, __FILE__);
QCOMPARE(messageInfo.line, line);
QCOMPARE(messageInfo.function, Q_FUNC_INFO);
QCOMPARE(messageInfo.category, categoryName);
} else {
// the callback was not called
QVERIFY(messageInfo.messageType == QtFatalMsg);
QVERIFY(messageInfo.message.isEmpty());
QVERIFY(messageInfo.file == nullptr);
QVERIFY(messageInfo.line == 0);
QVERIFY(messageInfo.function == nullptr);
QVERIFY(messageInfo.category == nullptr);
}
}
QTEST_MAIN(tst_QMessageLogger)
#include "tst_qmessagelogger.moc"

View File

@ -0,0 +1,9 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_test(tst_qnativeinterface
SOURCES
tst_qnativeinterface.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,115 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtCore/qnativeinterface.h>
#include <QtCore/private/qnativeinterface_p.h>
class tst_QNativeInterface: public QObject
{
Q_OBJECT
private slots:
void typeInfo() const;
void resolve() const;
void accessor() const;
friend struct PublicClass;
};
struct InterfaceImplementation;
struct PublicClass
{
PublicClass();
QT_DECLARE_NATIVE_INTERFACE_ACCESSOR(PublicClass)
std::unique_ptr<InterfaceImplementation> m_implementation;
friend void tst_QNativeInterface::resolve() const;
};
QT_BEGIN_NAMESPACE
namespace QNativeInterface {
struct Interface
{
QT_DECLARE_NATIVE_INTERFACE(Interface, 10, PublicClass)
virtual int foo() = 0;
};
struct OtherInterface
{
QT_DECLARE_NATIVE_INTERFACE(OtherInterface, 10, PublicClass)
};
}
QT_DEFINE_NATIVE_INTERFACE(Interface);
QT_DEFINE_NATIVE_INTERFACE(OtherInterface);
QT_END_NAMESPACE
struct NotInterface {};
struct AlmostInterface
{
struct TypeInfo {
// Missing required members
};
};
using namespace QNativeInterface;
struct InterfaceImplementation : public Interface
{
int foo() override { return 123; }
};
PublicClass::PublicClass() : m_implementation(new InterfaceImplementation) {}
void* PublicClass::resolveInterface(char const* name, int revision) const
{
auto *implementation = m_implementation.get();
QT_NATIVE_INTERFACE_RETURN_IF(Interface, implementation);
QT_NATIVE_INTERFACE_RETURN_IF(OtherInterface, implementation);
return nullptr;
}
void tst_QNativeInterface::typeInfo() const
{
using namespace QNativeInterface::Private;
QCOMPARE(TypeInfo<Interface>::haveTypeInfo, true);
QCOMPARE(TypeInfo<NotInterface>::haveTypeInfo, false);
QCOMPARE(TypeInfo<AlmostInterface>::haveTypeInfo, false);
QCOMPARE(TypeInfo<Interface>::isCompatibleWith<PublicClass>, true);
QCOMPARE(TypeInfo<Interface>::isCompatibleWith<QObject>, false);
QCOMPARE(TypeInfo<Interface>::isCompatibleWith<int>, false);
QCOMPARE(TypeInfo<Interface>::revision(), 10);
QCOMPARE(TypeInfo<Interface>::name(), "Interface");
}
void tst_QNativeInterface::resolve() const
{
using namespace QNativeInterface::Private;
PublicClass foo;
QVERIFY(foo.resolveInterface("Interface", 10));
QTest::ignoreMessage(QtWarningMsg, "Native interface revision mismatch "
"(requested 5 / available 10) for interface Interface");
QCOMPARE(foo.resolveInterface("Interface", 5), nullptr);
QCOMPARE(foo.resolveInterface("NotInterface", 10), nullptr);
QCOMPARE(foo.resolveInterface("OtherInterface", 10), nullptr);
}
void tst_QNativeInterface::accessor() const
{
PublicClass foo;
QVERIFY(foo.nativeInterface<Interface>());
QCOMPARE(foo.nativeInterface<Interface>()->foo(), 123);
}
QTEST_MAIN(tst_QNativeInterface)
#include "tst_qnativeinterface.moc"

View File

@ -0,0 +1,16 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnumeric Test:
#####################################################################
qt_internal_add_test(tst_qnumeric
SOURCES
tst_qnumeric.cpp
LIBRARIES
Qt::CorePrivate
)
## Scopes:
#####################################################################

View File

@ -0,0 +1,751 @@
// Copyright (C) 2022 The Qt Company Ltd.
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtGlobal>
#include "private/qnumeric_p.h"
#include <math.h>
#include <float.h>
namespace {
template <typename F> struct Fuzzy {};
/* Data taken from qglobal.h's implementation of qFuzzyCompare:
* qFuzzyCompare conflates values with fractional difference up to (and
* including) the given scale.
*/
template <> struct Fuzzy<double> { constexpr static double scale = 1e12; };
template <> struct Fuzzy<float> { constexpr static float scale = 1e5f; };
}
class tst_QNumeric: public QObject
{
Q_OBJECT
// Support for floating-point:
template<typename F> inline void fuzzyCompare_data();
template<typename F> inline void fuzzyCompare();
template<typename F> inline void fuzzyIsNull_data();
template<typename F> inline void fuzzyIsNull();
template<typename F> inline void checkNaN(F nan);
template<typename F> inline void rawNaN_data();
template<typename F> inline void rawNaN();
#if QT_CONFIG(signaling_nan)
template<typename F> inline void distinctNaN();
#endif
template<typename F, typename Whole> inline void generalNaN_data();
template<typename F, typename Whole> inline void generalNaN();
template<typename F> inline void infinity();
template<typename F> inline void classifyfp();
template<typename F, typename Count> inline void distance_data();
template<typename F, typename Count> inline void distance();
private slots:
// Floating-point tests:
void fuzzyCompareF_data() { fuzzyCompare_data<float>(); }
void fuzzyCompareF() { fuzzyCompare<float>(); }
void fuzzyCompareD_data() { fuzzyCompare_data<double>(); }
void fuzzyCompareD() { fuzzyCompare<double>(); }
void fuzzyIsNullF_data() { fuzzyIsNull_data<float>(); }
void fuzzyIsNullF() { fuzzyIsNull<float>(); }
void fuzzyIsNullD_data() { fuzzyIsNull_data<double>(); }
void fuzzyIsNullD() { fuzzyIsNull<double>(); }
void rawNaNF_data() { rawNaN_data<float>(); }
void rawNaNF() { rawNaN<float>(); }
void rawNaND_data() { rawNaN_data<double>(); }
void rawNaND() { rawNaN<double>(); }
#if QT_CONFIG(signaling_nan)
void distinctNaNF();
void distinctNaND() { distinctNaN<double>(); }
#endif
void generalNaNd_data() { generalNaN_data<double, quint64>(); }
void generalNaNd() { generalNaN<double, quint64>(); }
void generalNaNf_data() { generalNaN_data<float, quint32>(); }
void generalNaNf() { generalNaN<float, quint32>(); }
void infinityF() { infinity<float>(); }
void infinityD() { infinity<double>(); }
void classifyF() { classifyfp<float>(); }
void classifyD() { classifyfp<double>(); }
void floatDistance_data() { distance_data<float, quint32>(); }
void floatDistance() { distance<float, quint32>(); }
void doubleDistance_data() { distance_data<double, quint64>(); }
void doubleDistance() { distance<double, quint64>(); }
// Whole number tests:
void addOverflow_data();
void addOverflow();
void mulOverflow_data();
void mulOverflow();
void signedOverflow();
};
// Floating-point tests:
template<typename F>
void tst_QNumeric::fuzzyCompare_data()
{
QTest::addColumn<F>("val1");
QTest::addColumn<F>("val2");
QTest::addColumn<bool>("isEqual");
const F zero(0), one(1), ten(10);
const F huge = Fuzzy<F>::scale, tiny = one / huge;
const F deci(.1), giga(1e9), nano(1e-9), big(1e7), small(1e-10);
QTest::newRow("zero") << zero << zero << true;
QTest::newRow("ten") << ten << ten << true;
QTest::newRow("large") << giga << giga << true;
QTest::newRow("small") << small << small << true;
QTest::newRow("10+9*tiny==10") << (ten + 9 * tiny) << ten << true;
QTest::newRow("huge+.9==huge") << (huge + 9 * deci) << huge << true;
QTest::newRow("eps2") << (ten + tiny) << (ten + 2 * tiny) << true;
QTest::newRow("eps9") << (ten + tiny) << (ten + 9 * tiny) << true;
QTest::newRow("0!=1") << zero << one << false;
QTest::newRow("0!=big") << zero << big << false;
QTest::newRow("0!=nano") << zero << nano << false;
QTest::newRow("giga!=nano") << giga << nano << false;
QTest::newRow("small!=nano") << small << nano << false;
QTest::newRow("huge+1.1!=huge") << (huge + 1 + deci) << huge << false;
QTest::newRow("1+1.1*tiny!=1") << (one + tiny * (one + deci)) << one << false;
}
template<typename F>
void tst_QNumeric::fuzzyCompare()
{
QFETCH(F, val1);
QFETCH(F, val2);
QFETCH(bool, isEqual);
QCOMPARE(::qFuzzyCompare(val1, val2), isEqual);
QCOMPARE(::qFuzzyCompare(val2, val1), isEqual);
QCOMPARE(::qFuzzyCompare(-val1, -val2), isEqual);
QCOMPARE(::qFuzzyCompare(-val2, -val1), isEqual);
}
template<typename F>
void tst_QNumeric::fuzzyIsNull_data()
{
QTest::addColumn<F>("value");
QTest::addColumn<bool>("isNull");
using Bounds = std::numeric_limits<F>;
const F one(1), huge = Fuzzy<F>::scale, tiny = one / huge;
QTest::newRow("zero") << F(0) << true;
QTest::newRow("min") << Bounds::min() << true;
QTest::newRow("denorm_min") << Bounds::denorm_min() << true;
QTest::newRow("tiny") << tiny << true;
QTest::newRow("deci") << F(.1) << false;
QTest::newRow("one") << one << false;
QTest::newRow("ten") << F(10) << false;
QTest::newRow("large") << F(1e9) << false;
QTest::newRow("huge") << huge << false;
}
template<typename F>
void tst_QNumeric::fuzzyIsNull()
{
QFETCH(F, value);
QFETCH(bool, isNull);
QCOMPARE(::qFuzzyIsNull(value), isNull);
QCOMPARE(::qFuzzyIsNull(-value), isNull);
}
static void clearFpExceptions()
{
// Call after any functions that exercise floating-point exceptions, such as
// sqrt(-1) or log(0).
#ifdef Q_OS_WIN
_clearfp();
#endif
}
#if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)
// turn -ffast-math off
# pragma GCC optimize "no-fast-math"
#endif
template<typename F>
void tst_QNumeric::checkNaN(F nan)
{
const auto cleanup = qScopeGuard([]() { clearFpExceptions(); });
#define CHECKNAN(value) \
do { \
const F v = (value); \
QCOMPARE(qFpClassify(v), FP_NAN); \
QVERIFY(qIsNaN(v)); \
QVERIFY(!qIsFinite(v)); \
QVERIFY(!qIsInf(v)); \
} while (0)
const F zero(0), one(1), two(2);
QVERIFY(!(zero > nan));
QVERIFY(!(zero < nan));
QVERIFY(!(zero == nan));
QVERIFY(!(nan == nan));
CHECKNAN(nan);
CHECKNAN(nan + one);
CHECKNAN(nan - one);
CHECKNAN(-nan);
CHECKNAN(nan * two);
CHECKNAN(nan / two);
CHECKNAN(one / nan);
CHECKNAN(zero / nan);
CHECKNAN(zero * nan);
CHECKNAN(sqrt(-one));
// When any NaN is expected, any NaN will do:
QCOMPARE(nan, nan);
QCOMPARE(nan, -nan);
QCOMPARE(nan, qQNaN());
#undef CHECKNAN
}
template<typename F>
void tst_QNumeric::rawNaN_data()
{
#if defined __FAST_MATH__ && (__GNUC__ * 100 + __GNUC_MINOR__ < 404)
QSKIP("Non-conformant fast math mode is enabled, cannot run test");
#endif
QTest::addColumn<F>("nan");
QTest::newRow("quiet") << F(qQNaN());
#if QT_CONFIG(signaling_nan)
QTest::newRow("signaling") << F(qSNaN());
#endif
}
template<typename F>
void tst_QNumeric::rawNaN()
{
QFETCH(F, nan);
#ifdef Q_OS_WASM
# ifdef __asmjs
QEXPECT_FAIL("", "Fastcomp conflates quiet and signaling NaNs", Continue);
# endif // but the modern clang compiler handls it fine.
#endif
checkNaN(nan);
}
#if QT_CONFIG(signaling_nan)
template<typename F>
void tst_QNumeric::distinctNaN()
{
const F qnan = qQNaN();
const F snan = qSNaN();
QVERIFY(memcmp(&qnan, &snan, sizeof(F)) != 0);
}
void tst_QNumeric::distinctNaNF() {
#ifdef Q_CC_MSVC
QEXPECT_FAIL("", "MSVC's float conflates quiet and signaling NaNs", Continue);
#endif
distinctNaN<float>();
}
#endif // signaling_nan
template<typename F, typename Whole>
void tst_QNumeric::generalNaN_data()
{
static_assert(sizeof(F) == sizeof(Whole));
QTest::addColumn<Whole>("whole");
// Every value with every bit of the exponent set is a NaN.
// Sign and mantissa can be anything without interfering with that.
using Bounds = std::numeric_limits<F>;
// Bounds::digits is one more than the number of bits used to encode the mantissa:
const int mantissaBits = Bounds::digits - 1;
// One bit for sign, the rest are mantissa and exponent:
const int exponentBits = sizeof(F) * CHAR_BIT - 1 - mantissaBits;
const Whole exponent = ((Whole(1) << exponentBits) - 1) << mantissaBits;
const Whole sign = Whole(1) << (exponentBits + mantissaBits);
const Whole mantissaTop = Whole(1) << (mantissaBits - 1);
QTest::newRow("lowload") << (exponent | 1);
QTest::newRow("sign-lowload") << (sign | exponent | 1);
QTest::newRow("highload") << (exponent | mantissaTop);
QTest::newRow("sign-highload") << (sign | exponent | mantissaTop);
}
template<typename F, typename Whole>
void tst_QNumeric::generalNaN()
{
static_assert(sizeof(F) == sizeof(Whole));
QFETCH(const Whole, whole);
F nan;
memcpy(&nan, &whole, sizeof(F));
checkNaN(nan);
}
template<typename F>
void tst_QNumeric::infinity()
{
const auto cleanup = qScopeGuard([]() { clearFpExceptions(); });
const F inf = qInf();
const F zero(0), one(1), two(2);
QVERIFY(inf > zero);
QVERIFY(-inf < zero);
QVERIFY(qIsInf(inf));
QCOMPARE(inf, inf);
QCOMPARE(-inf, -inf);
QVERIFY(qIsInf(-inf));
QVERIFY(qIsInf(inf + one));
QVERIFY(qIsInf(inf - one));
QVERIFY(qIsInf(-inf - one));
QVERIFY(qIsInf(-inf + one));
QVERIFY(qIsInf(inf * two));
QVERIFY(qIsInf(-inf * two));
QVERIFY(qIsInf(inf / two));
QVERIFY(qIsInf(-inf / two));
QVERIFY(qFuzzyCompare(one / inf, zero));
QCOMPARE(1.0 / inf, 0.0);
QVERIFY(qFuzzyCompare(one / -inf, zero));
QCOMPARE(one / -inf, zero);
QVERIFY(qIsNaN(zero * inf));
QVERIFY(qIsNaN(zero * -inf));
QCOMPARE(log(zero), -inf);
}
template<typename F>
void tst_QNumeric::classifyfp()
{
using Bounds = std::numeric_limits<F>;
const F huge = Bounds::max();
const F tiny = Bounds::min();
// NaNs already handled, see checkNaN()'s callers.
const F one(1), two(2), inf(qInf());
QCOMPARE(qFpClassify(inf), FP_INFINITE);
QCOMPARE(qFpClassify(-inf), FP_INFINITE);
QCOMPARE(qFpClassify(huge * two), FP_INFINITE);
QCOMPARE(qFpClassify(huge * -two), FP_INFINITE);
QCOMPARE(qFpClassify(one), FP_NORMAL);
QCOMPARE(qFpClassify(huge), FP_NORMAL);
QCOMPARE(qFpClassify(-huge), FP_NORMAL);
QCOMPARE(qFpClassify(tiny), FP_NORMAL);
QCOMPARE(qFpClassify(-tiny), FP_NORMAL);
if (Bounds::has_denorm == std::denorm_present) {
QCOMPARE(qFpClassify(tiny / two), FP_SUBNORMAL);
QCOMPARE(qFpClassify(tiny / -two), FP_SUBNORMAL);
}
}
template<typename F, typename Count>
void tst_QNumeric::distance_data()
{
using Bounds = std::numeric_limits<F>;
const F huge = Bounds::max();
const F tiny = Bounds::min();
QTest::addColumn<F>("from");
QTest::addColumn<F>("stop");
QTest::addColumn<Count>("expectedDistance");
using Bounds = std::numeric_limits<F>;
const int mantissaBits = Bounds::digits - 1;
const int exponentBits = sizeof(F) * CHAR_BIT - 1 - mantissaBits;
// Set to 1 and 0 if denormals are not included:
const Count count_0_to_tiny = Count(1) << mantissaBits;
const Count count_denormals = count_0_to_tiny - 1;
// We need +1 to include the 0:
const Count count_0_to_1
= (Count(1) << mantissaBits) * ((Count(1) << (exponentBits - 1)) - 2)
+ 1 + count_denormals;
const Count count_1_to_2 = Count(1) << mantissaBits;
// We don't need +1 because huge has all bits set in the mantissa. (Thus mantissa
// have not wrapped back to 0, which would be the case for 1 in _0_to_1
const Count count_0_to_huge
= (Count(1) << mantissaBits) * ((Count(1) << exponentBits) - 2)
+ count_denormals;
const F zero(0), half(.5), one(1), sesqui(1.5), two(2);
const F denormal = tiny / two;
QTest::newRow("[0,tiny]") << zero << tiny << count_0_to_tiny;
QTest::newRow("[0,huge]") << zero << huge << count_0_to_huge;
QTest::newRow("[1,1.5]") << one << sesqui << (Count(1) << (mantissaBits - 1));
QTest::newRow("[0,1]") << zero << one << count_0_to_1;
QTest::newRow("[0.5,1]") << half << one << (Count(1) << mantissaBits);
QTest::newRow("[1,2]") << one << two << count_1_to_2;
QTest::newRow("[-1,+1]") << -one << +one << 2 * count_0_to_1;
QTest::newRow("[-1,0]") << -one << zero << count_0_to_1;
QTest::newRow("[-1,huge]") << -one << huge << count_0_to_1 + count_0_to_huge;
QTest::newRow("[-2,-1") << -two << -one << count_1_to_2;
QTest::newRow("[-1,-2") << -one << -two << count_1_to_2;
QTest::newRow("[tiny,huge]") << tiny << huge << count_0_to_huge - count_0_to_tiny;
QTest::newRow("[-huge,huge]") << -huge << huge << (2 * count_0_to_huge);
QTest::newRow("denormal") << zero << denormal << count_0_to_tiny / 2;
}
template<typename F, typename Count>
void tst_QNumeric::distance()
{
QFETCH(F, from);
QFETCH(F, stop);
QFETCH(Count, expectedDistance);
if constexpr (std::numeric_limits<F>::has_denorm != std::denorm_present) {
if (qstrcmp(QTest::currentDataTag(), "denormal") == 0) {
QSKIP("Skipping 'denorm' as this type lacks denormals on this system");
}
}
QCOMPARE(qFloatDistance(from, stop), expectedDistance);
QCOMPARE(qFloatDistance(stop, from), expectedDistance);
}
// Whole number tests:
void tst_QNumeric::addOverflow_data()
{
QTest::addColumn<int>("size");
// for unsigned, all sizes are supported
QTest::newRow("quint8") << 8;
QTest::newRow("quint16") << 16;
QTest::newRow("quint32") << 32;
QTest::newRow("quint64") << 64;
QTest::newRow("ulong") << 48; // it's either 32- or 64-bit, so on average it's 48 :-)
// for signed, we can't guarantee 64-bit
QTest::newRow("qint8") << -8;
QTest::newRow("qint16") << -16;
QTest::newRow("qint32") << -32;
if (sizeof(void *) == sizeof(qint64))
QTest::newRow("qint64") << -64;
}
// Note: in release mode, all the tests may be statically determined and only the calls
// to QTest::toString and QTest::qCompare will remain.
template <typename Int> static void addOverflow_template()
{
#if defined(Q_CC_MSVC) && Q_CC_MSVC < 2000
QSKIP("Test disabled, this test generates an Internal Compiler Error compiling in release mode");
#else
constexpr Int max = std::numeric_limits<Int>::max();
constexpr Int min = std::numeric_limits<Int>::min();
Int r;
#define ADD_COMPARE_NONOVF(v1, v2, expected) \
do { \
QCOMPARE(add_overflow(Int(v1), Int(v2), &r), false); \
QCOMPARE(r, Int(expected)); \
QCOMPARE(add_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), false); \
QCOMPARE(r, Int(expected)); \
QCOMPARE(add_overflow<v2>(Int(v1), &r), false); \
QCOMPARE(r, Int(expected)); \
} while (false)
#define ADD_COMPARE_OVF(v1, v2) \
do { \
QCOMPARE(add_overflow(Int(v1), Int(v2), &r), true); \
QCOMPARE(add_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), true); \
QCOMPARE(add_overflow<v2>(Int(v1), &r), true); \
} while (false)
#define SUB_COMPARE_NONOVF(v1, v2, expected) \
do { \
QCOMPARE(sub_overflow(Int(v1), Int(v2), &r), false); \
QCOMPARE(r, Int(expected)); \
QCOMPARE(sub_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), false); \
QCOMPARE(r, Int(expected)); \
QCOMPARE(sub_overflow<v2>(Int(v1), &r), false); \
QCOMPARE(r, Int(expected)); \
} while (false)
#define SUB_COMPARE_OVF(v1, v2) \
do { \
QCOMPARE(sub_overflow(Int(v1), Int(v2), &r), true); \
QCOMPARE(sub_overflow(Int(v1), (std::integral_constant<Int, Int(v2)>()), &r), true); \
QCOMPARE(sub_overflow<v2>(Int(v1), &r), true); \
} while (false)
// basic values
ADD_COMPARE_NONOVF(0, 0, 0);
ADD_COMPARE_NONOVF(1, 0, 1);
ADD_COMPARE_NONOVF(0, 1, 1);
SUB_COMPARE_NONOVF(0, 0, 0);
SUB_COMPARE_NONOVF(1, 0, 1);
SUB_COMPARE_NONOVF(1, 1, 0);
if (min)
SUB_COMPARE_NONOVF(0, 1, -1);
else
SUB_COMPARE_OVF(0, 1);
// half-way through max
ADD_COMPARE_NONOVF(max/2, max/2, max / 2 * 2);
SUB_COMPARE_NONOVF(max/2, max/2, 0);
ADD_COMPARE_NONOVF(max/2 - 1, max/2 + 1, max / 2 * 2);
if (min)
SUB_COMPARE_NONOVF(max/2 - 1, max/2 + 1, -2);
else
SUB_COMPARE_OVF(max/2 - 1, max/2 + 1);
ADD_COMPARE_NONOVF(max/2 + 1, max/2, max);
SUB_COMPARE_NONOVF(max/2 + 1, max/2, 1);
ADD_COMPARE_NONOVF(max/2, max/2 + 1, max);
if (min)
SUB_COMPARE_NONOVF(max/2, max/2 + 1, -1);
else
SUB_COMPARE_OVF(max/2, max/2 + 1);
ADD_COMPARE_NONOVF(min/2, min/2, min / 2 * 2);
SUB_COMPARE_NONOVF(min/2, min/2, 0);
if (min)
ADD_COMPARE_NONOVF(min/2 - 1, min/2 + 1, min / 2 * 2);
else
ADD_COMPARE_OVF(min/2 - 1, min/2 + 1);
SUB_COMPARE_NONOVF(min/2 - 1, min/2 + 1, -2);
SUB_COMPARE_NONOVF(min/2 + 1, min/2, 1);
if (min)
SUB_COMPARE_NONOVF(min/2, min/2 + 1, -1);
else
SUB_COMPARE_OVF(min/2, min/2 + 1);
// more than half
ADD_COMPARE_NONOVF(max/4 * 3, max/4, max / 4 * 4);
// max
ADD_COMPARE_NONOVF(max, 0, max);
SUB_COMPARE_NONOVF(max, 0, max);
ADD_COMPARE_NONOVF(0, max, max);
if (min)
SUB_COMPARE_NONOVF(0, max, -max);
else
SUB_COMPARE_OVF(0, max);
ADD_COMPARE_NONOVF(min, 0, min);
SUB_COMPARE_NONOVF(min, 0, min);
ADD_COMPARE_NONOVF(0, min, min);
if (min)
SUB_COMPARE_NONOVF(0, min+1, -(min+1));
else
SUB_COMPARE_OVF(0, min+1);
// 64-bit issues
if constexpr (max > std::numeric_limits<uint>::max()) {
ADD_COMPARE_NONOVF(std::numeric_limits<uint>::max(), std::numeric_limits<uint>::max(), 2 * Int(std::numeric_limits<uint>::max()));
SUB_COMPARE_NONOVF(std::numeric_limits<uint>::max(), std::numeric_limits<uint>::max(), 0);
}
if constexpr (min != 0) {
if (qint64(min) < qint64(-std::numeric_limits<uint>::max())) {
ADD_COMPARE_NONOVF(-Int(std::numeric_limits<uint>::max()), -Int(std::numeric_limits<uint>::max()), -2 * Int(std::numeric_limits<uint>::max()));
SUB_COMPARE_NONOVF(-Int(std::numeric_limits<uint>::max()), -Int(std::numeric_limits<uint>::max()), 0);
}
}
// overflows past max
ADD_COMPARE_OVF(max, 1);
ADD_COMPARE_OVF(1, max);
ADD_COMPARE_OVF(max/2 + 1, max/2 + 1);
// overflows past min
if constexpr (min != 0) {
ADD_COMPARE_OVF(-max, -2);
SUB_COMPARE_OVF(-max, 2);
SUB_COMPARE_OVF(-max/2 - 1, max/2 + 2);
SUB_COMPARE_OVF(min, 1);
SUB_COMPARE_OVF(1, min);
SUB_COMPARE_OVF(min/2 - 1, -Int(min/2));
ADD_COMPARE_OVF(min, -1);
ADD_COMPARE_OVF(-1, min);
}
#undef ADD_COMPARE_NONOVF
#undef ADD_COMPARE_OVF
#undef SUB_COMPARE_NONOVF
#undef SUB_COMPARE_OVF
#endif
}
void tst_QNumeric::addOverflow()
{
QFETCH(int, size);
if (size == 8)
addOverflow_template<quint8>();
if (size == 16)
addOverflow_template<quint16>();
if (size == 32)
addOverflow_template<quint32>();
if (size == 48)
addOverflow_template<ulong>(); // not really 48-bit
if (size == 64)
addOverflow_template<quint64>();
if (size == -8)
addOverflow_template<qint8>();
if (size == -16)
addOverflow_template<qint16>();
if (size == -32)
addOverflow_template<qint32>();
if (size == -64)
addOverflow_template<qint64>();
}
void tst_QNumeric::mulOverflow_data()
{
addOverflow_data();
}
// Note: in release mode, all the tests may be statically determined and only the calls
// to QTest::toString and QTest::qCompare will remain.
template <typename Int> static void mulOverflow_template()
{
#if defined(Q_CC_MSVC) && Q_CC_MSVC < 1900
QSKIP("Test disabled, this test generates an Internal Compiler Error compiling");
#else
constexpr Int max = std::numeric_limits<Int>::max();
constexpr Int min = std::numeric_limits<Int>::min();
// for unsigned (even number of significant bits): mid2 = mid1 - 1
// for signed (odd number of significant bits): mid2 = mid1 / 2 - 1
constexpr Int mid1 = Int(Int(1) << (sizeof(Int) * CHAR_BIT / 2));
constexpr Int mid2 = (std::numeric_limits<Int>::digits % 2 ? mid1 / 2 : mid1) - 1;
Int r;
#define MUL_COMPARE_NONOVF(v1, v2, expected) \
do { \
QCOMPARE(mul_overflow(Int(v1), Int(v2), &r), false); \
QCOMPARE(r, Int(expected)); \
QCOMPARE(mul_overflow(Int(v1), (std::integral_constant<Int, v2>()), &r), false); \
QCOMPARE(r, Int(expected)); \
QCOMPARE(mul_overflow<v2>(Int(v1), &r), false); \
QCOMPARE(r, Int(expected)); \
} while (false);
#define MUL_COMPARE_OVF(v1, v2) \
do { \
QCOMPARE(mul_overflow(Int(v1), Int(v2), &r), true); \
QCOMPARE(mul_overflow(Int(v1), (std::integral_constant<Int, v2>()), &r), true); \
QCOMPARE(mul_overflow<v2>(Int(v1), &r), true); \
} while (false);
// basic multiplications
MUL_COMPARE_NONOVF(0, 0, 0);
MUL_COMPARE_NONOVF(1, 0, 0);
MUL_COMPARE_NONOVF(0, 1, 0);
MUL_COMPARE_NONOVF(max, 0, 0);
MUL_COMPARE_NONOVF(0, max, 0);
MUL_COMPARE_NONOVF(min, 0, 0);
MUL_COMPARE_NONOVF(0, min, 0);
if constexpr (min != 0) {
MUL_COMPARE_NONOVF(0, -1, 0);
MUL_COMPARE_NONOVF(1, -1, -1);
MUL_COMPARE_NONOVF(max, -1, -max);
}
MUL_COMPARE_NONOVF(1, 1, 1);
MUL_COMPARE_NONOVF(1, max, max);
MUL_COMPARE_NONOVF(max, 1, max);
MUL_COMPARE_NONOVF(1, min, min);
MUL_COMPARE_NONOVF(min, 1, min);
// almost max
MUL_COMPARE_NONOVF(mid1, mid2, max - mid1 + 1);
MUL_COMPARE_NONOVF(max / 2, 2, max & ~Int(1));
MUL_COMPARE_NONOVF(max / 4, 4, max & ~Int(3));
if constexpr (min != 0) {
MUL_COMPARE_NONOVF(-mid1, mid2, -max + mid1 - 1);
MUL_COMPARE_NONOVF(-max / 2, 2, -max + 1);
MUL_COMPARE_NONOVF(-max / 4, 4, -max + 3);
MUL_COMPARE_NONOVF(-mid1, mid2 + 1, min);
MUL_COMPARE_NONOVF(mid1, -mid2 - 1, min);
}
// overflows
MUL_COMPARE_OVF(max, 2);
MUL_COMPARE_OVF(max / 2, 3);
MUL_COMPARE_OVF(mid1, mid2 + 1);
MUL_COMPARE_OVF(max / 2 + 2, 2);
MUL_COMPARE_OVF(max - max / 2, 2);
MUL_COMPARE_OVF(1ULL << (std::numeric_limits<Int>::digits - 1), 2);
if constexpr (min != 0) {
MUL_COMPARE_OVF(min, -1);
MUL_COMPARE_OVF(min, 2);
MUL_COMPARE_OVF(min / 2, 3);
MUL_COMPARE_OVF(min / 2 - 1, 2);
}
#undef MUL_COMPARE_NONOVF
#undef MUL_COMPARE_OVF
#endif
}
template <typename Int, bool enabled = sizeof(Int) <= sizeof(void*)> struct MulOverflowDispatch;
template <typename Int> struct MulOverflowDispatch<Int, true>
{
void operator()() { mulOverflow_template<Int>(); }
};
template <typename Int> struct MulOverflowDispatch<Int, false>
{
void operator()() { QSKIP("This type is too big for this architecture"); }
};
void tst_QNumeric::mulOverflow()
{
QFETCH(int, size);
if (size == 8)
MulOverflowDispatch<quint8>()();
if (size == 16)
MulOverflowDispatch<quint16>()();
if (size == 32)
MulOverflowDispatch<quint32>()();
if (size == 48)
MulOverflowDispatch<ulong>()(); // not really 48-bit
if (size == 64)
MulOverflowDispatch<quint64>()();
if (size == -8)
MulOverflowDispatch<qint8>()();
if (size == -16)
MulOverflowDispatch<qint16>()();
if (size == -32)
MulOverflowDispatch<qint32>()();
if (size == -64) {
#if QT_POINTER_SIZE == 8 || defined(Q_INTRINSIC_MUL_OVERFLOW64)
MulOverflowDispatch<qint64>()();
#else
QFAIL("128-bit multiplication not supported on this platform");
#endif
}
}
void tst_QNumeric::signedOverflow()
{
const int minInt = std::numeric_limits<int>::min();
const int maxInt = std::numeric_limits<int>::max();
int r;
QCOMPARE(add_overflow(minInt + 1, int(-1), &r), false);
QCOMPARE(add_overflow(minInt, int(-1), &r), true);
QCOMPARE(add_overflow(minInt, minInt, &r), true);
QCOMPARE(add_overflow(maxInt - 1, int(1), &r), false);
QCOMPARE(add_overflow(maxInt, int(1), &r), true);
QCOMPARE(add_overflow(maxInt, maxInt, &r), true);
QCOMPARE(sub_overflow(minInt + 1, int(1), &r), false);
QCOMPARE(sub_overflow(minInt, int(1), &r), true);
QCOMPARE(sub_overflow(minInt, maxInt, &r), true);
QCOMPARE(sub_overflow(maxInt - 1, int(-1), &r), false);
QCOMPARE(sub_overflow(maxInt, int(-1), &r), true);
QCOMPARE(sub_overflow(maxInt, minInt, &r), true);
QCOMPARE(mul_overflow(minInt, int(1), &r), false);
QCOMPARE(mul_overflow(minInt, int(-1), &r), true);
QCOMPARE(mul_overflow(minInt, int(2), &r), true);
QCOMPARE(mul_overflow(minInt, minInt, &r), true);
QCOMPARE(mul_overflow(maxInt, int(1), &r), false);
QCOMPARE(mul_overflow(maxInt, int(-1), &r), false);
QCOMPARE(mul_overflow(maxInt, int(2), &r), true);
QCOMPARE(mul_overflow(maxInt, maxInt, &r), true);
}
QTEST_APPLESS_MAIN(tst_QNumeric)
#include "tst_qnumeric.moc"

View File

@ -0,0 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qoperatingsystemversion Test:
#####################################################################
qt_internal_add_test(tst_qoperatingsystemversion
SOURCES
tst_qoperatingsystemversion.cpp
)
## Scopes:
#####################################################################

View File

@ -0,0 +1,257 @@
// 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 <QTest>
#include <qoperatingsystemversion.h>
class tst_QOperatingSystemVersion : public QObject
{
Q_OBJECT
private slots:
void construction_data();
void construction();
void globals_data();
void globals();
void anyOf();
void comparison_data();
void comparison();
void comparison2_data();
void comparison2();
void mixedComparison();
};
void tst_QOperatingSystemVersion::construction_data()
{
QTest::addColumn<QOperatingSystemVersion::OSType>("osType");
QTest::addColumn<int>("majorVersion");
QTest::addColumn<int>("minorVersion");
QTest::addColumn<int>("microVersion");
QTest::addColumn<int>("segmentCount");
QTest::newRow("Major only") << QOperatingSystemVersion::OSType::Windows << 1 << -1 << -1 << 1;
QTest::newRow("Major and minor") << QOperatingSystemVersion::OSType::MacOS
<< 1 << 2 << -1 << 2;
QTest::newRow("Major, minor and micro") << QOperatingSystemVersion::OSType::Android
<< 1 << 2 << 3 << 3;
}
void tst_QOperatingSystemVersion::construction()
{
QFETCH(QOperatingSystemVersion::OSType, osType);
QFETCH(int, majorVersion);
QFETCH(int, minorVersion);
QFETCH(int, microVersion);
QFETCH(int, segmentCount);
const QOperatingSystemVersion systemVersion(osType, majorVersion, minorVersion, microVersion);
QCOMPARE(systemVersion.type(), osType);
QCOMPARE(systemVersion.segmentCount(), segmentCount);
QCOMPARE(systemVersion.majorVersion(), majorVersion);
QCOMPARE(systemVersion.minorVersion(), minorVersion);
QCOMPARE(systemVersion.microVersion(), microVersion);
if (osType != QOperatingSystemVersion::OSType::Unknown)
QVERIFY(!systemVersion.name().isEmpty());
}
void tst_QOperatingSystemVersion::globals_data()
{
QTest::addColumn<QOperatingSystemVersion>("osver");
QTest::addColumn<QOperatingSystemVersion::OSType>("osType");
#define ADDROW(os) QTest::newRow(#os) << QOperatingSystemVersion(QOperatingSystemVersion::os)
// legacy ones (global variables)
ADDROW(Windows7) << QOperatingSystemVersion::Windows;
ADDROW(Windows10) << QOperatingSystemVersion::Windows;
ADDROW(OSXMavericks) << QOperatingSystemVersion::MacOS;
ADDROW(MacOSMonterey) << QOperatingSystemVersion::MacOS;
ADDROW(AndroidJellyBean) << QOperatingSystemVersion::Android;
ADDROW(Android11) << QOperatingSystemVersion::Android;
// new ones (static constexpr)
ADDROW(Windows11) << QOperatingSystemVersion::Windows;
ADDROW(Android12) << QOperatingSystemVersion::Android;
#undef ADDROW
}
void tst_QOperatingSystemVersion::globals()
{
QFETCH(QOperatingSystemVersion, osver);
QFETCH(QOperatingSystemVersion::OSType, osType);
QCOMPARE(osver.type(), osType);
QCOMPARE_NE(osver.majorVersion(), 0);
}
void tst_QOperatingSystemVersion::anyOf()
{
std::initializer_list<QOperatingSystemVersion::OSType> typesToCheck = {
QOperatingSystemVersion::OSType::Windows, QOperatingSystemVersion::OSType::Android,
QOperatingSystemVersion::OSType::MacOS, QOperatingSystemVersion::OSType::Unknown
};
{
// type found case
const QOperatingSystemVersion systemVersion(QOperatingSystemVersion::OSType::MacOS, 1);
QCOMPARE(systemVersion.isAnyOfType(typesToCheck), true);
}
{
// type NOT found case
const QOperatingSystemVersion systemVersion(QOperatingSystemVersion::OSType::WatchOS, 1);
QCOMPARE(systemVersion.isAnyOfType(typesToCheck), false);
}
}
void tst_QOperatingSystemVersion::comparison_data()
{
QTest::addColumn<QOperatingSystemVersion::OSType>("lhsType");
QTest::addColumn<int>("lhsMajor");
QTest::addColumn<int>("lhsMinor");
QTest::addColumn<int>("lhsMicro");
QTest::addColumn<QOperatingSystemVersion::OSType>("rhsType");
QTest::addColumn<int>("rhsMajor");
QTest::addColumn<int>("rhsMinor");
QTest::addColumn<int>("rhsMicro");
QTest::addColumn<bool>("lessResult");
QTest::addColumn<bool>("lessEqualResult");
QTest::addColumn<bool>("moreResult");
QTest::addColumn<bool>("moreEqualResult");
QTest::addRow("mismatching types") << QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< QOperatingSystemVersion::OSType::MacOS << 1 << 2 << 3
<< false << false << false << false;
QTest::addRow("equal versions") << QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< false << true << false << true;
QTest::addRow("lhs micro less") << QOperatingSystemVersion::OSType::Windows << 1 << 2 << 2
<< QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< true << true << false << false;
QTest::addRow("rhs micro less") << QOperatingSystemVersion::OSType::Windows << 1 << 2 << 2
<< QOperatingSystemVersion::OSType::Windows << 1 << 2 << 1
<< false << false << true << true;
QTest::addRow("lhs minor less") << QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< QOperatingSystemVersion::OSType::Windows << 1 << 3 << 3
<< true << true << false << false;
QTest::addRow("rhs minor less") << QOperatingSystemVersion::OSType::Windows << 1 << 2 << 2
<< QOperatingSystemVersion::OSType::Windows << 1 << 1 << 3
<< false << false << true << true;
QTest::addRow("lhs major less") << QOperatingSystemVersion::OSType::Windows << 0 << 5 << 6
<< QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< true << true << false << false;
QTest::addRow("rhs major less") << QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< QOperatingSystemVersion::OSType::Windows << 0 << 2 << 3
<< false << false << true << true;
QTest::addRow("different segmentCount")
<< QOperatingSystemVersion::OSType::Windows << 1 << 2 << 3
<< QOperatingSystemVersion::OSType::Windows << 1 << 2 << -1
<< false << true << false << true;
}
void tst_QOperatingSystemVersion::comparison()
{
QFETCH(QOperatingSystemVersion::OSType, lhsType);
QFETCH(int, lhsMajor);
QFETCH(int, lhsMinor);
QFETCH(int, lhsMicro);
const QOperatingSystemVersion lhsSystemInfo(lhsType, lhsMajor, lhsMinor, lhsMicro);
QFETCH(QOperatingSystemVersion::OSType, rhsType);
QFETCH(int, rhsMajor);
QFETCH(int, rhsMinor);
QFETCH(int, rhsMicro);
const QOperatingSystemVersion rhsSystemInfo(rhsType, rhsMajor, rhsMinor, rhsMicro);
QFETCH(bool, lessResult);
QCOMPARE(lhsSystemInfo < rhsSystemInfo, lessResult);
QFETCH(bool, lessEqualResult);
QCOMPARE(lhsSystemInfo <= rhsSystemInfo, lessEqualResult);
QFETCH(bool, moreResult);
QCOMPARE(lhsSystemInfo > rhsSystemInfo, moreResult);
QFETCH(bool, moreEqualResult);
QCOMPARE(lhsSystemInfo >= rhsSystemInfo, moreEqualResult);
}
void tst_QOperatingSystemVersion::comparison2_data()
{
QTest::addColumn<QOperatingSystemVersion>("lhs");
QTest::addColumn<QOperatingSystemVersion>("rhs");
QTest::addColumn<int>("result");
#define ADDROW(os1, os2) \
QTest::newRow(#os1 "-vs-" #os2) << QOperatingSystemVersion(QOperatingSystemVersion::os1) \
<< QOperatingSystemVersion(QOperatingSystemVersion::os2)
// Cross-OS testing: not comparables.
ADDROW(Windows10, MacOSMonterey) << -128;
ADDROW(Windows11, MacOSMonterey) << -128;
ADDROW(MacOSMonterey, Windows10) << -128;
ADDROW(MacOSMonterey, Windows11) << -128;
ADDROW(Windows10, MacOSVentura) << -128;
ADDROW(Windows11, MacOSVentura) << -128;
ADDROW(MacOSVentura, Windows10) << -128;
ADDROW(MacOSVentura, Windows11) << -128;
ADDROW(Windows10, Android10) << -128;
ADDROW(Windows11, Android11) << -128;
// Same-OS tests. This list does not have to be exhaustive.
ADDROW(Windows7, Windows7) << 0;
ADDROW(Windows7, Windows8) << -1;
ADDROW(Windows8, Windows7) << 1;
ADDROW(Windows8, Windows10) << -1;
ADDROW(Windows10, Windows8) << 1;
ADDROW(Windows10, Windows10_21H1) << -1;
ADDROW(Windows10_21H1, Windows10) << 1;
ADDROW(Windows10, Windows11) << -1;
ADDROW(MacOSCatalina, MacOSCatalina) << 0;
ADDROW(MacOSCatalina, MacOSBigSur) << -1;
ADDROW(MacOSBigSur, MacOSCatalina) << 1;
ADDROW(MacOSMonterey, MacOSVentura) << -1;
ADDROW(MacOSVentura, MacOSVentura) << 0;
ADDROW(MacOSVentura, MacOSMonterey) << 1;
#undef ADDROW
}
void tst_QOperatingSystemVersion::comparison2()
{
QFETCH(QOperatingSystemVersion, lhs);
QFETCH(QOperatingSystemVersion, rhs);
QFETCH(int, result);
QEXPECT_FAIL("Windows10-vs-Windows10_21H1", "QTBUG-107907: Unexpected behavior", Abort);
QEXPECT_FAIL("Windows10-vs-Windows11", "QTBUG-107907: Unexpected behavior", Abort);
// value -128 indicates "not comparable"
bool comparable = (result != -128);
QCOMPARE(lhs < rhs, result < 0 && comparable);
QEXPECT_FAIL("Windows10_21H1-vs-Windows10", "QTBUG-107907: Unexpected behavior", Abort);
QCOMPARE(lhs <= rhs, result <= 0 && comparable);
QCOMPARE(lhs > rhs, result > 0 && comparable);
QCOMPARE(lhs >= rhs, result >= 0 && comparable);
}
void tst_QOperatingSystemVersion::mixedComparison()
{
// ==
QVERIFY(QOperatingSystemVersion::Windows10
>= QOperatingSystemVersionBase(QOperatingSystemVersionBase::Windows, 10, 0));
QVERIFY(QOperatingSystemVersion::Windows10
<= QOperatingSystemVersionBase(QOperatingSystemVersionBase::Windows, 10, 0));
}
QTEST_MAIN(tst_QOperatingSystemVersion)
#include "tst_qoperatingsystemversion.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qrandomgenerator Test:
#####################################################################
qt_internal_add_test(tst_qrandomgenerator
SOURCES
tst_qrandomgenerator.cpp
LIBRARIES
Qt::CorePrivate
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qtendian Test:
#####################################################################
qt_internal_add_test(tst_qtendian
SOURCES
tst_qtendian.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,449 @@
// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QtCore/qendian.h>
#include <QtCore/private/qendian_p.h>
#include <QtCore/qsysinfo.h>
class tst_QtEndian: public QObject
{
Q_OBJECT
public:
enum Signedness {
Unsigned,
Signed
};
Q_ENUM(Signedness);
private slots:
void fromBigEndian();
void fromLittleEndian();
void fromBigEndianRegion_data();
void fromBigEndianRegion();
void fromLittleEndianRegion_data() { fromBigEndianRegion_data(); }
void fromLittleEndianRegion();
void toBigEndian();
void toLittleEndian();
void toBigEndianRegion_data() { fromBigEndianRegion_data(); }
void toBigEndianRegion();
void toLittleEndianRegion_data() { fromBigEndianRegion_data(); }
void toLittleEndianRegion();
void endianIntegers_data();
void endianIntegers();
void endianBitfieldUnions_data();
void endianBitfieldUnions();
};
struct TestData
{
quint64 data64;
quint32 data32;
quint16 data16;
quint8 data8;
float dataFloat;
double dataDouble;
quint8 reserved;
};
template <typename T> T getData(const TestData &d);
template <> quint8 getData(const TestData &d) { return d.data8; }
template <> quint16 getData(const TestData &d) { return d.data16; }
template <> quint32 getData(const TestData &d) { return d.data32; }
template <> quint64 getData(const TestData &d) { return d.data64; }
template <> float getData(const TestData &d) { return d.dataFloat; }
union RawTestData
{
uchar rawData[sizeof(TestData)];
TestData data;
};
template <typename Float>
Float int2Float(typename QIntegerForSizeof<Float>::Unsigned i)
{
Float result = 0;
memcpy(reinterpret_cast<char *>(&result), reinterpret_cast<const char *>(&i), sizeof (Float));
return result;
}
static const TestData inNativeEndian = {
Q_UINT64_C(0x0123456789abcdef),
0x00c0ffee,
0xcafe,
0xcf,
int2Float<float>(0x00c0ffeeU),
int2Float<double>(Q_UINT64_C(0x0123456789abcdef)),
'\0'
};
static const RawTestData inBigEndian = {
"\x01\x23\x45\x67\x89\xab\xcd\xef"
"\x00\xc0\xff\xee"
"\xca\xfe"
"\xcf"
"\x00\xc0\xff\xee"
"\x01\x23\x45\x67\x89\xab\xcd\xef"
};
static const RawTestData inLittleEndian = {
"\xef\xcd\xab\x89\x67\x45\x23\x01"
"\xee\xff\xc0\x00"
"\xfe\xca"
"\xcf"
"\xee\xff\xc0\x00"
"\xef\xcd\xab\x89\x67\x45\x23\x01"
};
#define EXPAND_ENDIAN_TEST(endian) \
do { \
/* Unsigned tests */ \
ENDIAN_TEST(endian, quint, 64); \
ENDIAN_TEST(endian, quint, 32); \
ENDIAN_TEST(endian, quint, 16); \
ENDIAN_TEST(endian, quint, 8); \
\
/* Signed tests */ \
ENDIAN_TEST(endian, qint, 64); \
ENDIAN_TEST(endian, qint, 32); \
ENDIAN_TEST(endian, qint, 16); \
ENDIAN_TEST(endian, qint, 8); \
} while (false) \
/**/
#define ENDIAN_TEST(endian, type, size) \
do { \
QCOMPARE(qFrom ## endian ## Endian( \
(type ## size)(in ## endian ## Endian.data.data ## size)), \
(type ## size)(inNativeEndian.data ## size)); \
QCOMPARE(qFrom ## endian ## Endian<type ## size>( \
in ## endian ## Endian.rawData + offsetof(TestData, data ## size)), \
(type ## size)(inNativeEndian.data ## size)); \
} while (false) \
/**/
void tst_QtEndian::fromBigEndian()
{
EXPAND_ENDIAN_TEST(Big);
}
void tst_QtEndian::fromLittleEndian()
{
EXPAND_ENDIAN_TEST(Little);
}
#undef ENDIAN_TEST
QT_WARNING_PUSH
QT_WARNING_DISABLE_GCC("-Wmemset-elt-size")
template <typename T>
void transformRegion_template(T (*transformOne)(T), void (*transformRegion)(const void *, qsizetype, void *))
{
enum { Size = 64 };
T source[Size];
T dest[Size];
T expected = transformOne(getData<T>(inNativeEndian));
std::fill_n(source, +Size, getData<T>(inNativeEndian));
memset(dest, 0, sizeof(dest));
auto checkBounds = [&](int from) {
for ( ; from < Size; ++from)
QCOMPARE(dest[from], T(0));
};
transformRegion(source, 1, dest);
QCOMPARE(dest[0], expected);
checkBounds(1);
memset(dest, 0, sizeof(T));
transformRegion(source, 2, dest);
QCOMPARE(dest[0], expected);
QCOMPARE(dest[1], expected);
checkBounds(2);
memset(dest, 0, sizeof(T) * 2);
transformRegion(source, 3, dest);
QCOMPARE(dest[0], expected);
QCOMPARE(dest[1], expected);
QCOMPARE(dest[2], expected);
checkBounds(3);
memset(dest, 0, sizeof(T) * 3);
transformRegion(source, 4, dest);
QCOMPARE(dest[0], expected);
QCOMPARE(dest[1], expected);
QCOMPARE(dest[2], expected);
QCOMPARE(dest[3], expected);
checkBounds(4);
memset(dest, 0, sizeof(T) * 4);
transformRegion(source, 8, dest);
for (int i = 0; i < 8; ++i)
QCOMPARE(dest[i], expected);
checkBounds(8);
memset(dest, 0, sizeof(T) * 8);
transformRegion(source, 16, dest);
for (int i = 0; i < 16; ++i)
QCOMPARE(dest[i], expected);
checkBounds(16);
memset(dest, 0, sizeof(T) * 16);
transformRegion(source, 32, dest);
for (int i = 0; i < 32; ++i)
QCOMPARE(dest[i], expected);
checkBounds(32);
memset(dest, 0, sizeof(T) * 32);
transformRegion(source, 64, dest);
for (int i = 0; i < 64; ++i)
QCOMPARE(dest[i], expected);
// check transforming in-place
memcpy(dest, source, sizeof(dest));
transformRegion(dest, 64, dest);
for (int i = 0; i < 64; ++i)
QCOMPARE(dest[i], expected);
}
QT_WARNING_POP
void tst_QtEndian::fromBigEndianRegion_data()
{
QTest::addColumn<int>("size");
QTest::newRow("1") << 1;
QTest::newRow("2") << 2;
QTest::newRow("4") << 4;
QTest::newRow("8") << 8;
}
void tst_QtEndian::fromBigEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qFromBigEndian<quint8>, qFromBigEndian<quint8>);
case 2: return transformRegion_template<quint16>(qFromBigEndian<quint16>, qFromBigEndian<quint16>);
case 4: return transformRegion_template<quint32>(qFromBigEndian<quint32>, qFromBigEndian<quint32>);
case 8: return transformRegion_template<quint64>(qFromBigEndian<quint64>, qFromBigEndian<quint64>);
}
}
void tst_QtEndian::fromLittleEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qFromLittleEndian<quint8>, qFromLittleEndian<quint8>);
case 2: return transformRegion_template<quint16>(qFromLittleEndian<quint16>, qFromLittleEndian<quint16>);
case 4: return transformRegion_template<quint32>(qFromLittleEndian<quint32>, qFromLittleEndian<quint32>);
case 8: return transformRegion_template<quint64>(qFromLittleEndian<quint64>, qFromLittleEndian<quint64>);
}
}
#define ENDIAN_TEST(endian, type, size) \
do { \
QCOMPARE(qTo ## endian ## Endian( \
(type ## size)(inNativeEndian.data ## size)), \
(type ## size)(in ## endian ## Endian.data.data ## size)); \
\
RawTestData test; \
qTo ## endian ## Endian( \
(type ## size)(inNativeEndian.data ## size), \
test.rawData + offsetof(TestData, data ## size)); \
QCOMPARE(test.data.data ## size, in ## endian ## Endian.data.data ## size ); \
} while (false) \
/**/
void tst_QtEndian::toBigEndian()
{
EXPAND_ENDIAN_TEST(Big);
}
void tst_QtEndian::toLittleEndian()
{
EXPAND_ENDIAN_TEST(Little);
}
#undef ENDIAN_TEST
void tst_QtEndian::toBigEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qToBigEndian<quint8>, qToBigEndian<quint8>);
case 2: return transformRegion_template<quint16>(qToBigEndian<quint16>, qToBigEndian<quint16>);
case 4: return transformRegion_template<quint32>(qToBigEndian<quint32>, qToBigEndian<quint32>);
case 8: return transformRegion_template<quint64>(qToBigEndian<quint64>, qToBigEndian<quint64>);
}
}
void tst_QtEndian::toLittleEndianRegion()
{
QFETCH(int, size);
switch (size) {
case 1: return transformRegion_template<quint8>(qToLittleEndian<quint8>, qToLittleEndian<quint8>);
case 2: return transformRegion_template<quint16>(qToLittleEndian<quint16>, qToLittleEndian<quint16>);
case 4: return transformRegion_template<quint32>(qToLittleEndian<quint32>, qToLittleEndian<quint32>);
case 8: return transformRegion_template<quint64>(qToLittleEndian<quint64>, qToLittleEndian<quint64>);
}
}
void tst_QtEndian::endianIntegers_data()
{
QTest::addColumn<int>("val");
QTest::newRow("-30000") << -30000;
QTest::newRow("-1") << -1;
QTest::newRow("0") << 0;
QTest::newRow("1020") << 1020;
QTest::newRow("16385") << 16385;
}
void tst_QtEndian::endianIntegers()
{
QFETCH(int, val);
qint16 vi16 = val;
qint32 vi32 = val;
qint64 vi64 = val;
quint16 vu16 = val;
quint32 vu32 = val;
quint64 vu64 = val;
#if Q_BYTE_ORDER == Q_BIG_ENDIAN
QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), vi16);
QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), vi32);
QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), vi64);
QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), qbswap(vi16));
QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), qbswap(vi32));
QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), qbswap(vi64));
QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), vu16);
QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), vu32);
QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), vu64);
QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), qbswap(vu16));
QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), qbswap(vu32));
QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), qbswap(vu64));
#else
QCOMPARE(*reinterpret_cast<qint16_be*>(&vi16), qbswap(vi16));
QCOMPARE(*reinterpret_cast<qint32_be*>(&vi32), qbswap(vi32));
QCOMPARE(*reinterpret_cast<qint64_be*>(&vi64), qbswap(vi64));
QCOMPARE(*reinterpret_cast<qint16_le*>(&vi16), vi16);
QCOMPARE(*reinterpret_cast<qint32_le*>(&vi32), vi32);
QCOMPARE(*reinterpret_cast<qint64_le*>(&vi64), vi64);
QCOMPARE(*reinterpret_cast<quint16_be*>(&vu16), qbswap(vu16));
QCOMPARE(*reinterpret_cast<quint32_be*>(&vu32), qbswap(vu32));
QCOMPARE(*reinterpret_cast<quint64_be*>(&vu64), qbswap(vu64));
QCOMPARE(*reinterpret_cast<quint16_le*>(&vu16), vu16);
QCOMPARE(*reinterpret_cast<quint32_le*>(&vu32), vu32);
QCOMPARE(*reinterpret_cast<quint64_le*>(&vu64), vu64);
#endif
}
template<template<typename... Accessors> typename Union, template<int, int, typename> typename Member>
void testBitfieldUnion()
{
using upper = Member<21, 11, uint>;
using lower = Member<10, 11, uint>;
using bottom = Member<0, 10, int>;
using all = Member<0, 32, uint>;
using UnionType = Union<upper, lower, bottom, all>;
UnionType u;
u.template set<upper>(200);
QCOMPARE(u.template get<upper>(), 200U);
u.template set<lower>(1000);
u.template set<bottom>(-8);
QCOMPARE(u.template get<lower>(), 1000U);
QCOMPARE(u.template get<upper>(), 200U);
u.template set<lower>(u.template get<lower>() + u.template get<upper>());
QCOMPARE(u.template get<upper>(), 200U);
QCOMPARE(u.template get<lower>(), 1200U);
u.template set<upper>(65536 + 7);
u.template set<lower>(65535);
QCOMPARE(u.template get<lower>(), 65535U & ((1<<11) - 1));
QCOMPARE(u.template get<upper>(), 7U);
QCOMPARE(u.template get<bottom>(), -8);
UnionType u2(QSpecialIntegerBitfieldZero);
QCOMPARE(u2.data(), 0U);
u2.template set<all>(std::numeric_limits<uint>::max());
QCOMPARE(u2.template get<all>(), std::numeric_limits<uint>::max());
u2.template set<all>(453);
QCOMPARE(u2.template get<all>(), 453U);
u2.template set<all>(0);
QCOMPARE(u2.template get<all>(), 0U);
UnionType u3(42U);
QCOMPARE(u3.data(), 42U);
using BEUintAccessor = QSpecialIntegerAccessor<QBigEndianStorageType<uint>, 21, 11>;
using LEUintAccessor = QSpecialIntegerAccessor<QLittleEndianStorageType<uint>, 21, 11>;
using BEIntAccessor = QSpecialIntegerAccessor<QBigEndianStorageType<int>, 0, 10>;
using LEIntAccessor = QSpecialIntegerAccessor<QLittleEndianStorageType<int>, 0, 10>;
if constexpr (std::is_same_v<BEUintAccessor, upper>) {
QCOMPARE(u.template get<BEUintAccessor>(), 7U);
} else if constexpr (std::is_same_v<LEUintAccessor, upper>) {
QCOMPARE(u.template get<LEUintAccessor>(), 7U);
} else if constexpr (std::is_same_v<BEIntAccessor, bottom>) {
QCOMPARE(u.template get<BEIntAccessor>(), -8);
} else if constexpr (std::is_same_v<LEIntAccessor, bottom>) {
QCOMPARE(u.template get<LEIntAccessor>(), -8);
} else {
QFAIL("none of the manually defined accessors match");
}
}
void tst_QtEndian::endianBitfieldUnions_data()
{
QTest::addColumn<QSysInfo::Endian>("byteOrder");
QTest::addColumn<Signedness>("signedness");
QTest::addRow("little endian unsigned") << QSysInfo::LittleEndian << Unsigned;
QTest::addRow("little endian signed") << QSysInfo::LittleEndian << Signed;
QTest::addRow("big endian unsigned") << QSysInfo::BigEndian << Unsigned;
QTest::addRow("big endian signed") << QSysInfo::BigEndian << Signed;
}
void tst_QtEndian::endianBitfieldUnions()
{
QFETCH(QSysInfo::Endian, byteOrder);
QFETCH(Signedness, signedness);
switch (byteOrder) {
case QSysInfo::LittleEndian:
switch (signedness) {
case Unsigned:
testBitfieldUnion<quint32_le_bitfield_union, quint32_le_bitfield_member>();
return;
case Signed:
testBitfieldUnion<qint32_le_bitfield_union, qint32_le_bitfield_member>();
return;
}
Q_UNREACHABLE_RETURN();
case QSysInfo::BigEndian:
switch (signedness) {
case Unsigned:
testBitfieldUnion<quint32_be_bitfield_union, quint32_be_bitfield_member>();
return;
case Signed:
testBitfieldUnion<qint32_be_bitfield_union, qint32_be_bitfield_member>();
return;
}
Q_UNREACHABLE_RETURN();
}
}
QTEST_MAIN(tst_QtEndian)
#include "tst_qtendian.moc"

View File

@ -0,0 +1 @@
add_subdirectory(function_ref)

View File

@ -0,0 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
qt_internal_add_test(tst_qxp_function_ref
EXCEPTIONS
SOURCES
tst_qxp_function_ref.cpp
LIBRARIES
Qt::Core
)

View File

@ -0,0 +1,279 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/qxpfunctional.h>
#include <QTest>
#include <type_traits>
// checking dependency q20::remove_cvref_t:
#define CHECK(in, out) \
static_assert(std::is_same_v<q20::remove_cvref_t< in >, out >)
CHECK(int, int);
CHECK(const int, int);
CHECK(int &, int);
CHECK(const int &, int);
CHECK(int &&, int);
CHECK(const int &&, int);
CHECK(int *, int *);
CHECK(const int *, const int *);
CHECK(int[4], int[4]);
CHECK(const int (&)[4], int[4]);
#undef CHECK
template <typename T> constexpr inline bool
is_noexcept_function_ref_helper_v = false;
template <typename R, typename...Args> constexpr inline bool
is_noexcept_function_ref_helper_v<qxp::function_ref<R(Args...) noexcept(true)>> = true;
template <typename R, typename...Args> constexpr inline bool
is_noexcept_function_ref_helper_v<qxp::function_ref<R(Args...) const noexcept(true)>> = true;
template <typename T> constexpr inline bool
is_noexcept_function_ref_v = is_noexcept_function_ref_helper_v<q20::remove_cvref_t<T>>;
class tst_qxp_function_ref : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
private Q_SLOTS:
void basics();
void constOverloads();
void constExpr();
void voidReturning();
void ctad();
};
void tst_qxp_function_ref::basics()
{
static_assert(std::is_trivially_copyable_v<qxp::function_ref<int(int)>>);
static_assert(std::is_trivially_copyable_v<qxp::function_ref<int()>>);
static_assert(std::is_trivially_copyable_v<qxp::function_ref<void()>>);
{
Q_CONSTINIT static int invoked = 0;
auto lambda = [](int i) noexcept { ++invoked; return i; };
const qxp::function_ref<int(int)> f = lambda;
QCOMPARE(invoked, 0);
QCOMPARE(f(42), 42);
QCOMPARE(invoked, 1);
const int fourtyTwo = 42;
const qxp::function_ref<int(int) noexcept> f2 = std::move(lambda);
QCOMPARE(invoked, 1);
QCOMPARE(f2(fourtyTwo), 42);
QCOMPARE(invoked, 2);
int (*fpr)(int) = lambda;
const qxp::function_ref f3 = fpr;
static_assert(!is_noexcept_function_ref_v<decltype(f3)>);
QCOMPARE(invoked, 2);
QCOMPARE(f3(42), 42);
QCOMPARE(invoked, 3);
int (*fpr2)(int) noexcept = lambda;
const qxp::function_ref f4 = fpr2;
static_assert(is_noexcept_function_ref_v<decltype(f4)>);
QCOMPARE(invoked, 3);
QCOMPARE(f4(42), 42);
QCOMPARE(invoked, 4);
}
{
Q_CONSTINIT static int invoked = 0;
auto lambda = [] { ++invoked; return 42; };
const qxp::function_ref<int()> f = lambda;
QCOMPARE(invoked, 0);
QCOMPARE(f(), 42);
QCOMPARE(invoked, 1);
const qxp::function_ref<int()> f2 = std::move(lambda);
QCOMPARE(invoked, 1);
QCOMPARE(f2(), 42);
QCOMPARE(invoked, 2);
int (*fpr)() = lambda;
const qxp::function_ref f3 = fpr;
static_assert(!is_noexcept_function_ref_v<decltype(f3)>);
QCOMPARE(invoked, 2);
QCOMPARE(f3(), 42);
QCOMPARE(invoked, 3);
}
{
Q_CONSTINIT static int invoked = 0;
auto lambda = [] { ++invoked; };
const qxp::function_ref<void()> f = lambda;
QCOMPARE(invoked, 0);
f();
QCOMPARE(invoked, 1);
const qxp::function_ref<void()> f2 = std::move(lambda);
QCOMPARE(invoked, 1);
f2();
QCOMPARE(invoked, 2);
void (*fpr)() = lambda;
const qxp::function_ref f3 = fpr;
QCOMPARE(invoked, 2);
f3();
QCOMPARE(invoked, 3);
}
}
void tst_qxp_function_ref::constOverloads()
{
auto func_c = [](qxp::function_ref<int() const> callable)
{
return callable();
};
auto func_m = [](qxp::function_ref<int() /*mutable*/> callable)
{
return callable();
};
struct S
{
int operator()() { return 1; }
int operator()() const { return 2; }
};
S s;
QCOMPARE(func_c(s), 2);
QCOMPARE(func_m(s), 1);
const S cs;
QCOMPARE(func_c(cs), 2);
#if 0
// this should not compile (and doesn't, but currently fails with an error in the impl,
// not by failing a constructor constaint → spec issue?).
QCOMPARE(func_m(cs), 2);
#endif
}
void tst_qxp_function_ref::constExpr()
{
Q_CONSTINIT static int invoked = 0;
{
Q_CONSTINIT static auto lambda = [] (int i) { ++invoked; return i; };
// the function object constructor is constexpr, so this should be constinit:
Q_CONSTINIT static qxp::function_ref<int(int)> f = lambda;
QCOMPARE(invoked, 0);
QCOMPARE(f(15), 15);
QCOMPARE(invoked, 1);
}
{
constexpr static auto lambda = [] (int i) { ++invoked; return i; };
// the function object constructor is constexpr, so this should be constinit:
Q_CONSTINIT static qxp::function_ref<int(int) const> f = lambda;
QCOMPARE(invoked, 1);
QCOMPARE(f(51), 51);
QCOMPARE(invoked, 2);
#if 0 // ### should this work?:
Q_CONSTINIT static qxp::function_ref<int(int)> f2 = lambda;
QCOMPARE(invoked, 2);
QCOMPARE(f(150), 150);
QCOMPARE(invoked, 3);
#endif
}
}
int i_f_i_nx(int i) noexcept { return i; }
void v_f_i_nx(int) noexcept {}
int i_f_v_nx() noexcept { return 42; }
void v_f_v_nx() noexcept {}
int i_f_i_ex(int i) { return i; }
void v_f_i_ex(int) {}
int i_f_v_ex() { return 42; }
void v_f_v_ex() {}
void tst_qxp_function_ref::voidReturning()
{
// check that "casting" int to void returns works:
using Fi = qxp::function_ref<void(int)>;
using Fv = qxp::function_ref<void()>;
{
Fi fi = i_f_i_nx;
fi(42);
Fv fv = i_f_v_nx;
fv();
}
{
Fi fi = i_f_i_ex;
fi(42);
Fv fv = i_f_v_ex;
fv();
}
// now with lambdas
bool ok = false; // prevent lambdas from decaying to function pointers
{
auto lambda1 = [&](int i) noexcept { return i + int(ok); };
Fi fi = lambda1;
fi(42);
auto lambda2 = [&]() noexcept { return int(ok); };
Fv fv = lambda2;
fv();
}
{
auto lambda1 = [&](int i) { return i + int(ok); };
Fi fi = lambda1;
fi(42);
auto lambda2 = [&]() { return int(ok); };
Fv fv = lambda2;
fv();
}
}
void tst_qxp_function_ref::ctad()
{
#define CHECK(fun, sig) \
do { \
qxp::function_ref f = fun; \
static_assert(std::is_same_v<decltype(f), \
qxp::function_ref<sig>>); \
qxp::function_ref f2 = &fun; \
static_assert(std::is_same_v<decltype(f2), \
qxp::function_ref<sig>>); \
} while (false)
CHECK(i_f_i_nx, int (int) noexcept);
CHECK(v_f_i_nx, void(int) noexcept);
CHECK(i_f_v_nx, int ( ) noexcept);
CHECK(v_f_v_nx, void( ) noexcept);
CHECK(i_f_i_ex, int (int));
CHECK(v_f_i_ex, void(int));
CHECK(i_f_v_ex, int ( ));
CHECK(v_f_v_ex, void( ));
#undef CHECK
#if 0 // no deduction guides for the non-function-pointer case, so no CTAD for lambdas
{
auto lambda = [](int i) -> int { return i; };
qxp::function_ref f = lambda;
static_assert(std::is_same_v<decltype(f),
qxp::function_ref<int(int)>>);
}
#endif
}
QTEST_APPLESS_MAIN(tst_qxp_function_ref);
#include "tst_qxp_function_ref.moc"

View File

@ -0,0 +1,66 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# There is no mounted filesystem for IO testing on INTEGRITY yet.
if(INTEGRITY)
return()
endif()
if(QT_FEATURE_private_tests)
add_subdirectory(qabstractfileengine)
add_subdirectory(qfileinfo)
add_subdirectory(qipaddress)
add_subdirectory(qloggingregistry)
add_subdirectory(qurlinternal)
endif()
add_subdirectory(qbuffer)
add_subdirectory(qdataurl)
add_subdirectory(qdiriterator)
add_subdirectory(qfile)
add_subdirectory(largefile)
add_subdirectory(qfileselector)
add_subdirectory(qfilesystemmetadata)
add_subdirectory(qloggingcategory)
add_subdirectory(qnodebug)
add_subdirectory(qsavefile)
add_subdirectory(qstandardpaths)
if(NOT QNX)
add_subdirectory(qstorageinfo)
endif()
add_subdirectory(qtemporarydir)
add_subdirectory(qtemporaryfile)
add_subdirectory(qurlquery)
add_subdirectory(qurluts46)
if(TARGET Qt::Concurrent)
if(NOT INTEGRITY)
add_subdirectory(qdebug)
endif()
add_subdirectory(qlockfile)
add_subdirectory(qurl)
endif()
if(NOT ANDROID)
add_subdirectory(qdir)
add_subdirectory(qresourceengine)
endif()
if(QT_FEATURE_private_tests)
add_subdirectory(qfilesystementry)
endif()
# QTBUG-88508
if(QT_FEATURE_filesystemwatcher AND NOT ANDROID)
add_subdirectory(qfilesystemwatcher)
endif()
if(TARGET Qt::Network)
add_subdirectory(qiodevice)
endif()
if(QT_FEATURE_process AND TARGET Qt::Network AND NOT ANDROID)
add_subdirectory(qprocess)
endif()
if(QT_FEATURE_process)
add_subdirectory(qprocess-noapplication)
endif()
if(QT_FEATURE_processenvironment)
add_subdirectory(qprocessenvironment)
endif()
if(QT_FEATURE_settings AND TARGET Qt::Gui)
add_subdirectory(qsettings)
endif()

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_largefile Test:
#####################################################################
qt_internal_add_test(tst_largefile
SOURCES
tst_largefile.cpp
LIBRARIES
Qt::TestPrivate
)

View File

@ -0,0 +1,530 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QFile>
#include <QFileInfo>
#include <QRandomGenerator>
#include <qplatformdefs.h>
#include <QDebug>
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#ifdef Q_OS_WIN
# include <qt_windows.h>
# include <io.h>
# ifndef FSCTL_SET_SPARSE
// MinGW doesn't define this.
# define FSCTL_SET_SPARSE (0x900C4)
# endif
#endif // Q_OS_WIN
#include <QtTest/private/qemulationdetector_p.h>
class tst_LargeFile
: public QObject
{
Q_OBJECT
public:
tst_LargeFile()
: blockSize(1 << 12)
, maxSizeBits()
, fd_(-1)
, stream_(0)
{
#if defined(QT_LARGEFILE_SUPPORT) && !defined(Q_OS_MAC) && !defined(Q_OS_QNX)
maxSizeBits = 36; // 64 GiB
#elif defined(Q_OS_MAC)
// HFS+ does not support sparse files, so we limit file size for the test
// on Mac OS.
maxSizeBits = 24; // 16 MiB
#elif defined(Q_OS_QNX)
// Many of the filesystems that QNX supports use a 32-bit format.
// This means that files are limited to 2 GB 1 bytes.
// Limit max size to 256MB
maxSizeBits = 28; // 256 MiB
#else
maxSizeBits = 24; // 16 MiB
#endif
// QEMU only supports < 4GB files
if (QTestPrivate::isRunningArmOnX86())
maxSizeBits = qMin(maxSizeBits, 28);
}
private:
void sparseFileData();
QByteArray const &getDataBlock(int index, qint64 position);
private slots:
// The LargeFile test case was designed to be run in order as a single unit
void initTestCase();
void cleanupTestCase();
void init();
void cleanup();
// Create and fill large file
void createSparseFile();
void fillFileSparsely();
void closeSparseFile();
// Verify file was created
void fileCreated();
// Positioning in large files
void filePositioning();
void fdPositioning();
void streamPositioning();
// Read data from file
void openFileForReading();
void readFile();
// Map/unmap large file
void mapFile();
void mapOffsetOverflow();
void closeFile() { largeFile.close(); }
// Test data
void fillFileSparsely_data() { sparseFileData(); }
void filePositioning_data() { sparseFileData(); }
void fdPositioning_data() { sparseFileData(); }
void streamPositioning_data() { sparseFileData(); }
void readFile_data() { sparseFileData(); }
void mapFile_data() { sparseFileData(); }
private:
const int blockSize;
int maxSizeBits;
QFile largeFile;
QByteArrayList generatedBlocks;
int fd_;
FILE *stream_;
QSharedPointer<QTemporaryDir> m_tempDir;
QString m_previousCurrent;
};
/*
Convenience function to hide reinterpret_cast when copying a POD directly
into a QByteArray.
*/
template <class T>
static inline void appendRaw(QByteArray &array, T data)
{
array.append(reinterpret_cast<char *>(&data), sizeof(T));
}
/*
Pad array with filler up to size. On return, array.size() returns size.
*/
static inline void topUpWith(QByteArray &array, QByteArray filler, int size)
{
for (int i = (size - array.size()) / filler.size(); i > 0; --i)
array.append(filler);
if (array.size() < size) {
array.append(filler.left(size - array.size()));
}
}
/*
Generate a unique data block containing identifiable data. Unaligned,
overlapping and partial blocks should not compare equal.
*/
static inline QByteArray generateDataBlock(int blockSize, QString text, qint64 userBits = -1)
{
QByteArray block;
block.reserve(blockSize);
// Use of counter and randomBits means content of block will be dependent
// on the generation order. For (file-)systems that do not support sparse
// files, these can be removed so the test file can be reused and doesn't
// have to be generated for every run.
static qint64 counter = 0;
qint64 randomBits = QRandomGenerator::global()->generate64();
appendRaw(block, randomBits);
appendRaw(block, userBits);
appendRaw(block, counter);
appendRaw(block, (qint32)0xdeadbeef);
appendRaw(block, blockSize);
QByteArray userContent = text.toUtf8();
appendRaw(block, userContent.size());
block.append(userContent);
appendRaw(block, (qint64)0);
// size, so far
appendRaw(block, block.size());
QByteArray filler("0123456789");
block.append(filler.right(10 - block.size() % 10));
topUpWith(block, filler, blockSize - 3 * sizeof(qint64));
appendRaw(block, counter);
appendRaw(block, userBits);
appendRaw(block, randomBits);
++counter;
return block;
}
/*
Generates data blocks the first time they are requested. Keeps copies for reuse.
*/
QByteArray const &tst_LargeFile::getDataBlock(int index, qint64 position)
{
if (index >= generatedBlocks.size())
generatedBlocks.resize(index + 1);
if (generatedBlocks[index].isNull()) {
QString text = QString("Current %1-byte block (index = %2) "
"starts %3 bytes into the file '%4'.")
.arg(blockSize)
.arg(index)
.arg(position)
.arg("qt_largefile.tmp");
generatedBlocks[index] = generateDataBlock(blockSize, text, (qint64)1 << index);
}
return generatedBlocks[index];
}
void tst_LargeFile::initTestCase()
{
m_previousCurrent = QDir::currentPath();
m_tempDir = QSharedPointer<QTemporaryDir>::create();
QVERIFY2(!m_tempDir.isNull(), qPrintable("Could not create temporary directory."));
QVERIFY2(QDir::setCurrent(m_tempDir->path()), qPrintable("Could not switch current directory"));
QFile file("qt_largefile.tmp");
QVERIFY( !file.exists() || file.remove() );
}
void tst_LargeFile::cleanupTestCase()
{
if (largeFile.isOpen())
largeFile.close();
QFile file("qt_largefile.tmp");
QVERIFY( !file.exists() || file.remove() );
QDir::setCurrent(m_previousCurrent);
}
void tst_LargeFile::init()
{
fd_ = -1;
stream_ = 0;
}
void tst_LargeFile::cleanup()
{
if (-1 != fd_)
QT_CLOSE(fd_);
if (stream_)
::fclose(stream_);
}
void tst_LargeFile::sparseFileData()
{
QTest::addColumn<int>("index");
QTest::addColumn<qint64>("position");
QTest::addColumn<QByteArray>("block");
QTest::newRow(QString("block[%1] @%2)")
.arg(0).arg(0)
.toLocal8Bit().constData())
<< 0 << (qint64)0 << getDataBlock(0, 0);
// While on Linux sparse files scale well, on Windows, testing at every
// power of 2 leads to very large files. i += 4 gives us a good coverage
// without taxing too much on resources.
for (int index = 12; index <= maxSizeBits; index += 4) {
qint64 position = (qint64)1 << index;
QByteArray block = getDataBlock(index, position);
QTest::newRow(
QString("block[%1] @%2)")
.arg(index).arg(position)
.toLocal8Bit().constData())
<< index << position << block;
}
}
void tst_LargeFile::createSparseFile()
{
#if defined(Q_OS_WIN32)
// On Windows platforms, we must explicitly set the file to be sparse,
// so disk space is not allocated for the full file when writing to it.
HANDLE handle = ::CreateFileA("qt_largefile.tmp",
GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 0, 0);
QVERIFY( INVALID_HANDLE_VALUE != handle );
DWORD bytes;
if (!::DeviceIoControl(handle, FSCTL_SET_SPARSE, NULL, 0, NULL, 0,
&bytes, NULL)) {
qWarning("Unable to set test file as sparse. "
"Limiting test file to 16MiB.");
maxSizeBits = 24;
}
int fd = ::_open_osfhandle((intptr_t)handle, 0);
QVERIFY( -1 != fd );
QVERIFY( largeFile.open(fd, QIODevice::WriteOnly | QIODevice::Unbuffered) );
#else // !Q_OS_WIN32
largeFile.setFileName("qt_largefile.tmp");
QVERIFY( largeFile.open(QIODevice::WriteOnly | QIODevice::Unbuffered) );
#endif
}
void tst_LargeFile::closeSparseFile()
{
#if defined(Q_OS_WIN32)
int fd = largeFile.handle();
#endif
largeFile.close();
#if defined(Q_OS_WIN32)
if (-1 != fd)
::_close(fd);
#endif
}
void tst_LargeFile::fillFileSparsely()
{
QFETCH( qint64, position );
QFETCH( QByteArray, block );
QCOMPARE( block.size(), blockSize );
static int lastKnownGoodIndex = 0;
struct ScopeGuard {
ScopeGuard(tst_LargeFile* test)
: this_(test)
, failed(true)
{
QFETCH( int, index );
index_ = index;
}
~ScopeGuard()
{
if (failed) {
this_->maxSizeBits = lastKnownGoodIndex;
qWarning("QFile::error %d: '%s'. Maximum size bits reset to %d.",
this_->largeFile.error(),
qPrintable(this_->largeFile.errorString()),
this_->maxSizeBits);
} else
lastKnownGoodIndex = qMax<int>(index_, lastKnownGoodIndex);
}
private:
tst_LargeFile * const this_;
int index_;
public:
bool failed;
};
ScopeGuard resetMaxSizeBitsOnFailure(this);
QVERIFY( largeFile.seek(position) );
QCOMPARE( largeFile.pos(), position );
QCOMPARE( largeFile.write(block), (qint64)blockSize );
QCOMPARE( largeFile.pos(), position + blockSize );
QVERIFY( largeFile.flush() );
resetMaxSizeBitsOnFailure.failed = false;
}
void tst_LargeFile::fileCreated()
{
QFileInfo info("qt_largefile.tmp");
QVERIFY( info.exists() );
QVERIFY( info.isFile() );
QVERIFY( info.size() >= ((qint64)1 << maxSizeBits) + blockSize );
}
void tst_LargeFile::filePositioning()
{
QFETCH( qint64, position );
QFile file("qt_largefile.tmp");
QVERIFY( file.open(QIODevice::ReadOnly) );
QVERIFY( file.seek(position) );
QCOMPARE( file.pos(), position );
}
void tst_LargeFile::fdPositioning()
{
QFETCH( qint64, position );
fd_ = QT_OPEN("qt_largefile.tmp",
QT_OPEN_RDONLY | QT_OPEN_LARGEFILE);
QVERIFY( -1 != fd_ );
QFile file;
QVERIFY( file.open(fd_, QIODevice::ReadOnly) );
QCOMPARE( file.pos(), (qint64)0 );
QVERIFY( file.seek(position) );
QCOMPARE( file.pos(), position );
file.close();
QCOMPARE( QT_OFF_T(QT_LSEEK(fd_, QT_OFF_T(0), SEEK_SET)), QT_OFF_T(0) );
QCOMPARE( QT_OFF_T(QT_LSEEK(fd_, QT_OFF_T(position), SEEK_SET)), QT_OFF_T(position) );
QVERIFY( file.open(fd_, QIODevice::ReadOnly) );
QCOMPARE( QT_OFF_T(QT_LSEEK(fd_, QT_OFF_T(0), SEEK_CUR)), QT_OFF_T(position) );
QCOMPARE( file.pos(), position );
QVERIFY( file.seek(0) );
QCOMPARE( file.pos(), (qint64)0 );
file.close();
QVERIFY( !QT_CLOSE(fd_) );
fd_ = -1;
}
void tst_LargeFile::streamPositioning()
{
QFETCH( qint64, position );
stream_ = QT_FOPEN("qt_largefile.tmp", "rb");
QVERIFY( 0 != stream_ );
QFile file;
QVERIFY( file.open(stream_, QIODevice::ReadOnly) );
QCOMPARE( file.pos(), (qint64)0 );
QVERIFY( file.seek(position) );
QCOMPARE( file.pos(), position );
file.close();
QVERIFY( !QT_FSEEK(stream_, QT_OFF_T(0), SEEK_SET) );
QCOMPARE( QT_OFF_T(QT_FTELL(stream_)), QT_OFF_T(0) );
QVERIFY( !QT_FSEEK(stream_, QT_OFF_T(position), SEEK_SET) );
QCOMPARE( QT_OFF_T(QT_FTELL(stream_)), QT_OFF_T(position) );
QVERIFY( file.open(stream_, QIODevice::ReadOnly) );
QCOMPARE( QT_OFF_T(QT_FTELL(stream_)), QT_OFF_T(position) );
QCOMPARE( file.pos(), position );
QVERIFY( file.seek(0) );
QCOMPARE( file.pos(), (qint64)0 );
file.close();
QVERIFY( !::fclose(stream_) );
stream_ = 0;
}
void tst_LargeFile::openFileForReading()
{
largeFile.setFileName("qt_largefile.tmp");
QVERIFY( largeFile.open(QIODevice::ReadOnly) );
}
void tst_LargeFile::readFile()
{
QFETCH( qint64, position );
QFETCH( QByteArray, block );
QCOMPARE( block.size(), blockSize );
QVERIFY( largeFile.size() >= position + blockSize );
QVERIFY( largeFile.seek(position) );
QCOMPARE( largeFile.pos(), position );
QCOMPARE( largeFile.read(blockSize), block );
QCOMPARE( largeFile.pos(), position + blockSize );
}
void tst_LargeFile::mapFile()
{
QFETCH( qint64, position );
QFETCH( QByteArray, block );
QCOMPARE( block.size(), blockSize );
// Keep full block mapped to facilitate OS and/or internal reuse by Qt.
uchar *baseAddress = largeFile.map(position, blockSize);
QVERIFY( baseAddress );
QVERIFY( std::equal(block.begin(), block.end(), reinterpret_cast<char*>(baseAddress)) );
for (int offset = 1; offset < blockSize; ++offset) {
uchar *address = largeFile.map(position + offset, blockSize - offset);
QVERIFY( address );
if ( !std::equal(block.begin() + offset, block.end(), reinterpret_cast<char*>(address)) ) {
qDebug() << "Expected:" << block.toHex();
qDebug() << "Actual :" << QByteArray(reinterpret_cast<char*>(address), blockSize).toHex();
QVERIFY(false);
}
QVERIFY( largeFile.unmap( address ) );
}
QVERIFY( largeFile.unmap( baseAddress ) );
}
//Mac: memory-mapping beyond EOF may succeed but it could generate bus error on access
//FreeBSD: same
//Linux: memory-mapping beyond EOF usually succeeds, but depends on the filesystem
// 32-bit: limited to 44-bit offsets (when sizeof(off_t) == 8)
//Windows: memory-mapping beyond EOF is not allowed
void tst_LargeFile::mapOffsetOverflow()
{
enum {
#ifdef Q_OS_WIN
Succeeds = false,
MaxOffset = 63
#else
Succeeds = true,
# if (defined(Q_OS_LINUX) || defined(Q_OS_ANDROID)) && Q_PROCESSOR_WORDSIZE == 4
MaxOffset = sizeof(QT_OFF_T) > 4 ? 43 : 30
# else
MaxOffset = 8 * sizeof(QT_OFF_T) - 1
# endif
#endif
};
QByteArray zeroPage(blockSize, '\0');
for (int i = maxSizeBits + 1; i < 63; ++i) {
bool succeeds = Succeeds && (i <= MaxOffset);
uchar *address = 0;
qint64 offset = Q_INT64_C(1) << i;
if (succeeds)
QTest::ignoreMessage(QtWarningMsg, "QFSFileEngine::map: Mapping a file beyond its size is not portable");
address = largeFile.map(offset, blockSize);
QCOMPARE(!!address, succeeds);
if (succeeds)
QTest::ignoreMessage(QtWarningMsg, "QFSFileEngine::map: Mapping a file beyond its size is not portable");
address = largeFile.map(offset + blockSize, blockSize);
QCOMPARE(!!address, succeeds);
}
}
QTEST_APPLESS_MAIN(tst_LargeFile)
#include "tst_largefile.moc"

View File

@ -0,0 +1,26 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qabstractfileengine Test:
#####################################################################
qt_internal_add_test(tst_qabstractfileengine
SOURCES
tst_qabstractfileengine.cpp
LIBRARIES
Qt::CorePrivate
)
# Resources:
set(qabstractfileengine_resource_files
"resources/"
)
qt_internal_add_resource(tst_qabstractfileengine "qabstractfileengine"
PREFIX
"/tst_qabstractfileengine/"
FILES
${qabstractfileengine_resource_files}
)

View File

@ -0,0 +1 @@
This is a simple text file.

View File

@ -0,0 +1,836 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/private/qabstractfileengine_p.h>
#include <QtCore/private/qfsfileengine_p.h>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <QtCore/QSharedPointer>
#include <QtCore/QScopedPointer>
#include <QtCore/QHash>
#include <QtCore/QDir>
#include <QtCore/QDirIterator>
#include <QtTest/QTest>
#include <QtCore/QDebug>
#include "../../../../shared/filesystem.h"
class tst_QAbstractFileEngine
: public QObject
{
Q_OBJECT
public slots:
void initTestCase();
void cleanupTestCase();
private slots:
void customHandler();
void fileIO_data();
void fileIO();
void mounting_data();
void mounting();
private:
QStringList filesForRemoval;
QSharedPointer<QTemporaryDir> m_currentDir;
QString m_previousCurrent;
};
class ReferenceFileEngine
: public QAbstractFileEngine
{
public:
ReferenceFileEngine(const QString &fileName)
: fileName_(QDir::cleanPath(fileName))
, position_(-1)
, openForRead_(false)
, openForWrite_(false)
{
}
bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override
{
Q_UNUSED(permissions);
if (openForRead_ || openForWrite_) {
qWarning("%s: file is already open for %s",
Q_FUNC_INFO,
(openForRead_ ? "reading" : "writing"));
return false;
}
openFile_ = resolveFile(openMode & QIODevice::WriteOnly);
if (!openFile_)
return false;
position_ = 0;
if (openMode & QIODevice::ReadOnly)
openForRead_ = true;
if (openMode & QIODevice::WriteOnly) {
openForWrite_ = true;
QMutexLocker lock(&openFile_->mutex);
if (openMode & QIODevice::Truncate
|| !(openForRead_ || openMode & QIODevice::Append))
openFile_->content.clear();
if (openMode & QIODevice::Append)
position_ = openFile_->content.size();
}
return true;
}
bool close() override
{
openFile_.clear();
openForRead_ = false;
openForWrite_ = false;
position_ = -1;
return true;
}
qint64 size() const override
{
QSharedPointer<File> file = resolveFile(false);
if (!file)
return 0;
QMutexLocker lock(&file->mutex);
return file->content.size();
}
qint64 pos() const override
{
if (!openForRead_ && !openForWrite_) {
qWarning("%s: file is not open", Q_FUNC_INFO);
return -1;
}
return position_;
}
bool seek(qint64 pos) override
{
if (!openForRead_ && !openForWrite_) {
qWarning("%s: file is not open", Q_FUNC_INFO);
return false;
}
if (pos >= 0) {
position_ = pos;
return true;
}
return false;
}
bool flush() override
{
if (!openForRead_ && !openForWrite_) {
qWarning("%s: file is not open", Q_FUNC_INFO);
return false;
}
return true;
}
bool remove() override
{
QMutexLocker lock(&fileSystemMutex);
int count = fileSystem.remove(fileName_);
return (count == 1);
}
bool copy(const QString &newName) override
{
QMutexLocker lock(&fileSystemMutex);
if (!fileSystem.contains(fileName_)
|| fileSystem.contains(newName))
return false;
fileSystem.insert(newName, fileSystem.value(fileName_));
return true;
}
bool rename(const QString &newName) override
{
QMutexLocker lock(&fileSystemMutex);
if (!fileSystem.contains(fileName_)
|| fileSystem.contains(newName))
return false;
fileSystem.insert(newName, fileSystem.take(fileName_));
return true;
}
bool setSize(qint64 size) override
{
if (size < 0)
return false;
QSharedPointer<File> file = resolveFile(false);
if (!file)
return false;
QMutexLocker lock(&file->mutex);
file->content.resize(size);
if (openForRead_ || openForWrite_)
if (position_ > size)
position_ = size;
return (file->content.size() == size);
}
FileFlags fileFlags(FileFlags type) const override
{
QSharedPointer<File> file = resolveFile(false);
if (file) {
QMutexLocker lock(&file->mutex);
return (file->fileFlags & type);
}
return FileFlags();
}
QString fileName(FileName file) const override
{
switch (file) {
case DefaultName:
return QLatin1String("DefaultName");
case BaseName:
return QLatin1String("BaseName");
case PathName:
return QLatin1String("PathName");
case AbsoluteName:
return QLatin1String("AbsoluteName");
case AbsolutePathName:
return QLatin1String("AbsolutePathName");
case AbsoluteLinkTarget:
return QLatin1String("AbsoluteLinkTarget");
case CanonicalName:
return QLatin1String("CanonicalName");
case CanonicalPathName:
return QLatin1String("CanonicalPathName");
case BundleName:
return QLatin1String("BundleName");
default:
break;
}
return QString();
}
uint ownerId(FileOwner owner) const override
{
QSharedPointer<File> file = resolveFile(false);
if (file) {
switch (owner) {
case OwnerUser:
{
QMutexLocker lock(&file->mutex);
return file->userId;
}
case OwnerGroup:
{
QMutexLocker lock(&file->mutex);
return file->groupId;
}
}
}
return -2;
}
QString owner(FileOwner owner) const override
{
QSharedPointer<File> file = resolveFile(false);
if (file) {
uint ownerId;
switch (owner) {
case OwnerUser:
{
QMutexLocker lock(&file->mutex);
ownerId = file->userId;
}
{
QMutexLocker lock(&fileSystemMutex);
return fileSystemUsers.value(ownerId);
}
case OwnerGroup:
{
QMutexLocker lock(&file->mutex);
ownerId = file->groupId;
}
{
QMutexLocker lock(&fileSystemMutex);
return fileSystemGroups.value(ownerId);
}
}
}
return QString();
}
QDateTime fileTime(FileTime time) const override
{
QSharedPointer<File> file = resolveFile(false);
if (file) {
QMutexLocker lock(&file->mutex);
switch (time) {
case BirthTime:
return file->birth;
case MetadataChangeTime:
return file->change;
case ModificationTime:
return file->modification;
case AccessTime:
return file->access;
}
}
return QDateTime();
}
void setFileName(const QString &file) override
{
if (openForRead_ || openForWrite_)
qWarning("%s: Can't set file name while file is open", Q_FUNC_INFO);
else
fileName_ = file;
}
qint64 read(char *data, qint64 maxLen) override
{
if (!openForRead_) {
qWarning("%s: file must be open for reading", Q_FUNC_INFO);
return -1;
}
if (openFile_.isNull()) {
qWarning("%s: file must not be null", Q_FUNC_INFO);
return -1;
}
QMutexLocker lock(&openFile_->mutex);
qint64 readSize = qMin(openFile_->content.size() - position_, maxLen);
if (readSize < 0)
return -1;
memcpy(data, openFile_->content.constData() + position_, readSize);
position_ += readSize;
return readSize;
}
qint64 write(const char *data, qint64 length) override
{
if (!openForWrite_) {
qWarning("%s: file must be open for writing", Q_FUNC_INFO);
return -1;
}
if (openFile_.isNull()) {
qWarning("%s: file must not be null", Q_FUNC_INFO);
return -1;
}
if (length < 0)
return -1;
QMutexLocker lock(&openFile_->mutex);
if (openFile_->content.size() == position_)
openFile_->content.append(data, length);
else {
if (position_ + length > openFile_->content.size())
openFile_->content.resize(position_ + length);
openFile_->content.replace(position_, length, data, length);
}
qint64 writeSize = qMin(length, openFile_->content.size() - position_);
position_ += writeSize;
return writeSize;
}
protected:
// void setError(QFile::FileError error, const QString &str);
struct File
{
File()
: userId(0)
, groupId(0)
, fileFlags(
ReadOwnerPerm | WriteOwnerPerm | ExeOwnerPerm
| ReadUserPerm | WriteUserPerm | ExeUserPerm
| ReadGroupPerm | WriteGroupPerm | ExeGroupPerm
| ReadOtherPerm | WriteOtherPerm | ExeOtherPerm
| FileType | ExistsFlag)
{
}
QMutex mutex;
uint userId, groupId;
QAbstractFileEngine::FileFlags fileFlags;
QDateTime birth, change, modification, access;
QByteArray content;
};
QSharedPointer<File> resolveFile(bool create) const
{
if (openForRead_ || openForWrite_) {
if (!openFile_)
qWarning("%s: file should not be null", Q_FUNC_INFO);
return openFile_;
}
QMutexLocker lock(&fileSystemMutex);
if (create) {
QSharedPointer<File> &p = fileSystem[fileName_];
if (p.isNull())
p = QSharedPointer<File>::create();
return p;
}
return fileSystem.value(fileName_);
}
static QMutex fileSystemMutex;
static QHash<uint, QString> fileSystemUsers, fileSystemGroups;
static QHash<QString, QSharedPointer<File> > fileSystem;
private:
QString fileName_;
qint64 position_;
bool openForRead_;
bool openForWrite_;
mutable QSharedPointer<File> openFile_;
};
class MountingFileEngine : public QFSFileEngine
{
public:
class Iterator : public QAbstractFileEngineIterator
{
public:
Iterator(QDir::Filters filters, const QStringList &filterNames)
: QAbstractFileEngineIterator(filters, filterNames)
{
names.append("foo");
names.append("bar");
index = -1;
}
QString currentFileName() const override
{
return names.at(index);
}
bool hasNext() const override
{
return index < names.size() - 1;
}
QString next() override
{
if (!hasNext())
return QString();
++index;
return currentFilePath();
}
QStringList names;
int index;
};
MountingFileEngine(QString fileName)
: QFSFileEngine(fileName)
{
}
Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
{
return new Iterator(filters, filterNames);
}
FileFlags fileFlags(FileFlags type) const override
{
if (fileName(DefaultName).endsWith(".tar")) {
FileFlags ret = QFSFileEngine::fileFlags(type);
//make this file in file system appear to be a directory
ret &= ~FileType;
ret |= DirectoryType;
return ret;
} else {
//file inside the archive
return ExistsFlag | FileType;
}
}
};
QMutex ReferenceFileEngine::fileSystemMutex;
QHash<uint, QString> ReferenceFileEngine::fileSystemUsers, ReferenceFileEngine::fileSystemGroups;
QHash<QString, QSharedPointer<ReferenceFileEngine::File> > ReferenceFileEngine::fileSystem;
class FileEngineHandler
: QAbstractFileEngineHandler
{
QAbstractFileEngine *create(const QString &fileName) const override
{
if (fileName.endsWith(".tar") || fileName.contains(".tar/"))
return new MountingFileEngine(fileName);
if (fileName.startsWith("QFSFileEngine:"))
return new QFSFileEngine(fileName.mid(14));
if (fileName.startsWith("reference-file-engine:"))
return new ReferenceFileEngine(fileName.mid(22));
if (fileName.startsWith("resource:"))
return QAbstractFileEngine::create(QLatin1String(":/tst_qabstractfileengine/resources/") + fileName.mid(9));
return 0;
}
};
void tst_QAbstractFileEngine::initTestCase()
{
m_previousCurrent = QDir::currentPath();
m_currentDir = QSharedPointer<QTemporaryDir>::create();
QVERIFY2(!m_currentDir.isNull(), qPrintable("Could not create current directory."));
QDir::setCurrent(m_currentDir->path());
}
void tst_QAbstractFileEngine::cleanupTestCase()
{
bool failed = false;
FileEngineHandler handler;
Q_FOREACH(QString file, filesForRemoval)
if (!QFile::remove(file)
|| QFile::exists(file)) {
failed = true;
qDebug() << "Couldn't remove file:" << file;
}
QVERIFY(!failed);
QDir::setCurrent(m_previousCurrent);
}
void tst_QAbstractFileEngine::customHandler()
{
QScopedPointer<QAbstractFileEngine> file;
{
file.reset(QAbstractFileEngine::create("resource:file.txt"));
QVERIFY(file);
}
{
FileEngineHandler handler;
QFile file("resource:file.txt");
QVERIFY(file.exists());
}
{
QFile file("resource:file.txt");
QVERIFY(!file.exists());
}
}
void tst_QAbstractFileEngine::fileIO_data()
{
QTest::addColumn<QString>("fileName");
QTest::addColumn<QByteArray>("readContent");
QTest::addColumn<QByteArray>("writeContent");
QTest::addColumn<bool>("fileExists");
QString resourceTxtFile(":/tst_qabstractfileengine/resources/file.txt");
QByteArray readContent("This is a simple text file.\n");
QByteArray writeContent("This contains two lines of text.\n");
QTest::newRow("resource") << resourceTxtFile << readContent << QByteArray() << true;
QTest::newRow("native") << "native-file.txt" << readContent << writeContent << false;
QTest::newRow("Forced QFSFileEngine") << "QFSFileEngine:QFSFileEngine-file.txt" << readContent << writeContent << false;
QTest::newRow("Custom FE") << "reference-file-engine:file.txt" << readContent << writeContent << false;
QTest::newRow("Forced QFSFileEngine (native)") << "QFSFileEngine:native-file.txt" << readContent << writeContent << true;
QTest::newRow("native (Forced QFSFileEngine)") << "QFSFileEngine-file.txt" << readContent << writeContent << true;
QTest::newRow("Custom FE (2)") << "reference-file-engine:file.txt" << readContent << writeContent << true;
}
void tst_QAbstractFileEngine::fileIO()
{
QFETCH(QString, fileName);
QFETCH(QByteArray, readContent);
QFETCH(QByteArray, writeContent);
QFETCH(bool, fileExists);
FileEngineHandler handler;
{
QFile file(fileName);
QCOMPARE(file.exists(), fileExists);
if (!fileExists) {
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Unbuffered));
filesForRemoval.append(fileName);
QCOMPARE(file.write(readContent), qint64(readContent.size()));
}
}
//
// File content is: readContent
//
qint64 fileSize = readContent.size();
{
// Reading
QFile file(fileName);
QVERIFY(!file.isOpen());
/* For an exact match, this test requires the repository to
* be checked out with UNIX-style line endings on Windows.
* Try to succeed also for the common case of checking out with autocrlf
* by reading the file as text and checking if the size matches
* the original size + the '\r' characters added by autocrlf. */
QFile::OpenMode openMode = QIODevice::ReadOnly | QIODevice::Unbuffered;
#ifdef Q_OS_WIN
openMode |= QIODevice::Text;
#endif
QVERIFY(file.open(openMode));
QVERIFY(file.isOpen());
#ifdef Q_OS_WIN
const qint64 convertedSize = fileSize + readContent.count('\n');
if (file.size() == convertedSize)
fileSize = convertedSize;
#endif
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.pos(), qint64(0));
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.readAll(), readContent);
QCOMPARE(file.pos(), fileSize);
file.close();
QVERIFY(!file.isOpen());
QCOMPARE(file.size(), fileSize);
}
if (writeContent.isEmpty())
return;
{
// Writing / appending
QFile file(fileName);
QVERIFY(!file.isOpen());
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Unbuffered));
QVERIFY(file.isOpen());
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.write(writeContent), qint64(writeContent.size()));
fileSize += writeContent.size();
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.size(), fileSize);
file.close();
QVERIFY(!file.isOpen());
QCOMPARE(file.size(), fileSize);
}
//
// File content is: readContent + writeContent
//
{
// Reading and Writing
QFile file(fileName);
QVERIFY(!file.isOpen());
QVERIFY(file.open(QIODevice::ReadWrite | QIODevice::Unbuffered));
QVERIFY(file.isOpen());
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.pos(), qint64(0));
QCOMPARE(file.readAll(), readContent + writeContent);
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.size(), fileSize);
QVERIFY(file.seek(writeContent.size()));
QCOMPARE(file.pos(), qint64(writeContent.size()));
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.write(readContent), qint64(readContent.size()));
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.size(), fileSize);
QVERIFY(file.seek(0));
QCOMPARE(file.pos(), qint64(0));
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.write(writeContent), qint64(writeContent.size()));
QCOMPARE(file.pos(), qint64(writeContent.size()));
QCOMPARE(file.size(), fileSize);
QVERIFY(file.seek(0));
QCOMPARE(file.read(writeContent.size()), writeContent);
QCOMPARE(file.pos(), qint64(writeContent.size()));
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.readAll(), readContent);
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.size(), fileSize);
file.close();
QVERIFY(!file.isOpen());
QCOMPARE(file.size(), fileSize);
}
//
// File content is: writeContent + readContent
//
{
// Writing
QFile file(fileName);
QVERIFY(!file.isOpen());
QVERIFY(file.open(QIODevice::ReadWrite | QIODevice::Unbuffered));
QVERIFY(file.isOpen());
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.pos(), qint64(0));
QCOMPARE(file.write(writeContent), qint64(writeContent.size()));
QCOMPARE(file.pos(), qint64(writeContent.size()));
QCOMPARE(file.size(), fileSize);
QVERIFY(file.resize(writeContent.size()));
QCOMPARE(file.size(), qint64(writeContent.size()));
file.close();
QVERIFY(!file.isOpen());
QCOMPARE(file.size(), qint64(writeContent.size()));
QVERIFY(file.resize(fileSize));
QCOMPARE(file.size(), fileSize);
}
//
// File content is: writeContent + <undefined>
// File size is : (readContent + writeContent).size()
//
{
// Writing / extending
QFile file(fileName);
QVERIFY(!file.isOpen());
QVERIFY(file.open(QIODevice::ReadWrite | QIODevice::Unbuffered));
QVERIFY(file.isOpen());
QCOMPARE(file.size(), fileSize);
QCOMPARE(file.pos(), qint64(0));
QVERIFY(file.seek(1024));
QCOMPARE(file.pos(), qint64(1024));
QCOMPARE(file.size(), fileSize);
fileSize = 1024 + writeContent.size();
QCOMPARE(file.write(writeContent), qint64(writeContent.size()));
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.size(), fileSize);
QVERIFY(file.seek(1028));
QCOMPARE(file.pos(), qint64(1028));
QCOMPARE(file.size(), fileSize);
fileSize = 1028 + writeContent.size();
QCOMPARE(file.write(writeContent), qint64(writeContent.size()));
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.size(), fileSize);
file.close();
QVERIFY(!file.isOpen());
QCOMPARE(file.size(), fileSize);
}
//
// File content is: writeContent + <undefined> + writeContent
// File size is : 1024 + writeContent.size()
//
{
// Writing / truncating
QFile file(fileName);
QVERIFY(!file.isOpen());
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Unbuffered));
QVERIFY(file.isOpen());
QCOMPARE(file.size(), qint64(0));
QCOMPARE(file.pos(), qint64(0));
fileSize = readContent.size();
QCOMPARE(file.write(readContent), fileSize);
QCOMPARE(file.pos(), fileSize);
QCOMPARE(file.size(), fileSize);
file.close();
QVERIFY(!file.isOpen());
QCOMPARE(file.size(), fileSize);
}
//
// File content is: readContent
//
}
void tst_QAbstractFileEngine::mounting_data()
{
QTest::addColumn<QString>("fileName");
QTest::newRow("native") << "test.tar";
QTest::newRow("Forced QFSFileEngine") << "QFSFileEngine:test.tar";
}
void tst_QAbstractFileEngine::mounting()
{
FileSystem fs;
QVERIFY(fs.createFile("test.tar"));
FileEngineHandler handler;
QFETCH(QString, fileName);
const QString absName = fs.absoluteFilePath(fileName);
QVERIFY(QFileInfo(absName).isDir());
QDir dir(absName);
QCOMPARE(dir.entryList(), (QStringList() << "bar" << "foo"));
QDir dir2(fs.path());
bool found = false;
foreach (QFileInfo info, dir2.entryInfoList()) {
if (info.fileName() == QLatin1String("test.tar")) {
QVERIFY(!found);
found = true;
QVERIFY(info.isDir());
}
}
QVERIFY(found);
}
QTEST_APPLESS_MAIN(tst_QAbstractFileEngine)
#include "tst_qabstractfileengine.moc"

View File

@ -0,0 +1,11 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qbuffer Test:
#####################################################################
qt_internal_add_test(tst_qbuffer
SOURCES
tst_qbuffer.cpp
)

View File

@ -0,0 +1,704 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QTest>
#include <QTestEventLoop>
#include <QBuffer>
#include <QByteArray>
#include <QElapsedTimer>
#include <string>
class tst_QBuffer : public QObject
{
Q_OBJECT
private slots:
void open();
void openWriteOnlyDoesNotTruncate();
void getSetCheck();
void readBlock();
void readBlockPastEnd();
void writeBlock_data();
void writeBlock();
void seek();
void invalidSeeks();
void seekTest_data();
void seekTest();
void read_rawdata();
void isSequential();
void signalTest_data();
void signalTest();
void isClosedAfterClose();
void readLine_data();
void readLine();
void canReadLine_data();
void canReadLine();
void atEnd();
void readLineBoundaries();
void getAndUngetChar();
void writeAfterQByteArrayResize();
void writeOfMoreThan2GiB();
void read_null();
protected slots:
void readyReadSlot();
void bytesWrittenSlot(qint64 written);
private:
qint64 totalBytesWritten;
bool gotReadyRead;
};
// Testing get/set functions
void tst_QBuffer::getSetCheck()
{
QBuffer obj1;
// const QByteArray & QBuffer::data()
// void QBuffer::setData(const QByteArray &)
QByteArray var1("Bogus data");
obj1.setData(var1);
QCOMPARE(var1, obj1.data());
obj1.setData(QByteArray());
QCOMPARE(QByteArray(), obj1.data());
}
void tst_QBuffer::open()
{
QByteArray data(10, 'f');
QBuffer b;
QTest::ignoreMessage(QtWarningMsg, "QBuffer::open: Buffer access not specified");
QVERIFY(!b.open(QIODevice::NotOpen));
QVERIFY(!b.isOpen());
b.close();
QTest::ignoreMessage(QtWarningMsg, "QBuffer::open: Buffer access not specified");
QVERIFY(!b.open(QIODevice::Text));
QVERIFY(!b.isOpen());
b.close();
QTest::ignoreMessage(QtWarningMsg, "QBuffer::open: Buffer access not specified");
QVERIFY(!b.open(QIODevice::Unbuffered));
QVERIFY(!b.isOpen());
b.close();
QVERIFY(b.open(QIODevice::ReadOnly));
QVERIFY(b.isReadable());
b.close();
QVERIFY(b.open(QIODevice::WriteOnly));
QVERIFY(b.isWritable());
b.close();
b.setData(data);
QVERIFY(b.open(QIODevice::Append));
QVERIFY(b.isWritable());
QCOMPARE(b.size(), qint64(10));
QCOMPARE(b.pos(), b.size());
b.close();
b.setData(data);
QVERIFY(b.open(QIODevice::Truncate));
QVERIFY(b.isWritable());
QCOMPARE(b.size(), qint64(0));
QCOMPARE(b.pos(), qint64(0));
b.close();
QVERIFY(b.open(QIODevice::ReadWrite));
QVERIFY(b.isReadable());
QVERIFY(b.isWritable());
b.close();
}
void tst_QBuffer::openWriteOnlyDoesNotTruncate()
{
QBuffer b;
const auto data = QByteArrayLiteral("Hey, presto!");
{
QVERIFY(b.open(QIODevice::WriteOnly));
b.write(data);
b.close();
}
{
QVERIFY(b.open(QIODevice::ReadOnly));
QCOMPARE(b.readAll(), data);
b.close();
}
{
QVERIFY(b.open(QIODevice::WriteOnly));
QCOMPARE(b.size(), data.size());
QCOMPARE(b.pos(), 0);
b.close();
}
}
// some status() tests, too
void tst_QBuffer::readBlock()
{
const int arraySize = 10;
char a[arraySize];
QBuffer b;
QCOMPARE(b.bytesAvailable(), (qint64) 0); // no data
QCOMPARE(b.read(a, arraySize), (qint64) -1); // not opened
QVERIFY(b.atEnd());
QByteArray ba;
ba.resize(arraySize);
b.setBuffer(&ba);
QCOMPARE(b.bytesAvailable(), (qint64) arraySize);
b.open(QIODevice::WriteOnly);
QCOMPARE(b.bytesAvailable(), (qint64) arraySize);
QTest::ignoreMessage(QtWarningMsg, "QIODevice::read (QBuffer): WriteOnly device");
QCOMPARE(b.read(a, arraySize), (qint64) -1); // no read access
b.close();
b.open(QIODevice::ReadOnly);
QCOMPARE(b.bytesAvailable(), (qint64) arraySize);
QCOMPARE(b.read(a, arraySize), (qint64) arraySize);
QVERIFY(b.atEnd());
QCOMPARE(b.bytesAvailable(), (qint64) 0);
// up to 3.0.x reading beyond the end was an error while ok
// this has been made consistent with other QIODevice sub classes in 3.1
QCOMPARE(b.read(a, 1), qint64(0));
QVERIFY(b.atEnd());
// read in two chunks
b.close();
b.open(QIODevice::ReadOnly);
QCOMPARE(b.bytesAvailable(), (qint64) arraySize);
QCOMPARE(b.read(a, arraySize/2), (qint64) arraySize/2);
QCOMPARE(b.bytesAvailable(), (qint64) arraySize/2);
QCOMPARE(b.read(a + arraySize/2, arraySize - arraySize/2),
(qint64)(arraySize - arraySize/2));
QVERIFY(b.atEnd());
QCOMPARE(b.bytesAvailable(), (qint64) 0);
}
void tst_QBuffer::readBlockPastEnd()
{
QByteArray arr(4096 + 3616, 'd');
QBuffer buf(&arr);
buf.open(QIODevice::ReadOnly);
char dummy[4096];
buf.read(1);
QCOMPARE(buf.read(dummy, 4096), qint64(4096));
QCOMPARE(buf.read(dummy, 4096), qint64(3615));
QVERIFY(buf.atEnd());
}
void tst_QBuffer::writeBlock_data()
{
QTest::addColumn<QString>("str");
QTest::newRow( "small_bytearray" ) << QString("Test");
QTest::newRow( "large_bytearray" ) << QString(
"The QBuffer class is an I/O device that operates on a QByteArray.\n"
"QBuffer is used to read and write to a memory buffer. It is normally "
"used with a QTextStream or a QDataStream. QBuffer has an associated "
"QByteArray which holds the buffer data. The size() of the buffer is "
"automatically adjusted as data is written.\n"
"The constructor QBuffer(QByteArray) creates a QBuffer using an existing "
"byte array. The byte array can also be set with setBuffer(). Writing to "
"the QBuffer will modify the original byte array because QByteArray is "
"explicitly shared.\n"
"Use open() to open the buffer before use and to set the mode (read-only, "
"write-only, etc.). close() closes the buffer. The buffer must be closed "
"before reopening or calling setBuffer().\n"
"A common way to use QBuffer is through QDataStream or QTextStream, which "
"have constructors that take a QBuffer parameter. For convenience, there "
"are also QDataStream and QTextStream constructors that take a QByteArray "
"parameter. These constructors create and open an internal QBuffer.\n"
"Note that QTextStream can also operate on a QString (a Unicode string); a "
"QBuffer cannot.\n"
"You can also use QBuffer directly through the standard QIODevice functions "
"readBlock(), writeBlock() readLine(), at(), getch(), putch() and ungetch().\n"
"See also QFile, QDataStream, QTextStream, QByteArray, Shared Classes, Collection "
"Classes and Input/Output and Networking.\n\n"
"The QBuffer class is an I/O device that operates on a QByteArray.\n"
"QBuffer is used to read and write to a memory buffer. It is normally "
"used with a QTextStream or a QDataStream. QBuffer has an associated "
"QByteArray which holds the buffer data. The size() of the buffer is "
"automatically adjusted as data is written.\n"
"The constructor QBuffer(QByteArray) creates a QBuffer using an existing "
"byte array. The byte array can also be set with setBuffer(). Writing to "
"the QBuffer will modify the original byte array because QByteArray is "
"explicitly shared.\n"
"Use open() to open the buffer before use and to set the mode (read-only, "
"write-only, etc.). close() closes the buffer. The buffer must be closed "
"before reopening or calling setBuffer().\n"
"A common way to use QBuffer is through QDataStream or QTextStream, which "
"have constructors that take a QBuffer parameter. For convenience, there "
"are also QDataStream and QTextStream constructors that take a QByteArray "
"parameter. These constructors create and open an internal QBuffer.\n"
"Note that QTextStream can also operate on a QString (a Unicode string); a "
"QBuffer cannot.\n"
"You can also use QBuffer directly through the standard QIODevice functions "
"readBlock(), writeBlock() readLine(), at(), getch(), putch() and ungetch().\n"
"See also QFile, QDataStream, QTextStream, QByteArray, Shared Classes, Collection "
"Classes and Input/Output and Networking.\n\n"
"The QBuffer class is an I/O device that operates on a QByteArray.\n"
"QBuffer is used to read and write to a memory buffer. It is normally "
"used with a QTextStream or a QDataStream. QBuffer has an associated "
"QByteArray which holds the buffer data. The size() of the buffer is "
"automatically adjusted as data is written.\n"
"The constructor QBuffer(QByteArray) creates a QBuffer using an existing "
"byte array. The byte array can also be set with setBuffer(). Writing to "
"the QBuffer will modify the original byte array because QByteArray is "
"explicitly shared.\n"
"Use open() to open the buffer before use and to set the mode (read-only, "
"write-only, etc.). close() closes the buffer. The buffer must be closed "
"before reopening or calling setBuffer().\n"
"A common way to use QBuffer is through QDataStream or QTextStream, which "
"have constructors that take a QBuffer parameter. For convenience, there "
"are also QDataStream and QTextStream constructors that take a QByteArray "
"parameter. These constructors create and open an internal QBuffer.\n"
"Note that QTextStream can also operate on a QString (a Unicode string); a "
"QBuffer cannot.\n"
"You can also use QBuffer directly through the standard QIODevice functions "
"readBlock(), writeBlock() readLine(), at(), getch(), putch() and ungetch().\n"
"See also QFile, QDataStream, QTextStream, QByteArray, Shared Classes, Collection "
"Classes and Input/Output and Networking.");
}
void tst_QBuffer::writeBlock()
{
QFETCH( QString, str );
QByteArray ba;
QBuffer buf( &ba );
buf.open(QIODevice::ReadWrite);
QByteArray data = str.toLatin1();
QCOMPARE(buf.write( data.constData(), data.size() ), qint64(data.size()));
QCOMPARE(buf.data(), str.toLatin1());
}
void tst_QBuffer::seek()
{
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
QCOMPARE(buffer.size(), qint64(0));
QCOMPARE(buffer.pos(), qint64(0));
const qint64 pos = 10;
QVERIFY(buffer.seek(pos));
QCOMPARE(buffer.size(), pos);
}
void tst_QBuffer::invalidSeeks()
{
if constexpr (sizeof(qsizetype) == sizeof(qint64)) {
// sizeof(qsizetype) == sizeof(qint64), so +1 would overflow
QSKIP("This is a 32-bit-only test.");
} else {
QBuffer buffer;
buffer.open(QIODevice::WriteOnly);
QCOMPARE(buffer.buffer().size(), qsizetype(0));
QCOMPARE(buffer.pos(), qint64(0));
constexpr qint64 MaxQByteArrayCapacity = (std::numeric_limits<qsizetype>::max)();
// this should fail fast, not after trying to allocate nearly 2 GiB of data,
// potentially crashing in the process:
QVERIFY(!buffer.seek(2 * MaxQByteArrayCapacity - 1));
QCOMPARE(buffer.buffer().size(), qsizetype(0));
QCOMPARE(buffer.pos(), qint64(0));
// ditto:
QVERIFY(!buffer.seek(MaxQByteArrayCapacity + 1));
QCOMPARE(buffer.buffer().size(), qsizetype(0));
QCOMPARE(buffer.pos(), qint64(0));
}
}
void tst_QBuffer::seekTest_data()
{
writeBlock_data();
}
#define DO_VALID_SEEK(position) { \
char c; \
QVERIFY(buf.seek(qint64(position))); \
QCOMPARE(buf.pos(), qint64(position)); \
QVERIFY(buf.getChar(&c)); \
QCOMPARE(QChar(c), str.at(qint64(position))); \
}
#define DO_INVALID_SEEK(position) { \
qint64 prev_pos = buf.pos(); \
QVERIFY(!buf.seek(qint64(position))); \
QCOMPARE(buf.pos(), prev_pos); /* position should not be changed */ \
}
void tst_QBuffer::seekTest()
{
QFETCH(QString, str);
QByteArray ba;
QBuffer buf(&ba);
QCOMPARE(buf.pos(), qint64(0));
buf.open(QIODevice::ReadWrite);
QCOMPARE(buf.pos(), qint64(0));
QCOMPARE(buf.bytesAvailable(), qint64(0));
QByteArray data = str.toLatin1();
QCOMPARE(buf.write( data.constData(), data.size() ), qint64(data.size()));
QCOMPARE(buf.bytesAvailable(), qint64(0)); // we're at the end
QCOMPARE(buf.size(), qint64(data.size()));
QTest::ignoreMessage(QtWarningMsg, "QBuffer::seek: Invalid pos: -1");
DO_INVALID_SEEK(-1);
DO_VALID_SEEK(0);
DO_VALID_SEEK(str.size() - 1);
QVERIFY(buf.atEnd());
DO_VALID_SEEK(str.size() / 2);
// Special case: valid to seek one position past the buffer.
// Its then legal to write, but not read.
{
char c = 'a';
QVERIFY(buf.seek(qint64(str.size())));
QCOMPARE(buf.bytesAvailable(), qint64(0));
QCOMPARE(buf.read(&c, qint64(1)), qint64(0));
QCOMPARE(c, 'a');
QCOMPARE(buf.write(&c, qint64(1)), qint64(1));
}
// Special case 2: seeking to an arbitrary position beyond the buffer auto-expands it
{
char c;
const int offset = 1; // any positive integer will do
const qint64 pos = buf.size() + offset;
QVERIFY(buf.seek(pos));
QCOMPARE(buf.bytesAvailable(), qint64(0));
QCOMPARE(buf.pos(), pos);
QVERIFY(!buf.getChar(&c));
QVERIFY(buf.seek(pos - 1));
QVERIFY(buf.getChar(&c));
QCOMPARE(c, buf.data().at(pos - 1));
QVERIFY(buf.seek(pos));
QVERIFY(buf.putChar(c));
}
}
void tst_QBuffer::read_rawdata()
{
static const unsigned char mydata[] = {
0x01, 0x00, 0x03, 0x84, 0x78, 0x9c, 0x3b, 0x76,
0xec, 0x18, 0xc3, 0x31, 0x0a, 0xf1, 0xcc, 0x99,
0x6d, 0x5b
};
QByteArray data = QByteArray::fromRawData((const char *)mydata, sizeof(mydata));
QBuffer buffer(&data);
buffer.open(QIODevice::ReadOnly);
QDataStream in(&buffer);
quint8 ch;
for (int i = 0; i < (int)sizeof(mydata); ++i) {
QVERIFY(!buffer.atEnd());
in >> ch;
QCOMPARE(ch, (quint8)mydata[i]);
}
QVERIFY(buffer.atEnd());
}
void tst_QBuffer::isSequential()
{
QBuffer buf;
QVERIFY(!buf.isSequential());
}
void tst_QBuffer::signalTest_data()
{
QTest::addColumn<QByteArray>("sample");
QTest::newRow("empty") << QByteArray();
QTest::newRow("size 1") << QByteArray("1");
QTest::newRow("size 2") << QByteArray("11");
QTest::newRow("size 100") << QByteArray(100, '1');
}
void tst_QBuffer::signalTest()
{
QFETCH(QByteArray, sample);
totalBytesWritten = 0;
QBuffer buf;
buf.open(QIODevice::WriteOnly);
buf.buffer().resize(sample.size() * 10);
connect(&buf, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
connect(&buf, SIGNAL(bytesWritten(qint64)), this, SLOT(bytesWrittenSlot(qint64)));
for (int i = 0; i < 10; ++i) {
gotReadyRead = false;
QCOMPARE(buf.write(sample), qint64(sample.size()));
if (sample.size() > 0) {
QTestEventLoop::instance().enterLoop(5);
if (QTestEventLoop::instance().timeout())
QFAIL("Timed out when waiting for readyRead()");
QCOMPARE(totalBytesWritten, qint64(sample.size() * (i + 1)));
QVERIFY(gotReadyRead);
} else {
QCOMPARE(totalBytesWritten, qint64(0));
QVERIFY(!gotReadyRead);
}
}
}
void tst_QBuffer::readyReadSlot()
{
gotReadyRead = true;
QTestEventLoop::instance().exitLoop();
}
void tst_QBuffer::bytesWrittenSlot(qint64 written)
{
totalBytesWritten += written;
}
void tst_QBuffer::isClosedAfterClose()
{
QBuffer buffer;
buffer.open(QBuffer::ReadOnly);
QVERIFY(buffer.isOpen());
buffer.close();
QVERIFY(!buffer.isOpen());
}
void tst_QBuffer::readLine_data()
{
QTest::addColumn<QByteArray>("src");
QTest::addColumn<int>("maxlen");
QTest::addColumn<QByteArray>("expected");
QTest::newRow("1") << QByteArray("line1\nline2\n") << 1024
<< QByteArray("line1\n");
QTest::newRow("2") << QByteArray("hi there") << 1024
<< QByteArray("hi there");
QTest::newRow("3") << QByteArray("l\n") << 3 << QByteArray("l\n");
QTest::newRow("4") << QByteArray("l\n") << 2 << QByteArray("l");
}
void tst_QBuffer::readLine()
{
QFETCH(QByteArray, src);
QFETCH(int, maxlen);
QFETCH(QByteArray, expected);
QBuffer buf;
buf.setBuffer(&src);
char *result = new char[maxlen + 1];
result[maxlen] = '\0';
QVERIFY(buf.open(QIODevice::ReadOnly));
qint64 bytes_read = buf.readLine(result, maxlen);
QCOMPARE(bytes_read, qint64(expected.size()));
QCOMPARE(QByteArray(result), expected);
buf.close();
delete[] result;
}
void tst_QBuffer::canReadLine_data()
{
QTest::addColumn<QByteArray>("src");
QTest::addColumn<bool>("expected");
QTest::newRow("1") << QByteArray("no newline") << false;
QTest::newRow("2") << QByteArray("two \n lines\n") << true;
QTest::newRow("3") << QByteArray("\n") << true;
QTest::newRow("4") << QByteArray() << false;
}
void tst_QBuffer::canReadLine()
{
QFETCH(QByteArray, src);
QFETCH(bool, expected);
QBuffer buf;
buf.setBuffer(&src);
QVERIFY(!buf.canReadLine());
QVERIFY(buf.open(QIODevice::ReadOnly));
QCOMPARE(buf.canReadLine(), expected);
}
void tst_QBuffer::atEnd()
{
QBuffer buffer;
buffer.open(QBuffer::Append);
buffer.write("heisann");
buffer.close();
buffer.open(QBuffer::ReadOnly);
buffer.seek(buffer.size());
char c;
QVERIFY(!buffer.getChar(&c));
QCOMPARE(buffer.read(&c, 1), qint64(0));
}
// Test that reading data out of a QBuffer a line at a time gives the same
// result as reading the whole buffer at once.
void tst_QBuffer::readLineBoundaries()
{
QByteArray line = "This is a line\n";
QBuffer buffer;
buffer.open(QIODevice::ReadWrite);
while (buffer.size() < 16384)
buffer.write(line);
buffer.seek(0);
QByteArray lineByLine;
while (!buffer.atEnd())
lineByLine.append(buffer.readLine());
buffer.seek(0);
QCOMPARE(buffer.bytesAvailable(), lineByLine.size());
QByteArray all = buffer.readAll();
QCOMPARE(all.size(), lineByLine.size());
QCOMPARE(all, lineByLine);
}
// Test that any character in a buffer can be read and pushed back.
void tst_QBuffer::getAndUngetChar()
{
// Create some data in a buffer
QByteArray line = "This is a line\n";
QBuffer buffer;
buffer.open(QIODevice::ReadWrite);
while (buffer.size() < 16384)
buffer.write(line);
// Take a copy of the data held in the buffer
buffer.seek(0);
QCOMPARE(buffer.bytesAvailable(), buffer.size());
QByteArray data = buffer.readAll();
QCOMPARE(buffer.bytesAvailable(), qint64(0));
// Get and unget each character in order
for (qint64 i = 0; i < buffer.size(); ++i) {
buffer.seek(i);
char c;
QVERIFY(buffer.getChar(&c));
QCOMPARE(c, data.at((uint)i));
buffer.ungetChar(c);
}
// Get and unget each character in reverse order
for (qint64 i = buffer.size() - 1; i >= 0; --i) {
buffer.seek(i);
char c;
QVERIFY(buffer.getChar(&c));
QCOMPARE(c, data.at((uint)i));
buffer.ungetChar(c);
}
// Verify that the state of the buffer still matches the original data.
buffer.seek(0);
QCOMPARE(buffer.bytesAvailable(), data.size());
QCOMPARE(buffer.readAll(), data);
QCOMPARE(buffer.bytesAvailable(), qint64(0));
}
void tst_QBuffer::writeAfterQByteArrayResize()
{
QBuffer buffer;
QVERIFY(buffer.open(QIODevice::WriteOnly));
buffer.write(QByteArray().fill('a', 1000));
QCOMPARE(buffer.buffer().size(), 1000);
// resize the QByteArray behind QBuffer's back
buffer.buffer().clear();
buffer.seek(0);
QCOMPARE(buffer.buffer().size(), 0);
buffer.write(QByteArray().fill('b', 1000));
QCOMPARE(buffer.buffer().size(), 1000);
}
void tst_QBuffer::writeOfMoreThan2GiB()
{
if constexpr (sizeof(void*) == 4)
QSKIP("This is a 64-bit-only test");
[[maybe_unused]] constexpr size_t GiB = 1024 * 1024 * 1024;
#ifndef QT_NO_EXCEPTIONS
try {
//
// GIVEN: an empty QBuffer open for writing
//
QBuffer buffer;
QVERIFY(buffer.open(QIODevice::WriteOnly));
//
// WHEN: writing more than 2GiB in a singe chunk:
//
QElapsedTimer timer;
timer.start();
const std::string input(2 * GiB + 1, 42);
qDebug("created dataset in %lld ms", timer.restart());
const auto inputSize = qint64(input.size());
QCOMPARE(buffer.write(input.data(), inputSize), inputSize);
qDebug("performed write in %lld ms", timer.restart());
//
// THEN: the buffer contains the written data
//
QCOMPARE(buffer.buffer().size(), inputSize);
QVERIFY(buffer.buffer() == QByteArrayView{input});
qDebug("verified result in %lld ms", timer.elapsed());
} catch (const std::bad_alloc &) {
QSKIP("Cannot allocate enough memory for this test");
}
#else
QSKIP("This test requires exceptions enabled.");
#endif // QT_NO_EXCEPTIONS
}
void tst_QBuffer::read_null()
{
QByteArray buffer;
buffer.resize(32000);
for (int i = 0; i < buffer.size(); ++i)
buffer[i] = char(i & 0xff);
QBuffer in(&buffer);
in.open(QIODevice::ReadOnly);
QByteArray chunk;
chunk.resize(16380);
in.read(chunk.data(), 16380);
QCOMPARE(chunk, buffer.mid(0, chunk.size()));
in.read(chunk.data(), 0);
chunk.resize(8);
in.read(chunk.data(), chunk.size());
QCOMPARE(chunk, buffer.mid(16380, chunk.size()));
}
QTEST_MAIN(tst_QBuffer)
#include "tst_qbuffer.moc"

View File

@ -0,0 +1,13 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qdataurl Test:
#####################################################################
qt_internal_add_test(tst_qdataurl
SOURCES
tst_qdataurl.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,59 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "private/qdataurl_p.h"
#include <QTest>
#include <QtCore/QDebug>
using namespace Qt::Literals;
class tst_QDataUrl : public QObject
{
Q_OBJECT
private slots:
void decode_data();
void decode();
};
void tst_QDataUrl::decode_data()
{
QTest::addColumn<QString>("input");
QTest::addColumn<bool>("result");
QTest::addColumn<QString>("mimeType");
QTest::addColumn<QByteArray>("payload");
auto row = [](const char *tag, const char *url, bool success, QString mimeType = {}, QByteArray payload = {}) {
QTest::newRow(tag) << url << success <<mimeType << payload;
};
row("nonData", "http://test.com", false);
row("emptyData", "data:text/plain", true,
"text/plain;charset=US-ASCII"_L1);
row("alreadyPercentageEncoded", "data:text/plain,%E2%88%9A", true,
"text/plain"_L1, QByteArray::fromPercentEncoding("%E2%88%9A"));
row("everythingIsCaseInsensitive", "Data:texT/PlaiN;charSet=iSo-8859-1;Base64,SGVsbG8=", true,
"texT/PlaiN;charSet=iSo-8859-1"_L1, QByteArrayLiteral("Hello"));
}
void tst_QDataUrl::decode()
{
QFETCH(const QString, input);
QFETCH(const bool, result);
QFETCH(const QString, mimeType);
QFETCH(const QByteArray, payload);
QString actualMimeType;
QByteArray actualPayload;
QUrl url(input);
const bool actualResult = qDecodeDataUrl(url, actualMimeType, actualPayload);
QCOMPARE(actualResult, result);
QCOMPARE(actualMimeType, mimeType);
QCOMPARE(actualPayload, payload);
QCOMPARE(actualPayload.isNull(), payload.isNull()); // assume nullness is significant
}
QTEST_MAIN(tst_QDataUrl)
#include "tst_qdataurl.moc"

View File

@ -0,0 +1,17 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qdebug Test:
#####################################################################
qt_internal_add_test(tst_qdebug
SOURCES
tst_qdebug.cpp
LIBRARIES
Qt::Concurrent
)
if (APPLE)
set_source_files_properties(tst_qdebug.cpp PROPERTIES LANGUAGE OBJCXX)
endif()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,96 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qdir Test:
#####################################################################
# Collect test data
list(APPEND test_data "testdir")
list(APPEND test_data "testData")
list(APPEND test_data "searchdir")
list(APPEND test_data "resources")
list(APPEND test_data "entrylist")
list(APPEND test_data "types")
list(APPEND test_data "tst_qdir.cpp")
qt_internal_add_test(tst_qdir
SOURCES
tst_qdir.cpp
LIBRARIES
Qt::CorePrivate
TESTDATA ${test_data}
)
# Resources:
set(qdir_resource_files
"resources/entryList/"
)
qt_internal_add_resource(tst_qdir "qdir"
PREFIX
"/tst_qdir/"
FILES
${qdir_resource_files}
)
## Scopes:
#####################################################################
qt_internal_extend_target(tst_qdir CONDITION CONFIG___contains___builtin_testdata
DEFINES
BUILTIN_TESTDATA
)
if(ANDROID)
# Resources:
set(android_testdata_resource_files
"entrylist/directory/dummy"
"entrylist/file"
"resources/entryList/file1.data"
"resources/entryList/file2.data"
"resources/entryList/file3.data"
"resources/entryList/file4.nothing"
"searchdir/subdir1/picker.png"
"searchdir/subdir2/picker.png"
"testData/empty"
"testdir/dir/qdir.pro"
"testdir/dir/qrc_qdir.cpp"
"testdir/dir/tmp/empty"
"testdir/dir/tst_qdir.cpp"
"testdir/spaces/foo. bar"
"testdir/spaces/foo.bar"
"tst_qdir.cpp"
"types/a"
"types/a.a"
"types/a.b"
"types/a.c"
"types/b"
"types/b.a"
"types/b.b"
"types/b.c"
"types/c"
"types/c.a"
"types/c.b"
"types/c.c"
"types/d.a/dummy"
"types/d.b/dummy"
"types/d.c/dummy"
"types/d/dummy"
"types/e.a/dummy"
"types/e.b/dummy"
"types/e.c/dummy"
"types/e/dummy"
"types/f.a/dummy"
"types/f.b/dummy"
"types/f.c/dummy"
"types/f/dummy"
)
qt_internal_add_resource(tst_qdir "android_testdata"
PREFIX
"/android_testdata"
FILES
${android_testdata_resource_files}
)
endif()

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${ASSETCATALOG_COMPILER_APPICON_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${QMAKE_SHORT_VERSION}</string>
<key>CFBundleSignature</key>
<string>${QMAKE_PKGINFO_TYPEINFO}</string>
<key>CFBundleVersion</key>
<string>${QMAKE_FULL_VERSION}</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>MinimumOSVersion</key>
<string>${IPHONEOS_DEPLOYMENT_TARGET}</string>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSPhotoLibraryUsageDescription</key>
<string>Enables use of assets file engine</string>
</dict>
</plist>

View File

@ -0,0 +1,44 @@
<RCC>
<qresource prefix="/android_testdata">
<file>tst_qdir.cpp</file>
<file>entrylist/file</file>
<file>entrylist/directory/dummy</file>
<file>searchdir/subdir1/picker.png</file>
<file>searchdir/subdir2/picker.png</file>
<file>testData/empty</file>
<file>testdir/dir/tmp/empty</file>
<file>testdir/dir/qdir.pro</file>
<file>testdir/dir/qrc_qdir.cpp</file>
<file>testdir/dir/tst_qdir.cpp</file>
<file>testdir/spaces/foo. bar</file>
<file>testdir/spaces/foo.bar</file>
<file>types/a</file>
<file>types/a.a</file>
<file>types/a.b</file>
<file>types/a.c</file>
<file>types/b</file>
<file>types/b.a</file>
<file>types/b.b</file>
<file>types/b.c</file>
<file>types/c</file>
<file>types/c.a</file>
<file>types/c.b</file>
<file>types/c.c</file>
<file>types/d/dummy</file>
<file>types/d.a/dummy</file>
<file>types/d.c/dummy</file>
<file>types/d.b/dummy</file>
<file>types/e/dummy</file>
<file>types/e.a/dummy</file>
<file>types/e.c/dummy</file>
<file>types/f/dummy</file>
<file>types/f.a/dummy</file>
<file>types/f.b/dummy</file>
<file>types/f.c/dummy</file>
<file>types/e.b/dummy</file>
<file>resources/entryList/file1.data</file>
<file>resources/entryList/file2.data</file>
<file>resources/entryList/file3.data</file>
<file>resources/entryList/file4.nothing</file>
</qresource>
</RCC>

View File

@ -0,0 +1 @@
mostly empty

View File

@ -0,0 +1 @@
mostly empty

View File

@ -0,0 +1 @@
this is just so QDir has something to dive into.

View File

@ -0,0 +1,12 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## qdir Binary:
#####################################################################
qt_internal_add_executable(qdir
GUI
LIBRARIES
Qt::Gui
)

View File

@ -0,0 +1,4 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

View File

@ -0,0 +1,4 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1 @@
a

View File

@ -0,0 +1 @@
aa

View File

@ -0,0 +1 @@
aaa

View File

View File

@ -0,0 +1 @@
aaaaaa

View File

@ -0,0 +1 @@
aaaa

View File

@ -0,0 +1 @@
aaaaa

Some files were not shown because too many files have changed in this diff Show More