Merge remote-tracking branch 'origin/5.13' into dev

Conflicts:
	qmake/generators/makefile.cpp

Change-Id: Ib3715e626f2fd32804c75c16ea9aa06a1216e76d
This commit is contained in:
Liang Qi 2019-07-22 06:50:42 +02:00
commit 261a87f956
16 changed files with 476 additions and 78 deletions

View File

@ -3429,9 +3429,9 @@ MakefileGenerator::writePkgConfigFile()
t << Qt::endl; t << Qt::endl;
// requires // requires
const QString requires = project->values("QMAKE_PKGCONFIG_REQUIRES").join(' '); const QString requiresString = project->values("QMAKE_PKGCONFIG_REQUIRES").join(' ');
if (!requires.isEmpty()) { if (!requiresString.isEmpty()) {
t << "Requires: " << requires << Qt::endl; t << "Requires: " << requiresString << Qt::endl;
} }
t << Qt::endl; t << Qt::endl;

View File

@ -433,10 +433,11 @@ public:
ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); } ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
ProFunctionDef(ProFunctionDef &&other) noexcept ProFunctionDef(ProFunctionDef &&other) noexcept
: m_pro(other.m_pro), m_offset(other.m_offset) { other.m_pro = nullptr; } : m_pro(other.m_pro), m_offset(other.m_offset) { other.m_pro = nullptr; }
~ProFunctionDef() { m_pro->deref(); } ~ProFunctionDef() { if (m_pro) m_pro->deref(); }
ProFunctionDef &operator=(const ProFunctionDef &o) ProFunctionDef &operator=(const ProFunctionDef &o)
{ {
if (this != &o) { if (this != &o) {
if (m_pro)
m_pro->deref(); m_pro->deref();
m_pro = o.m_pro; m_pro = o.m_pro;
m_pro->ref(); m_pro->ref();

View File

@ -468,7 +468,17 @@ inline bool QStorageIterator::next()
size_t len = strlen(buffer.data()); size_t len = strlen(buffer.data());
if (len == 0) if (len == 0)
return false; return false;
if (ptr[len - 1] == '\n') while (Q_UNLIKELY(ptr[len - 1] != '\n' && !feof(fp))) {
// buffer wasn't large enough. Enlarge and try again.
// (we're readidng from the kernel, so OOM is unlikely)
buffer.resize((buffer.size() + 4096) & ~4095);
ptr = buffer.data();
if (fgets(ptr + len, buffer.size() - len, fp) == nullptr)
return false;
len += strlen(ptr + len);
Q_ASSERT(len < size_t(buffer.size()));
}
ptr[len - 1] = '\0'; ptr[len - 1] = '\0';
// parse the line // parse the line

View File

@ -514,10 +514,9 @@ Q_CONSTRUCTOR_FUNCTION(qt_apple_check_os_version);
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
void QMacKeyValueObserver::addObserver() void QMacKeyValueObserver::addObserver(NSKeyValueObservingOptions options)
{ {
[object addObserver:observer forKeyPath:keyPath [object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
options:NSKeyValueObservingOptionNew context:callback.get()];
} }
void QMacKeyValueObserver::removeObserver() { void QMacKeyValueObserver::removeObserver() {

View File

@ -350,8 +350,12 @@ public:
QMacKeyValueObserver() {} QMacKeyValueObserver() {}
// Note: QMacKeyValueObserver must not outlive the object observed! // Note: QMacKeyValueObserver must not outlive the object observed!
QMacKeyValueObserver(id object, NSString *keyPath, Callback callback) QMacKeyValueObserver(id object, NSString *keyPath, Callback callback,
: object(object), keyPath(keyPath), callback(new Callback(callback)) { addObserver(); } NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew)
: object(object), keyPath(keyPath), callback(new Callback(callback))
{
addObserver(options);
}
QMacKeyValueObserver(const QMacKeyValueObserver &other) QMacKeyValueObserver(const QMacKeyValueObserver &other)
: QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get()) {} : QMacKeyValueObserver(other.object, other.keyPath, *other.callback.get()) {}
@ -381,7 +385,7 @@ private:
std::swap(first.callback, second.callback); std::swap(first.callback, second.callback);
} }
void addObserver(); void addObserver(NSKeyValueObservingOptions options);
id object = nil; id object = nil;
NSString *keyPath = nullptr; NSString *keyPath = nullptr;

View File

@ -1972,7 +1972,15 @@ inline void QCborStreamReader::preparse()
if (lastError() == QCborError::NoError) { if (lastError() == QCborError::NoError) {
type_ = cbor_value_get_type(&d->currentElement); type_ = cbor_value_get_type(&d->currentElement);
if (type_ != CborInvalidType) { if (type_ == CborInvalidType) {
// We may have reached the end.
if (d->device && d->containerStack.isEmpty()) {
d->buffer.clear();
if (d->bufferStart)
d->device->skip(d->bufferStart);
d->bufferStart = 0;
}
} else {
d->lastError = {}; d->lastError = {};
// Undo the type mapping that TinyCBOR does (we have an explicit type // Undo the type mapping that TinyCBOR does (we have an explicit type
// for negative integer and we don't have separate types for Boolean, // for negative integer and we don't have separate types for Boolean,

View File

@ -2053,6 +2053,23 @@ inline void fetchTransformed_pixelBounds(int max, int l1, int l2, int &v)
} }
} }
static inline bool canUseFastMatrixPath(const qreal cx, const qreal cy, const qsizetype length, const QSpanData *data)
{
if (Q_UNLIKELY(!data->fast_matrix))
return false;
qreal fx = (data->m21 * cy + data->m11 * cx + data->dx) * fixed_scale;
qreal fy = (data->m22 * cy + data->m12 * cx + data->dy) * fixed_scale;
qreal minc = std::min(fx, fy);
qreal maxc = std::max(fx, fy);
fx += std::trunc(data->m11 * fixed_scale) * length;
fy += std::trunc(data->m12 * fixed_scale) * length;
minc = std::min(minc, std::min(fx, fy));
maxc = std::max(maxc, std::max(fx, fy));
return minc >= std::numeric_limits<int>::min() && maxc <= std::numeric_limits<int>::max();
}
template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T> template<TextureBlendType blendType, QPixelLayout::BPP bpp, typename T>
static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data, static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data,
int y, int x, int length) int y, int x, int length)
@ -2070,7 +2087,7 @@ static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *dat
// When templated 'fetch' should be inlined at compile time: // When templated 'fetch' should be inlined at compile time:
const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>); const FetchPixelFunc fetch = (bpp == QPixelLayout::BPPNone) ? qFetchPixel[layout->bpp] : FetchPixelFunc(fetchPixel<bpp>);
if (data->fast_matrix) { if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline // The increment pr x in the scanline
int fdx = (int)(data->m11 * fixed_scale); int fdx = (int)(data->m11 * fixed_scale);
int fdy = (int)(data->m12 * fixed_scale); int fdy = (int)(data->m12 * fixed_scale);
@ -3026,7 +3043,7 @@ static const uint * QT_FASTCALL fetchTransformedBilinearARGB32PM(uint *buffer, c
uint *end = buffer + length; uint *end = buffer + length;
uint *b = buffer; uint *b = buffer;
if (data->fast_matrix) { if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline // The increment pr x in the scanline
int fdx = (int)(data->m11 * fixed_scale); int fdx = (int)(data->m11 * fixed_scale);
int fdy = (int)(data->m12 * fixed_scale); int fdy = (int)(data->m12 * fixed_scale);
@ -3383,7 +3400,7 @@ static const uint *QT_FASTCALL fetchTransformedBilinear(uint *buffer, const Oper
const qreal cx = x + qreal(0.5); const qreal cx = x + qreal(0.5);
const qreal cy = y + qreal(0.5); const qreal cy = y + qreal(0.5);
if (data->fast_matrix) { if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline // The increment pr x in the scanline
int fdx = (int)(data->m11 * fixed_scale); int fdx = (int)(data->m11 * fixed_scale);
int fdy = (int)(data->m12 * fixed_scale); int fdy = (int)(data->m12 * fixed_scale);
@ -3570,7 +3587,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint32(QRgba64 *buf
QRgba64 *end = buffer + length; QRgba64 *end = buffer + length;
QRgba64 *b = buffer; QRgba64 *b = buffer;
if (data->fast_matrix) { if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline // The increment pr x in the scanline
const int fdx = (int)(data->m11 * fixed_scale); const int fdx = (int)(data->m11 * fixed_scale);
const int fdy = (int)(data->m12 * fixed_scale); const int fdy = (int)(data->m12 * fixed_scale);
@ -3728,7 +3745,7 @@ static const QRgba64 *QT_FASTCALL fetchTransformedBilinear64_uint64(QRgba64 *buf
QRgba64 *end = buffer + length; QRgba64 *end = buffer + length;
QRgba64 *b = buffer; QRgba64 *b = buffer;
if (data->fast_matrix) { if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline // The increment pr x in the scanline
const int fdx = (int)(data->m11 * fixed_scale); const int fdx = (int)(data->m11 * fixed_scale);
const int fdy = (int)(data->m12 * fixed_scale); const int fdy = (int)(data->m12 * fixed_scale);

View File

@ -6,6 +6,7 @@ SOURCES += main.mm \
qcocoatheme.mm \ qcocoatheme.mm \
qcocoabackingstore.mm \ qcocoabackingstore.mm \
qcocoawindow.mm \ qcocoawindow.mm \
qcocoawindowmanager.mm \
qnsview.mm \ qnsview.mm \
qnswindow.mm \ qnswindow.mm \
qnswindowdelegate.mm \ qnswindowdelegate.mm \
@ -41,6 +42,7 @@ HEADERS += qcocoaintegration.h \
qcocoatheme.h \ qcocoatheme.h \
qcocoabackingstore.h \ qcocoabackingstore.h \
qcocoawindow.h \ qcocoawindow.h \
qcocoawindowmanager.h \
qnsview.h \ qnsview.h \
qnswindow.h \ qnswindow.h \
qnswindowdelegate.h \ qnswindowdelegate.h \

View File

@ -215,25 +215,6 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
connect(qGuiApp, &QGuiApplication::focusWindowChanged, connect(qGuiApp, &QGuiApplication::focusWindowChanged,
this, &QCocoaIntegration::focusWindowChanged); this, &QCocoaIntegration::focusWindowChanged);
static auto splashScreenHider = QMacKeyValueObserver(NSApp, @"modalWindow", []{
const QWindowList allWindows = QGuiApplication::topLevelWindows();
for (QWindow *window : allWindows) {
if ((window->flags() & Qt::SplashScreen) == Qt::SplashScreen) {
QCocoaWindow *platformWindow = static_cast<QCocoaWindow*>(window->handle());
NSWindow *splashWindow = platformWindow->view().window;
if (!splashWindow)
continue;
if (NSApp.modalWindow) {
NSInteger originalLevel = splashWindow.level;
splashWindow.level = NSNormalWindowLevel;
window->setProperty("_q_levelBeforeModalSession", (qlonglong)originalLevel);
} else if (NSInteger originalLevel = window->property("_q_levelBeforeModalSession").toLongLong()) {
splashWindow.level = originalLevel;
}
}
}
});
} }
QCocoaIntegration::~QCocoaIntegration() QCocoaIntegration::~QCocoaIntegration()

View File

@ -118,11 +118,13 @@ class QSystemTrayIconSys
public: public:
QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) { QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) {
item = [[QNSStatusItem alloc] initWithSysTray:sys]; item = [[QNSStatusItem alloc] initWithSysTray:sys];
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:item]; NSUserNotificationCenter.defaultUserNotificationCenter.delegate = item;
} }
~QSystemTrayIconSys() { ~QSystemTrayIconSys() {
[[[item item] view] setHidden: YES]; [[[item item] view] setHidden: YES];
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil]; NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter;
if (center.delegate == item)
center.delegate = nil;
[item release]; [item release];
} }
QNSStatusItem *item; QNSStatusItem *item;
@ -277,7 +279,10 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess
notification.contentImage = [nsimage autorelease]; notification.contentImage = [nsimage autorelease];
} }
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; NSUserNotificationCenter *center = NSUserNotificationCenter.defaultUserNotificationCenter;
center.delegate = m_sys->item;
[center deliverNotification:notification];
[notification release];
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -454,13 +454,35 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
if (type == Qt::ToolTip) if (type == Qt::ToolTip)
windowLevel = NSScreenSaverWindowLevel; windowLevel = NSScreenSaverWindowLevel;
// Any "special" window should be in at least the same level as its parent. auto *transientParent = window()->transientParent();
if (type != Qt::Window) { if (transientParent && transientParent->handle()) {
const QWindow * const transientParent = window()->transientParent(); // We try to keep windows in at least the same window level as
const QCocoaWindow * const transientParentWindow = transientParent ? // their transient parent. Unfortunately this only works when the
static_cast<QCocoaWindow *>(transientParent->handle()) : nullptr; // window is created. If the window level changes after that, as
if (transientParentWindow) // a result of a call to setWindowFlags, or by changing the level
windowLevel = qMax([transientParentWindow->nativeWindow() level], windowLevel); // of the native window, we will not pick this up, and the window
// will be left behind (or in a different window level than) its
// parent. We could KVO-observe the window level of our transient
// parent, but that requires us to know when the parent goes away
// so that we can unregister the observation before the parent is
// dealloced, something we can't do for generic NSWindows. Another
// way would be to override [NSWindow setLevel:] and notify child
// windows about the change, but that doesn't work for foreign
// windows, which can still be transient parents via fromWinId().
// One area where this problem is apparent is when AppKit tweaks
// the window level of modal windows during application activation
// and deactivation. Since we don't pick up on these window level
// changes in a generic way, we need to add logic explicitly to
// re-evaluate the window level after AppKit has done its tweaks.
auto *transientCocoaWindow = static_cast<QCocoaWindow *>(transientParent->handle());
auto *nsWindow = transientCocoaWindow->nativeWindow();
// We only upgrade the window level for "special" windows, to work
// around Qt Designer parenting the designer windows to the widget
// palette window (QTBUG-31779). This should be fixed in designer.
if (type != Qt::Window)
windowLevel = qMax(windowLevel, nsWindow.level);
} }
return windowLevel; return windowLevel;

View File

@ -0,0 +1,63 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QCOCOAWINDOWMANAGER_H
#define QCOCOAWINDOWMANAGER_H
#include <QtCore/qglobal.h>
#include <AppKit/AppKit.h>
QT_BEGIN_NAMESPACE
class QCocoaWindowManager
{
public:
static QCocoaWindowManager *instance();
private:
QCocoaWindowManager();
void initialize();
void modalSessionChanged();
};
QT_END_NAMESPACE
#endif // QCOCOAWINDOWMANAGER_H

View File

@ -0,0 +1,108 @@
/****************************************************************************
**
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qcocoawindowmanager.h"
#include "qcocoawindow.h"
#include <QtCore/private/qcore_mac_p.h>
#include <QtGui/qguiapplication.h>
#include <QtGui/qwindow.h>
QT_BEGIN_NAMESPACE
QCocoaWindowManager *QCocoaWindowManager::instance()
{
static auto *instance = new QCocoaWindowManager;
return instance;
}
QCocoaWindowManager::QCocoaWindowManager()
{
if (NSApp) {
initialize();
} else {
static auto applicationDidFinishLaunching(QMacNotificationObserver(nil,
NSApplicationDidFinishLaunchingNotification, [this] { initialize(); }));
}
}
void QCocoaWindowManager::initialize()
{
Q_ASSERT(NSApp);
// Whenever the modalWindow property of NSApplication changes we have a new
// modal session running. Observing the app modal window instead of our own
// event dispatcher sessions allows us to track session started by native
// APIs as well. We need to check the initial state as well, in case there
// is already a modal session running.
static auto modalSessionObserver(QMacKeyValueObserver(
NSApp, @"modalWindow", [this] { modalSessionChanged(); },
NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew));
}
void QCocoaWindowManager::modalSessionChanged()
{
// Make sure that no window is overlapping the modal window. This can
// happen for e.g. splash screens, which have the NSPopUpMenuWindowLevel.
for (auto *window : QGuiApplication::topLevelWindows()) {
auto *platformWindow = static_cast<QCocoaWindow*>(window->handle());
if (!platformWindow)
continue;
auto naturalWindowLevel = platformWindow->windowLevel(window->flags());
if (naturalWindowLevel > NSModalPanelWindowLevel) {
NSWindow *nativeWindow = platformWindow->nativeWindow();
if (NSApp.modalWindow) {
// Lower window to that of the modal windows, but no less
nativeWindow.level = NSModalPanelWindowLevel;
[nativeWindow orderBack:nil];
} else {
// Restore window's natural window level, whatever that was
nativeWindow.level = naturalWindowLevel;
}
}
}
}
static void initializeWindowManager() { Q_UNUSED(QCocoaWindowManager::instance()); }
Q_CONSTRUCTOR_FUNCTION(initializeWindowManager)
QT_END_NAMESPACE

View File

@ -80,6 +80,11 @@ private Q_SLOTS:
void addData_singleElement(); void addData_singleElement();
void addData_complex_data() { arrays_data(); } void addData_complex_data() { arrays_data(); }
void addData_complex(); void addData_complex();
void duplicatedData_data() { arrays_data(); }
void duplicatedData();
void extraData_data() { arrays_data(); }
void extraData();
}; };
#define FOR_CBOR_TYPE(F) \ #define FOR_CBOR_TYPE(F) \
@ -480,6 +485,28 @@ static QString parseOne(QCborStreamReader &reader)
return result; return result;
} }
static QString parse(QCborStreamReader &reader, const QByteArray &data)
{
qint64 oldPos = 0;
if (QIODevice *dev = reader.device())
oldPos = dev->pos();
QString r = parseOne(reader);
if (r.isEmpty())
return r;
if (reader.currentOffset() - oldPos != data.size())
r = QString("Number of parsed bytes (%1) not expected (%2)")
.arg(reader.currentOffset()).arg(data.size());
if (QIODevice *dev = reader.device()) {
if (dev->pos() - oldPos != data.size())
r = QString("QIODevice not advanced (%1) as expected (%2)")
.arg(dev->pos()).arg(data.size());
}
return r;
}
bool parseNonRecursive(QString &result, bool &printingStringChunks, QCborStreamReader &reader) bool parseNonRecursive(QString &result, bool &printingStringChunks, QCborStreamReader &reader)
{ {
while (reader.lastError() == QCborError::NoError) { while (reader.lastError() == QCborError::NoError) {
@ -612,13 +639,13 @@ void tst_QCborStreamReader::fixed()
} }
QVERIFY(reader.isValid()); QVERIFY(reader.isValid());
QCOMPARE(reader.lastError(), QCborError::NoError); QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parseOne(reader), expected); QCOMPARE(parse(reader, data), expected);
// verify that we can re-read // verify that we can re-read
reader.reset(); reader.reset();
QVERIFY(reader.isValid()); QVERIFY(reader.isValid());
QCOMPARE(reader.lastError(), QCborError::NoError); QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parseOne(reader), expected); QCOMPARE(parse(reader, data), expected);
} }
void tst_QCborStreamReader::strings_data() void tst_QCborStreamReader::strings_data()
@ -721,7 +748,7 @@ void tst_QCborStreamReader::emptyContainers()
QCOMPARE(reader.lastError(), QCborError::NoError); QCOMPARE(reader.lastError(), QCborError::NoError);
if (reader.isLengthKnown()) if (reader.isLengthKnown())
QCOMPARE(reader.length(), 0U); QCOMPARE(reader.length(), 0U);
QCOMPARE(parseOne(reader), expected); QCOMPARE(parse(reader, data), expected);
// verify that we can re-read // verify that we can re-read
reader.reset(); reader.reset();
@ -729,7 +756,7 @@ void tst_QCborStreamReader::emptyContainers()
QCOMPARE(reader.lastError(), QCborError::NoError); QCOMPARE(reader.lastError(), QCborError::NoError);
if (reader.isLengthKnown()) if (reader.isLengthKnown())
QCOMPARE(reader.length(), 0U); QCOMPARE(reader.length(), 0U);
QCOMPARE(parseOne(reader), expected); QCOMPARE(parse(reader, data), expected);
} }
void tst_QCborStreamReader::arrays_data() void tst_QCborStreamReader::arrays_data()
@ -758,7 +785,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec
QVERIFY(reader.isLengthKnown()); QVERIFY(reader.isLengthKnown());
QCOMPARE(reader.length(), uint(len)); QCOMPARE(reader.length(), uint(len));
} }
QCOMPARE(parseOne(reader), expected); QCOMPARE(parse(reader, data), expected);
// verify that we can re-read // verify that we can re-read
reader.reset(); reader.reset();
@ -768,7 +795,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec
QVERIFY(reader.isLengthKnown()); QVERIFY(reader.isLengthKnown());
QCOMPARE(reader.length(), uint(len)); QCOMPARE(reader.length(), uint(len));
} }
QCOMPARE(parseOne(reader), expected); QCOMPARE(parse(reader, data), expected);
} }
void tst_QCborStreamReader::arrays() void tst_QCborStreamReader::arrays()
@ -892,7 +919,7 @@ void tst_QCborStreamReader::validation()
buffer.open(QIODevice::ReadOnly); buffer.open(QIODevice::ReadOnly);
reader.setDevice(&buffer); reader.setDevice(&buffer);
} }
parseOne(reader); parse(reader, data);
QVERIFY(reader.lastError() != QCborError::NoError); QVERIFY(reader.lastError() != QCborError::NoError);
// next() should fail // next() should fail
@ -997,7 +1024,7 @@ void tst_QCborStreamReader::addData_singleElement()
reader.addData(data.constData() + i, 1); reader.addData(data.constData() + i, 1);
} }
parseOne(reader); parse(reader, data);
QCOMPARE(reader.lastError(), QCborError::EndOfFile); QCOMPARE(reader.lastError(), QCborError::EndOfFile);
} }
@ -1009,7 +1036,7 @@ void tst_QCborStreamReader::addData_singleElement()
reader.addData(data.right(1)); reader.addData(data.right(1));
} }
QCOMPARE(reader.lastError(), QCborError::NoError); QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parseOne(reader), expected); QCOMPARE(parse(reader, data), expected);
} }
void tst_QCborStreamReader::addData_complex() void tst_QCborStreamReader::addData_complex()
@ -1085,6 +1112,69 @@ void tst_QCborStreamReader::addData_complex()
"{1, [" + expected + ", " + expected + "]}"); "{1, [" + expected + ", " + expected + "]}");
} }
void tst_QCborStreamReader::duplicatedData()
{
QFETCH_GLOBAL(bool, useDevice);
QFETCH(QByteArray, data);
QFETCH(QString, expected);
removeIndicators(expected);
// double the data up
QByteArray doubledata = data + data;
QBuffer buffer(&doubledata);
QCborStreamReader reader(doubledata);
if (useDevice) {
buffer.open(QIODevice::ReadOnly);
reader.setDevice(&buffer);
}
QVERIFY(reader.isValid());
QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parse(reader, data), expected); // yes, data
QVERIFY(reader.currentOffset() < doubledata.size());
if (useDevice) {
reader.setDevice(&buffer);
QVERIFY(reader.isValid());
QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parse(reader, data), expected);
QCOMPARE(buffer.pos(), doubledata.size());
} else {
// there's no reader.setData()
}
}
void tst_QCborStreamReader::extraData()
{
QFETCH_GLOBAL(bool, useDevice);
QFETCH(QByteArray, data);
QFETCH(QString, expected);
removeIndicators(expected);
QByteArray extension(9, '\0');
// stress test everything with extra bytes (just one byte changing;
// TinyCBOR used to have a bug where the next byte got sometimes read)
for (int c = '\0'; c < 0x100; ++c) {
extension[0] = c;
QByteArray extendeddata = data + extension;
QBuffer buffer(&extendeddata);
QCborStreamReader reader(extendeddata);
if (useDevice) {
buffer.open(QIODevice::ReadOnly);
reader.setDevice(&buffer);
}
QVERIFY(reader.isValid());
QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parse(reader, data), expected); // yes, data
// if we were a parser, we could parse the next payload
if (useDevice)
QCOMPARE(buffer.readAll(), extension);
}
}
QTEST_MAIN(tst_QCborStreamReader) QTEST_MAIN(tst_QCborStreamReader)

View File

@ -91,8 +91,14 @@ private slots:
void toCbor_data(); void toCbor_data();
void toCbor(); void toCbor();
void toCborStreamWriter_data() { toCbor_data(); }
void toCborStreamWriter();
void fromCbor_data(); void fromCbor_data();
void fromCbor(); void fromCbor();
void fromCborStreamReaderByteArray_data() { fromCbor_data(); }
void fromCborStreamReaderByteArray();
void fromCborStreamReaderIODevice_data() { fromCbor_data(); }
void fromCborStreamReaderIODevice();
void validation_data(); void validation_data();
void validation(); void validation();
void toDiagnosticNotation_data(); void toDiagnosticNotation_data();
@ -1454,6 +1460,22 @@ void tst_QCborValue::toCbor()
"\xa1\x01\xd9\xd9\xf7" + result); "\xa1\x01\xd9\xd9\xf7" + result);
} }
void tst_QCborValue::toCborStreamWriter()
{
QFETCH(QCborValue, v);
QFETCH(QByteArray, result);
QFETCH(QCborValue::EncodingOptions, options);
QByteArray output;
QBuffer buffer(&output);
buffer.open(QIODevice::WriteOnly);
QCborStreamWriter writer(&buffer);
v.toCbor(writer, options);
QCOMPARE(buffer.pos(), result.size());
QCOMPARE(output, result);
}
void tst_QCborValue::fromCbor_data() void tst_QCborValue::fromCbor_data()
{ {
addCommonCborData(); addCommonCborData();
@ -1484,20 +1506,11 @@ void tst_QCborValue::fromCbor_data()
<< raw("\xd8\x25\x51" "\1\2\3\4""\4\3\2\0""\0\0\0\0""\0\0\0\1""\2"); << raw("\xd8\x25\x51" "\1\2\3\4""\4\3\2\0""\0\0\0\0""\0\0\0\1""\2");
} }
void tst_QCborValue::fromCbor() void fromCbor_common(void (*doCheck)(const QCborValue &, const QByteArray &))
{ {
QFETCH(QCborValue, v); QFETCH(QCborValue, v);
QFETCH(QByteArray, result); QFETCH(QByteArray, result);
auto doCheck = [](const QCborValue &v, const QByteArray &result) {
QCborParserError error;
QCborValue decoded = QCborValue::fromCbor(result, &error);
QVERIFY2(error.error == QCborError(), qPrintable(error.errorString()));
QCOMPARE(error.offset, result.size());
QVERIFY(decoded == v);
QVERIFY(v == decoded);
};
doCheck(v, result); doCheck(v, result);
if (QTest::currentTestFailed()) if (QTest::currentTestFailed())
return; return;
@ -1548,6 +1561,52 @@ void tst_QCborValue::fromCbor()
return; return;
} }
void tst_QCborValue::fromCbor()
{
auto doCheck = [](const QCborValue &v, const QByteArray &result) {
QCborParserError error;
QCborValue decoded = QCborValue::fromCbor(result, &error);
QVERIFY2(error.error == QCborError(), qPrintable(error.errorString()));
QCOMPARE(error.offset, result.size());
QVERIFY(decoded == v);
QVERIFY(v == decoded);
};
fromCbor_common(doCheck);
}
void tst_QCborValue::fromCborStreamReaderByteArray()
{
auto doCheck = [](const QCborValue &expected, const QByteArray &data) {
QCborStreamReader reader(data);
QCborValue decoded = QCborValue::fromCbor(reader);
QCOMPARE(reader.lastError(), QCborError());
QCOMPARE(reader.currentOffset(), data.size());
QVERIFY(decoded == expected);
QVERIFY(expected == decoded);
};
fromCbor_common(doCheck);
}
void tst_QCborValue::fromCborStreamReaderIODevice()
{
auto doCheck = [](const QCborValue &expected, const QByteArray &data) {
QBuffer buffer;
buffer.setData(data);
buffer.open(QIODevice::ReadOnly);
QCborStreamReader reader(&buffer);
QCborValue decoded = QCborValue::fromCbor(reader);
QCOMPARE(reader.lastError(), QCborError());
QCOMPARE(reader.currentOffset(), data.size());
QVERIFY(decoded == expected);
QVERIFY(expected == decoded);
QCOMPARE(buffer.pos(), reader.currentOffset());
};
fromCbor_common(doCheck);
}
void tst_QCborValue::validation_data() void tst_QCborValue::validation_data()
{ {
addValidationColumns(); addValidationColumns();

View File

@ -46,6 +46,7 @@
#endif // NO_SSL #endif // NO_SSL
#include <cstdlib> #include <cstdlib>
#include <memory>
#include <string> #include <string>
#include "emulationdetector.h" #include "emulationdetector.h"
@ -117,7 +118,7 @@ private:
quint16 serverPort = 0; quint16 serverPort = 0;
QThread *workerThread = nullptr; QThread *workerThread = nullptr;
QNetworkAccessManager manager; std::unique_ptr<QNetworkAccessManager> manager;
QTestEventLoop eventLoop; QTestEventLoop eventLoop;
@ -131,6 +132,10 @@ private:
static const Http2::RawSettings defaultServerSettings; static const Http2::RawSettings defaultServerSettings;
}; };
#define STOP_ON_FAILURE \
if (QTest::currentTestFailed()) \
return;
const Http2::RawSettings tst_Http2::defaultServerSettings{{Http2::Settings::MAX_CONCURRENT_STREAMS_ID, 100}}; const Http2::RawSettings tst_Http2::defaultServerSettings{{Http2::Settings::MAX_CONCURRENT_STREAMS_ID, 100}};
namespace { namespace {
@ -178,7 +183,7 @@ tst_Http2::~tst_Http2()
void tst_Http2::init() void tst_Http2::init()
{ {
manager.clearConnectionCache(); manager.reset(new QNetworkAccessManager);
} }
void tst_Http2::singleRequest_data() void tst_Http2::singleRequest_data()
@ -235,13 +240,14 @@ void tst_Http2::singleRequest()
QFETCH(const QNetworkRequest::Attribute, h2Attribute); QFETCH(const QNetworkRequest::Attribute, h2Attribute);
request.setAttribute(h2Attribute, QVariant(true)); request.setAttribute(h2Attribute, QVariant(true));
auto reply = manager.get(request); auto reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
// Since we're using self-signed certificates, // Since we're using self-signed certificates,
// ignore SSL errors: // ignore SSL errors:
reply->ignoreSslErrors(); reply->ignoreSslErrors();
runEventLoop(); runEventLoop();
STOP_ON_FAILURE
QVERIFY(nRequests == 0); QVERIFY(nRequests == 0);
QVERIFY(prefaceOK); QVERIFY(prefaceOK);
@ -277,6 +283,7 @@ void tst_Http2::multipleRequests()
sendRequest(i, priorities[QRandomGenerator::global()->bounded(3)]); sendRequest(i, priorities[QRandomGenerator::global()->bounded(3)]);
runEventLoop(); runEventLoop();
STOP_ON_FAILURE
QVERIFY(nRequests == 0); QVERIFY(nRequests == 0);
QVERIFY(prefaceOK); QVERIFY(prefaceOK);
@ -306,7 +313,7 @@ void tst_Http2::flowControlClientSide()
params.maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize * 5; params.maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize * 5;
params.settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID] = Http2::defaultSessionWindowSize; params.settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID] = Http2::defaultSessionWindowSize;
// Inform our manager about non-default settings: // Inform our manager about non-default settings:
manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params)); manager->setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params));
const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, quint32(3)}}; const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, quint32(3)}};
ServerPtr srv(newServer(serverSettings, defaultConnectionType(), params)); ServerPtr srv(newServer(serverSettings, defaultConnectionType(), params));
@ -324,6 +331,7 @@ void tst_Http2::flowControlClientSide()
sendRequest(i); sendRequest(i);
runEventLoop(120000); runEventLoop(120000);
STOP_ON_FAILURE
QVERIFY(nRequests == 0); QVERIFY(nRequests == 0);
QVERIFY(prefaceOK); QVERIFY(prefaceOK);
@ -364,6 +372,7 @@ void tst_Http2::flowControlServerSide()
sendRequest(i, QNetworkRequest::NormalPriority, payload); sendRequest(i, QNetworkRequest::NormalPriority, payload);
runEventLoop(120000); runEventLoop(120000);
STOP_ON_FAILURE
QVERIFY(nRequests == 0); QVERIFY(nRequests == 0);
QVERIFY(prefaceOK); QVERIFY(prefaceOK);
@ -384,7 +393,7 @@ void tst_Http2::pushPromise()
Http2::ProtocolParameters params; Http2::ProtocolParameters params;
// Defaults are good, except ENABLE_PUSH: // Defaults are good, except ENABLE_PUSH:
params.settingsFrameData[Settings::ENABLE_PUSH_ID] = 1; params.settingsFrameData[Settings::ENABLE_PUSH_ID] = 1;
manager.setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params)); manager->setProperty(Http2::http2ParametersPropertyName, QVariant::fromValue(params));
ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType(), params)); ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType(), params));
srv->enablePushPromise(true, QByteArray("/script.js")); srv->enablePushPromise(true, QByteArray("/script.js"));
@ -400,12 +409,13 @@ void tst_Http2::pushPromise()
QNetworkRequest request(url); QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
auto reply = manager.get(request); auto reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
// Since we're using self-signed certificates, ignore SSL errors: // Since we're using self-signed certificates, ignore SSL errors:
reply->ignoreSslErrors(); reply->ignoreSslErrors();
runEventLoop(); runEventLoop();
STOP_ON_FAILURE
QVERIFY(nRequests == 0); QVERIFY(nRequests == 0);
QVERIFY(prefaceOK); QVERIFY(prefaceOK);
@ -423,7 +433,7 @@ void tst_Http2::pushPromise()
url.setPath("/script.js"); url.setPath("/script.js");
QNetworkRequest promisedRequest(url); QNetworkRequest promisedRequest(url);
promisedRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); promisedRequest.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
reply = manager.get(promisedRequest); reply = manager->get(promisedRequest);
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
reply->ignoreSslErrors(); reply->ignoreSslErrors();
@ -474,7 +484,7 @@ void tst_Http2::goaway()
url.setPath(QString("/%1").arg(i)); url.setPath(QString("/%1").arg(i));
QNetworkRequest request(url); QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true));
replies[i] = manager.get(request); replies[i] = manager->get(request);
QCOMPARE(replies[i]->error(), QNetworkReply::NoError); QCOMPARE(replies[i]->error(), QNetworkReply::NoError);
void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) = void (QNetworkReply::*errorSignal)(QNetworkReply::NetworkError) =
&QNetworkReply::error; &QNetworkReply::error;
@ -484,6 +494,7 @@ void tst_Http2::goaway()
} }
runEventLoop(5000 + responseTimeoutMS); runEventLoop(5000 + responseTimeoutMS);
STOP_ON_FAILURE
// No request processed, no 'replyFinished' slot calls: // No request processed, no 'replyFinished' slot calls:
QCOMPARE(nRequests, 0); QCOMPARE(nRequests, 0);
@ -527,6 +538,7 @@ void tst_Http2::earlyResponse()
sendRequest(1, QNetworkRequest::NormalPriority, {1000000, Qt::Uninitialized}); sendRequest(1, QNetworkRequest::NormalPriority, {1000000, Qt::Uninitialized});
runEventLoop(); runEventLoop();
STOP_ON_FAILURE
QVERIFY(nRequests == 0); QVERIFY(nRequests == 0);
QVERIFY(prefaceOK); QVERIFY(prefaceOK);
@ -544,7 +556,7 @@ void tst_Http2::clearHTTP2State()
windowUpdates = 0; windowUpdates = 0;
prefaceOK = false; prefaceOK = false;
serverGotSettingsACK = false; serverGotSettingsACK = false;
manager.setProperty(Http2::http2ParametersPropertyName, QVariant()); manager->setProperty(Http2::http2ParametersPropertyName, QVariant());
} }
void tst_Http2::runEventLoop(int ms) void tst_Http2::runEventLoop(int ms)
@ -597,9 +609,9 @@ void tst_Http2::sendRequest(int streamNumber,
QNetworkReply *reply = nullptr; QNetworkReply *reply = nullptr;
if (payload.size()) if (payload.size())
reply = manager.post(request, payload); reply = manager->post(request, payload);
else else
reply = manager.get(request); reply = manager->get(request);
reply->ignoreSslErrors(); reply->ignoreSslErrors();
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
@ -691,14 +703,29 @@ void tst_Http2::replyFinished()
QVERIFY(nRequests); QVERIFY(nRequests);
if (const auto reply = qobject_cast<QNetworkReply *>(sender())) { if (const auto reply = qobject_cast<QNetworkReply *>(sender())) {
if (reply->error() != QNetworkReply::NoError)
stopEventLoop();
QCOMPARE(reply->error(), QNetworkReply::NoError); QCOMPARE(reply->error(), QNetworkReply::NoError);
const QVariant http2Used(reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute)); const QVariant http2Used(reply->attribute(QNetworkRequest::HTTP2WasUsedAttribute));
if (!http2Used.isValid() || !http2Used.toBool())
stopEventLoop();
QVERIFY(http2Used.isValid()); QVERIFY(http2Used.isValid());
QVERIFY(http2Used.toBool()); QVERIFY(http2Used.toBool());
const QVariant spdyUsed(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute)); const QVariant spdyUsed(reply->attribute(QNetworkRequest::SpdyWasUsedAttribute));
if (!spdyUsed.isValid() || spdyUsed.toBool())
stopEventLoop();
QVERIFY(spdyUsed.isValid()); QVERIFY(spdyUsed.isValid());
QVERIFY(!spdyUsed.toBool()); QVERIFY(!spdyUsed.toBool());
const QVariant code(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute)); const QVariant code(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute));
if (!code.isValid() || !code.canConvert<int>() || code.value<int>() != 200)
stopEventLoop();
QVERIFY(code.isValid()); QVERIFY(code.isValid());
QVERIFY(code.canConvert<int>()); QVERIFY(code.canConvert<int>());
QCOMPARE(code.value<int>(), 200); QCOMPARE(code.value<int>(), 200);
@ -716,6 +743,8 @@ void tst_Http2::replyFinishedWithError()
if (const auto reply = qobject_cast<QNetworkReply *>(sender())) { if (const auto reply = qobject_cast<QNetworkReply *>(sender())) {
// For now this is a 'generic' code, it just verifies some error was // For now this is a 'generic' code, it just verifies some error was
// reported without testing its type. // reported without testing its type.
if (reply->error() == QNetworkReply::NoError)
stopEventLoop();
QVERIFY(reply->error() != QNetworkReply::NoError); QVERIFY(reply->error() != QNetworkReply::NoError);
} }