mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-02 07:15:27 +08:00
qt 6.5.1 original
This commit is contained in:
18
tests/auto/corelib/CMakeLists.txt
Normal file
18
tests/auto/corelib/CMakeLists.txt
Normal 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)
|
12
tests/auto/corelib/animation/CMakeLists.txt
Normal file
12
tests/auto/corelib/animation/CMakeLists.txt
Normal 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()
|
@ -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
|
||||
)
|
@ -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"
|
11
tests/auto/corelib/animation/qanimationgroup/CMakeLists.txt
Normal file
11
tests/auto/corelib/animation/qanimationgroup/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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"
|
@ -0,0 +1,2 @@
|
||||
[deleteChildrenWithRunningGroup]
|
||||
macos
|
@ -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
|
||||
)
|
File diff suppressed because it is too large
Load Diff
6
tests/auto/corelib/animation/qpauseanimation/BLACKLIST
Normal file
6
tests/auto/corelib/animation/qpauseanimation/BLACKLIST
Normal file
@ -0,0 +1,6 @@
|
||||
[pauseAndPropertyAnimations]
|
||||
macos
|
||||
[multipleSequentialGroups]
|
||||
macos
|
||||
[noTimerUpdates]
|
||||
macos
|
13
tests/auto/corelib/animation/qpauseanimation/CMakeLists.txt
Normal file
13
tests/auto/corelib/animation/qpauseanimation/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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"
|
@ -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
@ -0,0 +1,6 @@
|
||||
[finishWithUncontrolledAnimation]
|
||||
windows-10 msvc-2015
|
||||
macos
|
||||
[groupWithZeroDurationAnimations]
|
||||
macos
|
||||
|
@ -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
|
||||
)
|
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
)
|
@ -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"
|
23
tests/auto/corelib/global/CMakeLists.txt
Normal file
23
tests/auto/corelib/global/CMakeLists.txt
Normal 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)
|
11
tests/auto/corelib/global/q_func_info/CMakeLists.txt
Normal file
11
tests/auto/corelib/global/q_func_info/CMakeLists.txt
Normal 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
|
||||
)
|
106
tests/auto/corelib/global/q_func_info/tst_q_func_info.cpp
Normal file
106
tests/auto/corelib/global/q_func_info/tst_q_func_info.cpp
Normal 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"
|
14
tests/auto/corelib/global/qcompare/CMakeLists.txt
Normal file
14
tests/auto/corelib/global/qcompare/CMakeLists.txt
Normal 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:
|
||||
#####################################################################
|
98
tests/auto/corelib/global/qcompare/tst_qcompare.cpp
Normal file
98
tests/auto/corelib/global/qcompare/tst_qcompare.cpp
Normal 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"
|
18
tests/auto/corelib/global/qflags/CMakeLists.txt
Normal file
18
tests/auto/corelib/global/qflags/CMakeLists.txt
Normal 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
|
||||
)
|
481
tests/auto/corelib/global/qflags/tst_qflags.cpp
Normal file
481
tests/auto/corelib/global/qflags/tst_qflags.cpp
Normal 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"
|
13
tests/auto/corelib/global/qfloat16/CMakeLists.txt
Normal file
13
tests/auto/corelib/global/qfloat16/CMakeLists.txt
Normal 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
|
||||
)
|
694
tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp
Normal file
694
tests/auto/corelib/global/qfloat16/tst_qfloat16.cpp
Normal 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"
|
11
tests/auto/corelib/global/qgetputenv/CMakeLists.txt
Normal file
11
tests/auto/corelib/global/qgetputenv/CMakeLists.txt
Normal 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
|
||||
)
|
207
tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
Normal file
207
tests/auto/corelib/global/qgetputenv/tst_qgetputenv.cpp
Normal 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"
|
15
tests/auto/corelib/global/qglobal/CMakeLists.txt
Normal file
15
tests/auto/corelib/global/qglobal/CMakeLists.txt
Normal 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:
|
||||
#####################################################################
|
92
tests/auto/corelib/global/qglobal/qglobal.c
Normal file
92
tests/auto/corelib/global/qglobal/qglobal.c
Normal 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
|
||||
|
695
tests/auto/corelib/global/qglobal/tst_qglobal.cpp
Normal file
695
tests/auto/corelib/global/qglobal/tst_qglobal.cpp
Normal 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"
|
15
tests/auto/corelib/global/qglobalstatic/CMakeLists.txt
Normal file
15
tests/auto/corelib/global/qglobalstatic/CMakeLists.txt
Normal 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
|
||||
)
|
233
tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp
Normal file
233
tests/auto/corelib/global/qglobalstatic/tst_qglobalstatic.cpp
Normal 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"
|
13
tests/auto/corelib/global/qhooks/CMakeLists.txt
Normal file
13
tests/auto/corelib/global/qhooks/CMakeLists.txt
Normal 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
|
||||
)
|
122
tests/auto/corelib/global/qhooks/tst_qhooks.cpp
Normal file
122
tests/auto/corelib/global/qhooks/tst_qhooks.cpp
Normal 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"
|
11
tests/auto/corelib/global/qkeycombination/CMakeLists.txt
Normal file
11
tests/auto/corelib/global/qkeycombination/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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"
|
9
tests/auto/corelib/global/qlogging/BLACKLIST
Normal file
9
tests/auto/corelib/global/qlogging/BLACKLIST
Normal file
@ -0,0 +1,9 @@
|
||||
[qMessagePattern:backtrace]
|
||||
# QTBUG-63915
|
||||
b2qt 64bit
|
||||
|
||||
[qMessagePattern:backtrace depth,separator]
|
||||
# QTBUG-63915
|
||||
b2qt 64bit
|
||||
# QTBUG-85364
|
||||
b2qt cmake
|
26
tests/auto/corelib/global/qlogging/CMakeLists.txt
Normal file
26
tests/auto/corelib/global/qlogging/CMakeLists.txt
Normal 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
|
||||
)
|
63
tests/auto/corelib/global/qlogging/app/main.cpp
Normal file
63
tests/auto/corelib/global/qlogging/app/main.cpp
Normal 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"
|
988
tests/auto/corelib/global/qlogging/tst_qlogging.cpp
Normal file
988
tests/auto/corelib/global/qlogging/tst_qlogging.cpp
Normal 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"
|
334
tests/auto/corelib/global/qlogging/tst_qmessagelogger.cpp
Normal file
334
tests/auto/corelib/global/qlogging/tst_qmessagelogger.cpp
Normal 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"
|
@ -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
|
||||
)
|
@ -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"
|
16
tests/auto/corelib/global/qnumeric/CMakeLists.txt
Normal file
16
tests/auto/corelib/global/qnumeric/CMakeLists.txt
Normal 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:
|
||||
#####################################################################
|
751
tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
Normal file
751
tests/auto/corelib/global/qnumeric/tst_qnumeric.cpp
Normal 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"
|
@ -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:
|
||||
#####################################################################
|
@ -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"
|
13
tests/auto/corelib/global/qrandomgenerator/CMakeLists.txt
Normal file
13
tests/auto/corelib/global/qrandomgenerator/CMakeLists.txt
Normal 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
|
||||
)
|
1009
tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
Normal file
1009
tests/auto/corelib/global/qrandomgenerator/tst_qrandomgenerator.cpp
Normal file
File diff suppressed because it is too large
Load Diff
13
tests/auto/corelib/global/qtendian/CMakeLists.txt
Normal file
13
tests/auto/corelib/global/qtendian/CMakeLists.txt
Normal 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
|
||||
)
|
449
tests/auto/corelib/global/qtendian/tst_qtendian.cpp
Normal file
449
tests/auto/corelib/global/qtendian/tst_qtendian.cpp
Normal 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"
|
1
tests/auto/corelib/global/qxp/CMakeLists.txt
Normal file
1
tests/auto/corelib/global/qxp/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(function_ref)
|
10
tests/auto/corelib/global/qxp/function_ref/CMakeLists.txt
Normal file
10
tests/auto/corelib/global/qxp/function_ref/CMakeLists.txt
Normal 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
|
||||
)
|
@ -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"
|
66
tests/auto/corelib/io/CMakeLists.txt
Normal file
66
tests/auto/corelib/io/CMakeLists.txt
Normal 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()
|
13
tests/auto/corelib/io/largefile/CMakeLists.txt
Normal file
13
tests/auto/corelib/io/largefile/CMakeLists.txt
Normal 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
|
||||
)
|
530
tests/auto/corelib/io/largefile/tst_largefile.cpp
Normal file
530
tests/auto/corelib/io/largefile/tst_largefile.cpp
Normal 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"
|
||||
|
26
tests/auto/corelib/io/qabstractfileengine/CMakeLists.txt
Normal file
26
tests/auto/corelib/io/qabstractfileengine/CMakeLists.txt
Normal 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}
|
||||
)
|
||||
|
@ -0,0 +1 @@
|
||||
This is a simple text 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"
|
||||
|
11
tests/auto/corelib/io/qbuffer/CMakeLists.txt
Normal file
11
tests/auto/corelib/io/qbuffer/CMakeLists.txt
Normal 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
|
||||
)
|
704
tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp
Normal file
704
tests/auto/corelib/io/qbuffer/tst_qbuffer.cpp
Normal 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"
|
13
tests/auto/corelib/io/qdataurl/CMakeLists.txt
Normal file
13
tests/auto/corelib/io/qdataurl/CMakeLists.txt
Normal 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
|
||||
)
|
59
tests/auto/corelib/io/qdataurl/tst_qdataurl.cpp
Normal file
59
tests/auto/corelib/io/qdataurl/tst_qdataurl.cpp
Normal 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"
|
17
tests/auto/corelib/io/qdebug/CMakeLists.txt
Normal file
17
tests/auto/corelib/io/qdebug/CMakeLists.txt
Normal 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()
|
1293
tests/auto/corelib/io/qdebug/tst_qdebug.cpp
Normal file
1293
tests/auto/corelib/io/qdebug/tst_qdebug.cpp
Normal file
File diff suppressed because it is too large
Load Diff
96
tests/auto/corelib/io/qdir/CMakeLists.txt
Normal file
96
tests/auto/corelib/io/qdir/CMakeLists.txt
Normal 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()
|
39
tests/auto/corelib/io/qdir/Info.plist
Normal file
39
tests/auto/corelib/io/qdir/Info.plist
Normal 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>
|
44
tests/auto/corelib/io/qdir/android_testdata.qrc
Normal file
44
tests/auto/corelib/io/qdir/android_testdata.qrc
Normal 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>
|
0
tests/auto/corelib/io/qdir/entrylist/file
Normal file
0
tests/auto/corelib/io/qdir/entrylist/file
Normal file
1
tests/auto/corelib/io/qdir/searchdir/subdir1/picker.png
Normal file
1
tests/auto/corelib/io/qdir/searchdir/subdir1/picker.png
Normal file
@ -0,0 +1 @@
|
||||
mostly empty
|
1
tests/auto/corelib/io/qdir/searchdir/subdir2/picker.png
Normal file
1
tests/auto/corelib/io/qdir/searchdir/subdir2/picker.png
Normal file
@ -0,0 +1 @@
|
||||
mostly empty
|
1
tests/auto/corelib/io/qdir/testData/empty
Normal file
1
tests/auto/corelib/io/qdir/testData/empty
Normal file
@ -0,0 +1 @@
|
||||
this is just so QDir has something to dive into.
|
12
tests/auto/corelib/io/qdir/testdir/dir/CMakeLists.txt
Normal file
12
tests/auto/corelib/io/qdir/testdir/dir/CMakeLists.txt
Normal 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
|
||||
)
|
0
tests/auto/corelib/io/qdir/testdir/dir/qdir.pro
Normal file
0
tests/auto/corelib/io/qdir/testdir/dir/qdir.pro
Normal file
4
tests/auto/corelib/io/qdir/testdir/dir/qrc_qdir.cpp
Normal file
4
tests/auto/corelib/io/qdir/testdir/dir/qrc_qdir.cpp
Normal 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
|
||||
|
||||
|
0
tests/auto/corelib/io/qdir/testdir/dir/tmp/empty
Normal file
0
tests/auto/corelib/io/qdir/testdir/dir/tmp/empty
Normal file
4
tests/auto/corelib/io/qdir/testdir/dir/tst_qdir.cpp
Normal file
4
tests/auto/corelib/io/qdir/testdir/dir/tst_qdir.cpp
Normal 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
|
||||
|
||||
|
0
tests/auto/corelib/io/qdir/testdir/spaces/foo. bar
Normal file
0
tests/auto/corelib/io/qdir/testdir/spaces/foo. bar
Normal file
0
tests/auto/corelib/io/qdir/testdir/spaces/foo.bar
Normal file
0
tests/auto/corelib/io/qdir/testdir/spaces/foo.bar
Normal file
2490
tests/auto/corelib/io/qdir/tst_qdir.cpp
Normal file
2490
tests/auto/corelib/io/qdir/tst_qdir.cpp
Normal file
File diff suppressed because it is too large
Load Diff
0
tests/auto/corelib/io/qdir/types/a
Normal file
0
tests/auto/corelib/io/qdir/types/a
Normal file
1
tests/auto/corelib/io/qdir/types/a.a
Normal file
1
tests/auto/corelib/io/qdir/types/a.a
Normal file
@ -0,0 +1 @@
|
||||
a
|
1
tests/auto/corelib/io/qdir/types/a.b
Normal file
1
tests/auto/corelib/io/qdir/types/a.b
Normal file
@ -0,0 +1 @@
|
||||
aa
|
1
tests/auto/corelib/io/qdir/types/a.c
Normal file
1
tests/auto/corelib/io/qdir/types/a.c
Normal file
@ -0,0 +1 @@
|
||||
aaa
|
0
tests/auto/corelib/io/qdir/types/b
Normal file
0
tests/auto/corelib/io/qdir/types/b
Normal file
1
tests/auto/corelib/io/qdir/types/b.a
Normal file
1
tests/auto/corelib/io/qdir/types/b.a
Normal file
@ -0,0 +1 @@
|
||||
aaaaaa
|
1
tests/auto/corelib/io/qdir/types/b.b
Normal file
1
tests/auto/corelib/io/qdir/types/b.b
Normal file
@ -0,0 +1 @@
|
||||
aaaa
|
1
tests/auto/corelib/io/qdir/types/b.c
Normal file
1
tests/auto/corelib/io/qdir/types/b.c
Normal file
@ -0,0 +1 @@
|
||||
aaaaa
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user