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

This commit is contained in:
Liang Qi 2019-07-22 06:50:42 +02:00
commit 46cb35fccc
16 changed files with 476 additions and 78 deletions

View File

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

View File

@ -433,11 +433,12 @@ public:
ProFunctionDef(const ProFunctionDef &o) : m_pro(o.m_pro), m_offset(o.m_offset) { m_pro->ref(); }
ProFunctionDef(ProFunctionDef &&other) noexcept
: 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)
{
if (this != &o) {
m_pro->deref();
if (m_pro)
m_pro->deref();
m_pro = o.m_pro;
m_pro->ref();
m_offset = o.m_offset;

View File

@ -468,8 +468,18 @@ inline bool QStorageIterator::next()
size_t len = strlen(buffer.data());
if (len == 0)
return false;
if (ptr[len - 1] == '\n')
ptr[len - 1] = '\0';
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';
// parse the line
bool ok;

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
options:NSKeyValueObservingOptionNew context:callback.get()];
[object addObserver:observer forKeyPath:keyPath options:options context:callback.get()];
}
void QMacKeyValueObserver::removeObserver() {

View File

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

View File

@ -1972,7 +1972,15 @@ inline void QCborStreamReader::preparse()
if (lastError() == QCborError::NoError) {
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 = {};
// Undo the type mapping that TinyCBOR does (we have an explicit type
// 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>
static void QT_FASTCALL fetchTransformed_fetcher(T *buffer, const QSpanData *data,
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:
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
int fdx = (int)(data->m11 * 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 *b = buffer;
if (data->fast_matrix) {
if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
int fdx = (int)(data->m11 * 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 cy = y + qreal(0.5);
if (data->fast_matrix) {
if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
int fdx = (int)(data->m11 * 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 *b = buffer;
if (data->fast_matrix) {
if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
const int fdx = (int)(data->m11 * 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 *b = buffer;
if (data->fast_matrix) {
if (canUseFastMatrixPath(cx, cy, length, data)) {
// The increment pr x in the scanline
const int fdx = (int)(data->m11 * fixed_scale);
const int fdy = (int)(data->m12 * fixed_scale);

View File

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

View File

@ -215,25 +215,6 @@ QCocoaIntegration::QCocoaIntegration(const QStringList &paramList)
connect(qGuiApp, &QGuiApplication::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()

View File

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

View File

@ -454,13 +454,35 @@ NSInteger QCocoaWindow::windowLevel(Qt::WindowFlags flags)
if (type == Qt::ToolTip)
windowLevel = NSScreenSaverWindowLevel;
// Any "special" window should be in at least the same level as its parent.
if (type != Qt::Window) {
const QWindow * const transientParent = window()->transientParent();
const QCocoaWindow * const transientParentWindow = transientParent ?
static_cast<QCocoaWindow *>(transientParent->handle()) : nullptr;
if (transientParentWindow)
windowLevel = qMax([transientParentWindow->nativeWindow() level], windowLevel);
auto *transientParent = window()->transientParent();
if (transientParent && transientParent->handle()) {
// We try to keep windows in at least the same window level as
// their transient parent. Unfortunately this only works when the
// window is created. If the window level changes after that, as
// a result of a call to setWindowFlags, or by changing the level
// 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;

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_complex_data() { arrays_data(); }
void addData_complex();
void duplicatedData_data() { arrays_data(); }
void duplicatedData();
void extraData_data() { arrays_data(); }
void extraData();
};
#define FOR_CBOR_TYPE(F) \
@ -480,6 +485,28 @@ static QString parseOne(QCborStreamReader &reader)
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)
{
while (reader.lastError() == QCborError::NoError) {
@ -612,13 +639,13 @@ void tst_QCborStreamReader::fixed()
}
QVERIFY(reader.isValid());
QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parseOne(reader), expected);
QCOMPARE(parse(reader, data), expected);
// verify that we can re-read
reader.reset();
QVERIFY(reader.isValid());
QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parseOne(reader), expected);
QCOMPARE(parse(reader, data), expected);
}
void tst_QCborStreamReader::strings_data()
@ -721,7 +748,7 @@ void tst_QCborStreamReader::emptyContainers()
QCOMPARE(reader.lastError(), QCborError::NoError);
if (reader.isLengthKnown())
QCOMPARE(reader.length(), 0U);
QCOMPARE(parseOne(reader), expected);
QCOMPARE(parse(reader, data), expected);
// verify that we can re-read
reader.reset();
@ -729,7 +756,7 @@ void tst_QCborStreamReader::emptyContainers()
QCOMPARE(reader.lastError(), QCborError::NoError);
if (reader.isLengthKnown())
QCOMPARE(reader.length(), 0U);
QCOMPARE(parseOne(reader), expected);
QCOMPARE(parse(reader, data), expected);
}
void tst_QCborStreamReader::arrays_data()
@ -758,7 +785,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec
QVERIFY(reader.isLengthKnown());
QCOMPARE(reader.length(), uint(len));
}
QCOMPARE(parseOne(reader), expected);
QCOMPARE(parse(reader, data), expected);
// verify that we can re-read
reader.reset();
@ -768,7 +795,7 @@ static void checkContainer(int len, const QByteArray &data, const QString &expec
QVERIFY(reader.isLengthKnown());
QCOMPARE(reader.length(), uint(len));
}
QCOMPARE(parseOne(reader), expected);
QCOMPARE(parse(reader, data), expected);
}
void tst_QCborStreamReader::arrays()
@ -892,7 +919,7 @@ void tst_QCborStreamReader::validation()
buffer.open(QIODevice::ReadOnly);
reader.setDevice(&buffer);
}
parseOne(reader);
parse(reader, data);
QVERIFY(reader.lastError() != QCborError::NoError);
// next() should fail
@ -997,7 +1024,7 @@ void tst_QCborStreamReader::addData_singleElement()
reader.addData(data.constData() + i, 1);
}
parseOne(reader);
parse(reader, data);
QCOMPARE(reader.lastError(), QCborError::EndOfFile);
}
@ -1009,7 +1036,7 @@ void tst_QCborStreamReader::addData_singleElement()
reader.addData(data.right(1));
}
QCOMPARE(reader.lastError(), QCborError::NoError);
QCOMPARE(parseOne(reader), expected);
QCOMPARE(parse(reader, data), expected);
}
void tst_QCborStreamReader::addData_complex()
@ -1085,6 +1112,69 @@ void tst_QCborStreamReader::addData_complex()
"{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)

View File

@ -91,8 +91,14 @@ private slots:
void toCbor_data();
void toCbor();
void toCborStreamWriter_data() { toCbor_data(); }
void toCborStreamWriter();
void fromCbor_data();
void fromCbor();
void fromCborStreamReaderByteArray_data() { fromCbor_data(); }
void fromCborStreamReaderByteArray();
void fromCborStreamReaderIODevice_data() { fromCbor_data(); }
void fromCborStreamReaderIODevice();
void validation_data();
void validation();
void toDiagnosticNotation_data();
@ -1454,6 +1460,22 @@ void tst_QCborValue::toCbor()
"\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()
{
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");
}
void tst_QCborValue::fromCbor()
void fromCbor_common(void (*doCheck)(const QCborValue &, const QByteArray &))
{
QFETCH(QCborValue, v);
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);
if (QTest::currentTestFailed())
return;
@ -1548,6 +1561,52 @@ void tst_QCborValue::fromCbor()
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()
{
addValidationColumns();

View File

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