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

Conflicts:
	src/widgets/styles/qstyle_p.h

Change-Id: I0e6e856bd6628da1135b3ba674dddffabbeb5c09
This commit is contained in:
Liang Qi 2019-04-11 07:47:21 +02:00
commit c7af193d2e
31 changed files with 838 additions and 385 deletions

View File

@ -242,7 +242,8 @@ static int doLink(int argc, char **argv)
#endif #endif
static int installFile(const QString &source, const QString &target, bool exe = false) static int installFile(const QString &source, const QString &target, bool exe = false,
bool preservePermissions = false)
{ {
QFile sourceFile(source); QFile sourceFile(source);
QFile targetFile(target); QFile targetFile(target);
@ -260,35 +261,32 @@ static int installFile(const QString &source, const QString &target, bool exe =
return 3; return 3;
} }
QFileDevice::Permissions targetPermissions = preservePermissions
? sourceFile.permissions()
: (QFileDevice::ReadOwner | QFileDevice::WriteOwner
| QFileDevice::ReadUser | QFileDevice::WriteUser
| QFileDevice::ReadGroup | QFileDevice::ReadOther);
if (exe) { if (exe) {
if (!targetFile.setPermissions(sourceFile.permissions() | QFileDevice::ExeOwner | QFileDevice::ExeUser | targetPermissions |= QFileDevice::ExeOwner | QFileDevice::ExeUser |
QFileDevice::ExeGroup | QFileDevice::ExeOther)) { QFileDevice::ExeGroup | QFileDevice::ExeOther;
fprintf(stderr, "Error setting execute permissions on %s: %s\n", }
qPrintable(target), qPrintable(targetFile.errorString())); if (!targetFile.setPermissions(targetPermissions)) {
return 3; fprintf(stderr, "Error setting permissions on %s: %s\n",
} qPrintable(target), qPrintable(targetFile.errorString()));
return 3;
} }
// Copy file times // Copy file times
QString error; QString error;
#ifdef Q_OS_WIN
const QFile::Permissions permissions = targetFile.permissions();
const bool readOnly = !(permissions & QFile::WriteUser);
if (readOnly)
targetFile.setPermissions(permissions | QFile::WriteUser);
#endif
if (!IoUtils::touchFile(target, sourceFile.fileName(), &error)) { if (!IoUtils::touchFile(target, sourceFile.fileName(), &error)) {
fprintf(stderr, "%s", qPrintable(error)); fprintf(stderr, "%s", qPrintable(error));
return 3; return 3;
} }
#ifdef Q_OS_WIN
if (readOnly)
targetFile.setPermissions(permissions);
#endif
return 0; return 0;
} }
static int installFileOrDirectory(const QString &source, const QString &target) static int installFileOrDirectory(const QString &source, const QString &target,
bool preservePermissions = false)
{ {
QFileInfo fi(source); QFileInfo fi(source);
if (false) { if (false) {
@ -308,18 +306,18 @@ static int installFileOrDirectory(const QString &source, const QString &target)
} else if (fi.isDir()) { } else if (fi.isDir()) {
QDir::current().mkpath(target); QDir::current().mkpath(target);
QDirIterator it(source, QDir::AllEntries | QDir::NoDotAndDotDot); QDirIterator it(source, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden);
while (it.hasNext()) { while (it.hasNext()) {
it.next(); it.next();
const QFileInfo &entry = it.fileInfo(); const QFileInfo &entry = it.fileInfo();
const QString &entryTarget = target + QDir::separator() + entry.fileName(); const QString &entryTarget = target + QDir::separator() + entry.fileName();
const int recursionResult = installFileOrDirectory(entry.filePath(), entryTarget); const int recursionResult = installFileOrDirectory(entry.filePath(), entryTarget, true);
if (recursionResult != 0) if (recursionResult != 0)
return recursionResult; return recursionResult;
} }
} else { } else {
const int fileCopyResult = installFile(source, target); const int fileCopyResult = installFile(source, target, /*exe*/ false, preservePermissions);
if (fileCopyResult != 0) if (fileCopyResult != 0)
return fileCopyResult; return fileCopyResult;
} }

View File

@ -257,6 +257,7 @@ CBOR_INLINE_API CborError cbor_encode_undefined(CborEncoder *encoder)
CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value)
{ return cbor_encode_floating_point(encoder, CborHalfFloatType, value); } { return cbor_encode_floating_point(encoder, CborHalfFloatType, value); }
CBOR_API CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value);
CBOR_INLINE_API CborError cbor_encode_float(CborEncoder *encoder, float value) CBOR_INLINE_API CborError cbor_encode_float(CborEncoder *encoder, float value)
{ return cbor_encode_floating_point(encoder, CborFloatType, &value); } { return cbor_encode_floating_point(encoder, CborFloatType, &value); }
CBOR_INLINE_API CborError cbor_encode_double(CborEncoder *encoder, double value) CBOR_INLINE_API CborError cbor_encode_double(CborEncoder *encoder, double value)
@ -593,12 +594,13 @@ CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *s
/* Floating point */ /* Floating point */
CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value)
{ return value->type == CborHalfFloatType; } { return value->type == CborHalfFloatType; }
CBOR_API CborError cbor_value_get_half_float_as_float(const CborValue *value, float *result);
CBOR_INLINE_API CborError cbor_value_get_half_float(const CborValue *value, void *result) CBOR_INLINE_API CborError cbor_value_get_half_float(const CborValue *value, void *result)
{ {
assert(cbor_value_is_half_float(value)); assert(cbor_value_is_half_float(value));
assert((value->flags & CborIteratorFlag_IntegerValueTooLarge) == 0); assert((value->flags & CborIteratorFlag_IntegerValueTooLarge) == 0);
/* size has been computed already */ /* size has already been computed */
memcpy(result, &value->extra, sizeof(value->extra)); memcpy(result, &value->extra, sizeof(value->extra));
return CborNoError; return CborNoError;
} }

View File

@ -76,7 +76,7 @@
* \code * \code
* uint8_t buf[16]; * uint8_t buf[16];
* CborEncoder encoder, mapEncoder; * CborEncoder encoder, mapEncoder;
* cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); * cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
* cbor_encoder_create_map(&encoder, &mapEncoder, 1); * cbor_encoder_create_map(&encoder, &mapEncoder, 1);
* cbor_encode_text_stringz(&mapEncoder, "foo"); * cbor_encode_text_stringz(&mapEncoder, "foo");
* cbor_encode_boolean(&mapEncoder, some_value); * cbor_encode_boolean(&mapEncoder, some_value);
@ -115,7 +115,7 @@
* uint8_t buf[16]; * uint8_t buf[16];
* CborError err; * CborError err;
* CborEncoder encoder, mapEncoder; * CborEncoder encoder, mapEncoder;
* cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); * cbor_encoder_init(&encoder, buf, sizeof(buf), 0);
* err = cbor_encoder_create_map(&encoder, &mapEncoder, 1); * err = cbor_encoder_create_map(&encoder, &mapEncoder, 1);
* if (!err) * if (!err)
* return err; * return err;
@ -155,7 +155,7 @@
* goto error; * goto error;
* buf = nbuf; * buf = nbuf;
* *
* cbor_encoder_init(&encoder, &buf, size, 0); * cbor_encoder_init(&encoder, buf, size, 0);
* err = cbor_encoder_create_array(&encoder, &arrayEncoder, n); * err = cbor_encoder_create_array(&encoder, &arrayEncoder, n);
* cbor_assert(err); // can't fail, the buffer is always big enough * cbor_assert(err); // can't fail, the buffer is always big enough
* *
@ -411,7 +411,7 @@ CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value)
* This function is useful for code that needs to pass through floating point * This function is useful for code that needs to pass through floating point
* values but does not wish to have the actual floating-point code. * values but does not wish to have the actual floating-point code.
* *
* \sa cbor_encode_half_float, cbor_encode_float, cbor_encode_double * \sa cbor_encode_half_float, cbor_encode_float_as_half_float, cbor_encode_float, cbor_encode_double
*/ */
CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value) CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value)
{ {
@ -621,13 +621,25 @@ CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *
* \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double() * \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double()
*/ */
/**
* \fn CborError cbor_encode_float_as_half_float(CborEncoder *encoder, float value)
*
* Convert the IEEE 754 single-precision (32-bit) floating point value \a value
* to the IEEE 754 half-precision (16-bit) floating point value and append it
* to the CBOR stream provided by \a encoder.
* The \a value should be in the range of the IEEE 754 half-precision floating point type,
* INFINITY, -INFINITY, or NAN, otherwise the behavior of this function is undefined.
*
* \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double()
*/
/** /**
* \fn CborError cbor_encode_float(CborEncoder *encoder, float value) * \fn CborError cbor_encode_float(CborEncoder *encoder, float value)
* *
* Appends the IEEE 754 single-precision (32-bit) floating point value \a value * Appends the IEEE 754 single-precision (32-bit) floating point value \a value
* to the CBOR stream provided by \a encoder. * to the CBOR stream provided by \a encoder.
* *
* \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_double() * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_double()
*/ */
/** /**
@ -636,7 +648,7 @@ CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *
* Appends the IEEE 754 double-precision (64-bit) floating point value \a value * Appends the IEEE 754 double-precision (64-bit) floating point value \a value
* to the CBOR stream provided by \a encoder. * to the CBOR stream provided by \a encoder.
* *
* \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float() * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float_as_half_float(), cbor_encode_float()
*/ */
/** /**

View File

@ -1520,7 +1520,7 @@ error:
* floating point, this function takes a \c{void *} as a parameter for the * floating point, this function takes a \c{void *} as a parameter for the
* storage area, which must be at least 16 bits wide. * storage area, which must be at least 16 bits wide.
* *
* \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_float() * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_half_float_as_float(), cbor_value_get_float()
*/ */
/** @} */ /** @} */

View File

@ -106,7 +106,7 @@
# define cbor_ntohs __builtin_bswap16 # define cbor_ntohs __builtin_bswap16
# define cbor_htons __builtin_bswap16 # define cbor_htons __builtin_bswap16
# else # else
# define cbor_ntohs(x) (((uint16_t)x >> 8) | ((uint16_t)x << 8)) # define cbor_ntohs(x) (((uint16_t)(x) >> 8) | ((uint16_t)(x) << 8))
# define cbor_htons cbor_ntohs # define cbor_htons cbor_ntohs
# endif # endif
# else # else

View File

@ -123,6 +123,42 @@ QVariant make_ilmap(const std::initializer_list<QPair<QVariant, QVariant>> &list
return QVariant::fromValue(IndeterminateLengthMap(list)); return QVariant::fromValue(IndeterminateLengthMap(list));
} }
void addHalfFloat()
{
QTest::addColumn<QByteArray>("output");
QTest::addColumn<unsigned>("rawInput");
QTest::addColumn<double>("floatInput");
QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0;
QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0;
QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10);
QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10);
QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10));
QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10));
QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14);
QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14);
QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0;
QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0;
QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5;
QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5;
QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10));
QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10));
QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << myInf();
QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << myNInf();
QTest::newRow("nan1") << raw("\x7c\x01") << 0x7c01U << myNaN();
QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << myNaN();
QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << myNaN();
QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << myNaN();
}
void addColumns() void addColumns()
{ {
QTest::addColumn<QByteArray>("output"); QTest::addColumn<QByteArray>("output");

View File

@ -41,6 +41,13 @@ class tst_Encoder : public QObject
{ {
Q_OBJECT Q_OBJECT
private slots: private slots:
void floatAsHalfFloat_data();
void floatAsHalfFloat();
void halfFloat_data();
void halfFloat();
void floatAsHalfFloatCloseToZero_data();
void floatAsHalfFloatCloseToZero();
void floatAsHalfFloatNaN();
void fixed_data(); void fixed_data();
void fixed(); void fixed();
void strings_data(); void strings_data();
@ -178,21 +185,127 @@ CborError encodeVariant(CborEncoder *encoder, const QVariant &v)
return CborErrorUnknownType; return CborErrorUnknownType;
} }
void compare(const QVariant &input, const QByteArray &output) template <typename Input, typename FnUnderTest>
void encodeOne(Input input, FnUnderTest fn_under_test, QByteArray &buffer, CborError &error)
{ {
QByteArray buffer(output.length(), Qt::Uninitialized);
uint8_t *bufptr = reinterpret_cast<quint8 *>(buffer.data()); uint8_t *bufptr = reinterpret_cast<quint8 *>(buffer.data());
CborEncoder encoder; CborEncoder encoder;
cbor_encoder_init(&encoder, bufptr, buffer.length(), 0); cbor_encoder_init(&encoder, bufptr, buffer.length(), 0);
QCOMPARE(encodeVariant(&encoder, input), CborNoError); error = fn_under_test(&encoder, input);
QCOMPARE(encoder.remaining, size_t(1));
QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0));
buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr))); if (error == CborNoError) {
QCOMPARE(encoder.remaining, size_t(1));
QCOMPARE(cbor_encoder_get_extra_bytes_needed(&encoder), size_t(0));
buffer.resize(int(cbor_encoder_get_buffer_size(&encoder, bufptr)));
}
}
template <typename Input, typename FnUnderTest>
void compare(Input input, FnUnderTest fn_under_test, const QByteArray &output)
{
QByteArray buffer(output.length(), Qt::Uninitialized);
CborError error;
encodeOne(input, fn_under_test, buffer, error);
if (QTest::currentTestFailed())
return;
QCOMPARE(error, CborNoError);
QCOMPARE(buffer, output); QCOMPARE(buffer, output);
} }
void compare(const QVariant &input, const QByteArray &output)
{
compare(input, encodeVariant, output);
}
void tst_Encoder::floatAsHalfFloat_data()
{
addHalfFloat();
}
void tst_Encoder::floatAsHalfFloat()
{
QFETCH(unsigned, rawInput);
QFETCH(double, floatInput);
QFETCH(QByteArray, output);
if (rawInput == 0U || rawInput == 0x8000U)
QSKIP("zero values are out of scope of this test case", QTest::SkipSingle);
if (qIsNaN(floatInput))
QSKIP("NaN values are out of scope of this test case", QTest::SkipSingle);
output.prepend('\xf9');
compare((float)floatInput, cbor_encode_float_as_half_float, output);
}
void tst_Encoder::halfFloat_data()
{
addHalfFloat();
}
void tst_Encoder::halfFloat()
{
QFETCH(unsigned, rawInput);
QFETCH(QByteArray, output);
uint16_t v = (uint16_t)rawInput;
output.prepend('\xf9');
compare(&v, cbor_encode_half_float, output);
}
void tst_Encoder::floatAsHalfFloatCloseToZero_data()
{
QTest::addColumn<double>("floatInput");
QTest::newRow("+0") << 0.0;
QTest::newRow("-0") << -0.0;
QTest::newRow("below min.denorm") << ldexp(1.0, -14) * ldexp(1.0, -11);
QTest::newRow("above -min.denorm") << ldexp(-1.0, -14) * ldexp(1.0, -11);
}
void tst_Encoder::floatAsHalfFloatCloseToZero()
{
QFETCH(double, floatInput);
QByteArray buffer(4, Qt::Uninitialized);
CborError error;
encodeOne((float)floatInput, cbor_encode_float_as_half_float, buffer, error);
QCOMPARE(error, CborNoError);
QVERIFY2(
buffer == raw("\xf9\x00\x00") || buffer == raw("\xf9\x80\x00"),
"Got value " + QByteArray::number(floatInput) + " encoded to: " + buffer);
}
void tst_Encoder::floatAsHalfFloatNaN()
{
QByteArray buffer(4, Qt::Uninitialized);
CborError error;
encodeOne(myNaNf(), cbor_encode_float_as_half_float, buffer, error);
QCOMPARE(error, CborNoError);
QCOMPARE(buffer.size(), 3);
uint8_t ini_byte = (uint8_t)buffer[0],
exp = (uint8_t)buffer[1] & 0x7cU,
manth = (uint8_t)buffer[1] & 0x03U,
mantl = (uint8_t)buffer[2];
QCOMPARE((unsigned)ini_byte, 0xf9U);
QCOMPARE((unsigned)exp, 0x7cU);
QVERIFY((manth | mantl) != 0);
}
void tst_Encoder::fixed_data() void tst_Encoder::fixed_data()
{ {
addColumns(); addColumns();

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2017 Intel Corporation ** Copyright (C) 2019 Intel Corporation
** **
** Permission is hereby granted, free of charge, to any person obtaining a copy ** Permission is hereby granted, free of charge, to any person obtaining a copy
** of this software and associated documentation files (the "Software"), to deal ** of this software and associated documentation files (the "Software"), to deal
@ -23,11 +23,22 @@
****************************************************************************/ ****************************************************************************/
#define _XOPEN_SOURCE 700 #define _XOPEN_SOURCE 700
#define _DARWIN_C_SOURCE 1 /* need MAP_ANON */
#include <QtTest> #include <QtTest>
#include "cbor.h" #include "cbor.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#if defined(Q_OS_UNIX)
# include <sys/mman.h>
# include <unistd.h>
#elif defined(Q_OS_WIN)
# define WIN32_LEAN_AND_MEAN 1
# define NOMINMAX 1
# include <windows.h>
#endif
namespace QTest { namespace QTest {
template<> char *toString<CborError>(const CborError &err) template<> char *toString<CborError>(const CborError &err)
{ {
@ -44,6 +55,8 @@ private slots:
// parsing API // parsing API
void integers_data(); void integers_data();
void integers(); void integers();
void halfFloat_data();
void halfFloat();
void fixed_data(); void fixed_data();
void fixed(); void fixed();
void strings_data(); void strings_data();
@ -105,6 +118,100 @@ private slots:
void recursionLimit(); void recursionLimit();
}; };
struct ParserWrapper
{
void *realdata = nullptr;
uint8_t *data;
size_t len;
CborParser parser;
CborValue first;
~ParserWrapper() { freeMemory(); }
CborError init(const QByteArray &ba, uint32_t flags = 0)
{
return init(ba.constData(), ba.size(), flags);
}
CborError init(const char *ptr, int n, uint32_t flags = 0)
{
freeMemory();
data = allocateMemory(n);
memcpy(data, ptr, len);
return cbor_parser_init(data, len, flags, &parser, &first);
}
uint8_t *begin() { return data; }
uint8_t *end() { return data + len; }
uint8_t *allocateMemory(size_t);
void freeMemory();
static const size_t PageSize = 4096;
static inline size_t mmapAllocation(size_t n)
{
// round up and add one page
return (n + 2*PageSize) & ~(PageSize - 1);
}
static bool shouldUseMmap();
};
bool ParserWrapper::shouldUseMmap()
{
static int v = qEnvironmentVariableIntValue("PARSER_NO_MMAP");
return !v;
}
uint8_t *ParserWrapper::allocateMemory(size_t n)
{
len = n;
if (shouldUseMmap()) {
size_t alloc = mmapAllocation(n);
#if defined(Q_OS_UNIX)
realdata = mmap(nullptr, alloc, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
Q_ASSERT_X(realdata != MAP_FAILED, "allocateMemory", "mmap failed!");
// mark last page inaccessible
uint8_t *ptr = static_cast<uint8_t *>(realdata);
ptr += alloc - PageSize;
mprotect(ptr, PageSize, PROT_NONE);
ptr -= n;
return ptr;
#elif defined(Q_OS_WIN)
DWORD flAllocationType = MEM_COMMIT | MEM_RESERVE;
DWORD flProtect = PAGE_READWRITE;
realdata = VirtualAlloc(nullptr, alloc, flAllocationType, flProtect);
Q_ASSERT_X(realdata, "allocateMemory", "VirtualAlloc failed!");
// mark last page inaccessible
uint8_t *ptr = static_cast<uint8_t *>(realdata);
ptr += alloc - PageSize;
VirtualProtect(ptr, PageSize, PAGE_NOACCESS, nullptr);
ptr -= n;
return ptr;
#endif
}
realdata = malloc(n);
return static_cast<uint8_t *>(realdata);
}
void ParserWrapper::freeMemory()
{
if (shouldUseMmap()) {
if (realdata) {
#if defined(Q_OS_UNIX)
size_t alloc = mmapAllocation(len);
munmap(realdata, alloc);
#elif defined(Q_OS_WIN)
VirtualFree(realdata, 0, MEM_RELEASE);
#endif
}
return;
}
free(realdata);
}
static CborError qstring_printf(void *out, const char *fmt, ...) static CborError qstring_printf(void *out, const char *fmt, ...)
{ {
auto str = static_cast<QString *>(out); auto str = static_cast<QString *>(out);
@ -166,47 +273,46 @@ bool compareFailed = true;
void compareOne_real(const QByteArray &data, const QString &expected, int line, int n = -1) void compareOne_real(const QByteArray &data, const QString &expected, int line, int n = -1)
{ {
compareFailed = true; compareFailed = true;
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\"");
if (cbor_value_get_type(&first) == CborArrayType) { if (cbor_value_get_type(&w.first) == CborArrayType) {
size_t len; size_t len;
if (n >= 0) { if (n >= 0) {
QVERIFY(cbor_value_is_length_known(&first)); QVERIFY(cbor_value_is_length_known(&w.first));
QCOMPARE(cbor_value_get_array_length(&first, &len), CborNoError); QCOMPARE(cbor_value_get_array_length(&w.first, &len), CborNoError);
QCOMPARE(len, size_t(len)); QCOMPARE(len, size_t(len));
} else { } else {
QVERIFY(!cbor_value_is_length_known(&first)); QVERIFY(!cbor_value_is_length_known(&w.first));
QCOMPARE(cbor_value_get_array_length(&first, &len), CborErrorUnknownLength); QCOMPARE(cbor_value_get_array_length(&w.first, &len), CborErrorUnknownLength);
} }
} else if (cbor_value_get_type(&first) == CborMapType) { } else if (cbor_value_get_type(&w.first) == CborMapType) {
size_t len; size_t len;
if (n >= 0) { if (n >= 0) {
QVERIFY(cbor_value_is_length_known(&first)); QVERIFY(cbor_value_is_length_known(&w.first));
QCOMPARE(cbor_value_get_map_length(&first, &len), CborNoError); QCOMPARE(cbor_value_get_map_length(&w.first, &len), CborNoError);
QCOMPARE(len, size_t(len)); QCOMPARE(len, size_t(len));
} else { } else {
QVERIFY(!cbor_value_is_length_known(&first)); QVERIFY(!cbor_value_is_length_known(&w.first));
QCOMPARE(cbor_value_get_map_length(&first, &len), CborErrorUnknownLength); QCOMPARE(cbor_value_get_map_length(&w.first, &len), CborErrorUnknownLength);
} }
} else if (cbor_value_is_text_string(&first) || cbor_value_is_byte_string(&first)) { } else if (cbor_value_is_text_string(&w.first) || cbor_value_is_byte_string(&w.first)) {
size_t len; size_t len;
QCOMPARE(cbor_value_calculate_string_length(&first, &len), CborNoError); QCOMPARE(cbor_value_calculate_string_length(&w.first, &len), CborNoError);
if (cbor_value_is_length_known(&first)) { if (cbor_value_is_length_known(&w.first)) {
size_t len2; size_t len2;
QCOMPARE(cbor_value_get_string_length(&first, &len2), CborNoError); QCOMPARE(cbor_value_get_string_length(&w.first, &len2), CborNoError);
QCOMPARE(len2, len); QCOMPARE(len2, len);
} else { } else {
QCOMPARE(cbor_value_get_string_length(&first, &len), CborErrorUnknownLength); QCOMPARE(cbor_value_get_string_length(&w.first, &len), CborErrorUnknownLength);
} }
} }
CborError err2 = cbor_value_validate_basic(&first); CborError err2 = cbor_value_validate_basic(&w.first);
QString decoded; QString decoded;
err = parseOne(&first, &decoded); err = parseOne(&w.first, &decoded);
QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) +
"\"; decoded stream:\n" + decoded.toLatin1()); "\"; decoded stream:\n" + decoded.toLatin1());
QCOMPARE(decoded, expected); QCOMPARE(decoded, expected);
@ -215,7 +321,7 @@ void compareOne_real(const QByteArray &data, const QString &expected, int line,
QCOMPARE(err2, err); QCOMPARE(err2, err);
// check that we consumed everything // check that we consumed everything
QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd()); QCOMPARE((void*)cbor_value_get_next_byte(&w.first), (void*)w.end());
compareFailed = false; compareFailed = false;
} }
@ -237,39 +343,112 @@ void tst_Parser::integers()
QFETCH(qint64, expectedValue); QFETCH(qint64, expectedValue);
QFETCH(bool, inInt64Range); QFETCH(bool, inInt64Range);
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
QVERIFY(cbor_value_is_integer(&first)); QVERIFY(cbor_value_is_integer(&w.first));
uint64_t raw; uint64_t raw;
cbor_value_get_raw_integer(&first, &raw); cbor_value_get_raw_integer(&w.first, &raw);
QCOMPARE(quint64(raw), expectedRaw); QCOMPARE(quint64(raw), expectedRaw);
if (isNegative) { if (isNegative) {
QVERIFY(cbor_value_is_negative_integer(&first)); QVERIFY(cbor_value_is_negative_integer(&w.first));
QVERIFY(!cbor_value_is_unsigned_integer(&first)); QVERIFY(!cbor_value_is_unsigned_integer(&w.first));
} else { } else {
QVERIFY(!cbor_value_is_negative_integer(&first)); QVERIFY(!cbor_value_is_negative_integer(&w.first));
QVERIFY(cbor_value_is_unsigned_integer(&first)); QVERIFY(cbor_value_is_unsigned_integer(&w.first));
} }
int64_t value; int64_t value;
if (inInt64Range) { if (inInt64Range) {
cbor_value_get_int64(&first, &value); cbor_value_get_int64(&w.first, &value);
QCOMPARE(qint64(value), expectedValue); QCOMPARE(qint64(value), expectedValue);
} }
err = cbor_value_get_int64_checked(&first, &value); err = cbor_value_get_int64_checked(&w.first, &value);
QCOMPARE(err, inInt64Range ? CborNoError : CborErrorDataTooLarge); QCOMPARE(err, inInt64Range ? CborNoError : CborErrorDataTooLarge);
int ivalue; int ivalue;
bool inIntRange = inInt64Range && (expectedValue == int(expectedValue)); bool inIntRange = inInt64Range && (expectedValue == int(expectedValue));
err = cbor_value_get_int_checked(&first, &ivalue); err = cbor_value_get_int_checked(&w.first, &ivalue);
QCOMPARE(err, inIntRange ? CborNoError : CborErrorDataTooLarge); QCOMPARE(err, inIntRange ? CborNoError : CborErrorDataTooLarge);
} }
static void addHalfFloat()
{
QTest::addColumn<QByteArray>("data");
QTest::addColumn<unsigned>("expectedRaw");
QTest::addColumn<double>("expectedValue");
QTest::newRow("+0") << raw("\x00\x00") << 0U << 0.0;
QTest::newRow("-0") << raw("\x80\x00") << 0x8000U << 0.0;
QTest::newRow("min.denorm") << raw("\x00\x01") << 1U << ldexp(1.0, -14) * ldexp(1.0, -10);
QTest::newRow("-min.denorm") << raw("\x80\x01") << 0x8001U << ldexp(-1.0, -14) * ldexp(1.0, -10);
QTest::newRow("max.denorm") << raw("\x03\xff") << 0x03ffU << ldexp(1.0, -14) * (1.0 - ldexp(1.0, -10));
QTest::newRow("-max.denorm") << raw("\x83\xff") << 0x83ffU << ldexp(-1.0, -14) * (1.0 - ldexp(1.0, -10));
QTest::newRow("min.norm") << raw("\x04\x00") << 0x0400U << ldexp(1.0, -14);
QTest::newRow("-min.norm") << raw("\x84\x00") << 0x8400U << ldexp(-1.0, -14);
QTest::newRow("1.0") << raw("\x3c\x00") << 0x3c00U << 1.0;
QTest::newRow("-1.0") << raw("\xbc\x00") << 0xbc00U << -1.0;
QTest::newRow("1.5") << raw("\x3e\x00") << 0x3e00U << 1.5;
QTest::newRow("-1.5") << raw("\xbe\x00") << 0xbe00U << -1.5;
QTest::newRow("max") << raw("\x7b\xff") << 0x7bffU << ldexp(1.0, 15) * (2.0 - ldexp(1.0, -10));
QTest::newRow("-max") << raw("\xfb\xff") << 0xfbffU << ldexp(-1.0, 15) * (2.0 - ldexp(1.0, -10));
QTest::newRow("inf") << raw("\x7c\x00") << 0x7c00U << double(INFINITY);
QTest::newRow("-inf") << raw("\xfc\x00") << 0xfc00U << double(-INFINITY);
QTest::newRow("nan") << raw("\x7c\x01") << 0x7c01U << double(NAN);
QTest::newRow("nan2") << raw("\xfc\x01") << 0xfc01U << double(NAN);
QTest::newRow("nan3") << raw("\x7e\x00") << 0x7e00U << double(NAN);
QTest::newRow("nan4") << raw("\xfe\x00") << 0xfe00U << double(NAN);
}
void tst_Parser::halfFloat_data()
{
addHalfFloat();
}
void tst_Parser::halfFloat()
{
QFETCH(QByteArray, data);
QFETCH(unsigned, expectedRaw);
QFETCH(double, expectedValue);
CborParser parser;
CborValue first;
data.prepend('\xf9');
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
QVERIFY(cbor_value_is_half_float(&first));
uint16_t raw;
cbor_value_get_half_float(&first, &raw);
QCOMPARE(raw, uint16_t(expectedRaw));
float value;
cbor_value_get_half_float_as_float(&first, &value);
const double epsilon = ldexp(1.0, -25);
if (qIsNaN(expectedValue)) {
QVERIFY(qIsNaN(value));
} else if (qIsInf(expectedValue)) {
QVERIFY(value == (float)expectedValue);
} else {
QVERIFY(qAbs(value - (float)expectedValue) < epsilon);
}
}
void tst_Parser::fixed_data() void tst_Parser::fixed_data()
{ {
addColumns(); addColumns();
@ -678,14 +857,13 @@ void tst_Parser::chunkedString_data()
static void chunkedStringTest(const QByteArray &data, const QString &concatenated, static void chunkedStringTest(const QByteArray &data, const QString &concatenated,
QStringList &chunks, CborType ourType) QStringList &chunks, CborType ourType)
{ {
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
CborValue value; CborValue value;
QVERIFY(cbor_value_is_array(&first)); QVERIFY(cbor_value_is_array(&w.first));
err = cbor_value_enter_container(&first, &value); err = cbor_value_enter_container(&w.first, &value);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
QVERIFY(cbor_value_is_byte_string(&value) || cbor_value_is_text_string(&value)); QVERIFY(cbor_value_is_byte_string(&value) || cbor_value_is_text_string(&value));
@ -748,9 +926,9 @@ static void chunkedStringTest(const QByteArray &data, const QString &concatenate
// confirm EOF // confirm EOF
QVERIFY(cbor_value_at_end(&value)); QVERIFY(cbor_value_at_end(&value));
err = cbor_value_leave_container(&first, &value); err = cbor_value_leave_container(&w.first, &value);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
QCOMPARE((void*)cbor_value_get_next_byte(&first), (void*)data.constEnd()); QCOMPARE((void*)cbor_value_get_next_byte(&w.first), (void*)w.end());
} }
void tst_Parser::chunkedString() void tst_Parser::chunkedString()
@ -840,18 +1018,17 @@ void tst_Parser::stringLength()
QFETCH(QByteArray, data); QFETCH(QByteArray, data);
QFETCH(int, expected); QFETCH(int, expected);
CborParser parser; ParserWrapper w;
CborValue value; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &value);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
size_t result; size_t result;
err = cbor_value_calculate_string_length(&value, &result); err = cbor_value_calculate_string_length(&w.first, &result);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
QCOMPARE(result, size_t(expected)); QCOMPARE(result, size_t(expected));
if (cbor_value_is_length_known(&value)) { if (cbor_value_is_length_known(&w.first)) {
QCOMPARE(cbor_value_get_string_length(&value, &result), CborNoError); QCOMPARE(cbor_value_get_string_length(&w.first, &result), CborNoError);
QCOMPARE(result, size_t(expected)); QCOMPARE(result, size_t(expected));
} }
@ -935,25 +1112,24 @@ void compareOneString(const QByteArray &data, const QString &string, bool expect
{ {
compareFailed = true; compareFailed = true;
CborParser parser; ParserWrapper w;
CborValue value; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &value);
QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\"");
bool result; bool result;
QByteArray bastring = string.toUtf8(); QByteArray bastring = string.toUtf8();
err = cbor_value_text_string_equals(&value, bastring.constData(), &result); err = cbor_value_text_string_equals(&w.first, bastring.constData(), &result);
QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray::number(line) + ": Got error \"" + cbor_error_string(err) + "\"");
QCOMPARE(result, expected); QCOMPARE(result, expected);
if (expected) { if (expected) {
size_t len; size_t len;
cbor_value_skip_tag(&value); cbor_value_skip_tag(&w.first);
if (cbor_value_is_length_known(&value)) { if (cbor_value_is_length_known(&w.first)) {
QCOMPARE(cbor_value_get_string_length(&value, &len), CborNoError); QCOMPARE(cbor_value_get_string_length(&w.first, &len), CborNoError);
QCOMPARE(int(len), bastring.size()); QCOMPARE(int(len), bastring.size());
} }
QCOMPARE(cbor_value_calculate_string_length(&value, &len), CborNoError); QCOMPARE(cbor_value_calculate_string_length(&w.first, &len), CborNoError);
QCOMPARE(int(len), bastring.size()); QCOMPARE(int(len), bastring.size());
} }
@ -1042,13 +1218,12 @@ void tst_Parser::mapFind()
QFETCH(QByteArray, data); QFETCH(QByteArray, data);
QFETCH(bool, expected); QFETCH(bool, expected);
CborParser parser; ParserWrapper w;
CborValue value; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &value);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
CborValue element; CborValue element;
err = cbor_value_map_find_value(&value, "needle", &element); err = cbor_value_map_find_value(&w.first, "needle", &element);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
if (expected) { if (expected) {
@ -1119,13 +1294,12 @@ void tst_Parser::checkedIntegers()
QFETCH(QVariant, result); QFETCH(QVariant, result);
int64_t expected = result.toLongLong(); int64_t expected = result.toLongLong();
CborParser parser; ParserWrapper w;
CborValue value; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &value);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
int64_t v; int64_t v;
err = cbor_value_get_int64_checked(&value, &v); err = cbor_value_get_int64_checked(&w.first, &v);
if (result.isNull()) { if (result.isNull()) {
QCOMPARE(err, CborErrorDataTooLarge); QCOMPARE(err, CborErrorDataTooLarge);
} else { } else {
@ -1133,7 +1307,7 @@ void tst_Parser::checkedIntegers()
} }
int v2; int v2;
err = cbor_value_get_int_checked(&value, &v2); err = cbor_value_get_int_checked(&w.first, &v2);
if (result.isNull() || expected < std::numeric_limits<int>::min() || expected > std::numeric_limits<int>::max()) { if (result.isNull() || expected < std::numeric_limits<int>::min() || expected > std::numeric_limits<int>::max()) {
QCOMPARE(err, CborErrorDataTooLarge); QCOMPARE(err, CborErrorDataTooLarge);
} else { } else {
@ -1154,14 +1328,13 @@ void tst_Parser::validation()
QFETCH(CborError, expectedError); QFETCH(CborError, expectedError);
QString decoded; QString decoded;
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data, uint32_t(flags));
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), flags, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
CborError err2 = cbor_value_validate_basic(&first); CborError err2 = cbor_value_validate_basic(&w.first);
CborError err3 = cbor_value_validate(&first, CborValidateBasic); CborError err3 = cbor_value_validate(&w.first, CborValidateBasic);
err = parseOne(&first, &decoded); err = parseOne(&w.first, &decoded);
QCOMPARE(err, expectedError); QCOMPARE(err, expectedError);
if (!QByteArray(QTest::currentDataTag()).contains("utf8")) { if (!QByteArray(QTest::currentDataTag()).contains("utf8")) {
QCOMPARE(err2, expectedError); QCOMPARE(err2, expectedError);
@ -1352,24 +1525,36 @@ void tst_Parser::strictValidation_data()
QTest::newRow("overlong-_stringx2-0*8") << raw("\x7f\x60\x7b\0\0\0\0\0\0\0\0\xff") << int(CborValidateShortestNumbers) << CborErrorOverlongEncoding; QTest::newRow("overlong-_stringx2-0*8") << raw("\x7f\x60\x7b\0\0\0\0\0\0\0\0\xff") << int(CborValidateShortestNumbers) << CborErrorOverlongEncoding;
// strict mode // strict mode
QTest::newRow("invalid-utf8-1char") << raw("\x61\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; // UTF-8 sequences with invalid continuation bytes
QTest::newRow("invalid-utf8-2chars-1") << raw("\x62\xc2\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-1char") << raw("\x61\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-2chars-2") << raw("\x62\xc3\xdf") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-2chars-1") << raw("\x62\xc2\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-2chars-3") << raw("\x62\xc7\xf0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-2chars-2") << raw("\x62\xc3\xdf") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-3chars-1") << raw("\x63\xe0\xa0\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-2chars-3") << raw("\x62\xc7\xf0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-3chars-2") << raw("\x63\xe0\xc0\xa0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-3chars-1") << raw("\x63\xe0\xa0\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-4chars-1") << raw("\x64\xf0\x90\x80\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-3chars-2") << raw("\x63\xe0\xc0\xa0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-4chars-2") << raw("\x64\xf0\x90\xc0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-4chars-1") << raw("\x64\xf0\x90\x80\xc0") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-4chars-3") << raw("\x64\xf0\xc0\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-bad-continuation-4chars-2") << raw("\x64\xf0\x90\xc0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-bad-continuation-4chars-3") << raw("\x64\xf0\xc0\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
// Too short UTF-8 sequences (in an array so there's a byte after that would make it valid UTF-8 if it were part of the string)
QTest::newRow("invalid-utf8-too-short-2chars") << raw("\x82\x61\xc2\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-too-short-3chars-1") << raw("\x82\x61\xe0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-too-short-3chars-2") << raw("\x82\x62\xe0\xa0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-too-short-4chars-1") << raw("\x82\x61\xf0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-too-short-4chars-2") << raw("\x82\x62\xf0\x90\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-too-short-4chars-3") << raw("\x82\x63\xf0\x90\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
// UTF-16 surrogages encoded in UTF-8
QTest::newRow("invalid-utf8-hi-surrogate") << raw("\x63\xed\xa0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-hi-surrogate") << raw("\x63\xed\xa0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-lo-surrogate") << raw("\x63\xed\xb0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-lo-surrogate") << raw("\x63\xed\xb0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-surrogate-pair") << raw("\x66\xed\xa0\x80\xed\xb0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-surrogate-pair") << raw("\x66\xed\xa0\x80\xed\xb0\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
// Non-Unicode UTF-8 sequences
QTest::newRow("invalid-utf8-non-unicode-1") << raw("\x64\xf4\x90\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-non-unicode-1") << raw("\x64\xf4\x90\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-non-unicode-2") << raw("\x65\xf8\x88\x80\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-non-unicode-2") << raw("\x65\xf8\x88\x80\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-non-unicode-3") << raw("\x66\xfc\x84\x80\x80\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-non-unicode-3") << raw("\x66\xfc\x84\x80\x80\x80\x80") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-non-unicode-4") << raw("\x66\xfd\xbf\xbf\xbf\xbf\xbf") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-non-unicode-4") << raw("\x66\xfd\xbf\xbf\xbf\xbf\xbf") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
// invalid bytes in UTF-8
QTest::newRow("invalid-utf8-fe") << raw("\x61\xfe") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-fe") << raw("\x61\xfe") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-ff") << raw("\x61\xff") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-ff") << raw("\x61\xff") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
// Overlong sequences
QTest::newRow("invalid-utf8-overlong-1-2") << raw("\x62\xc1\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-overlong-1-2") << raw("\x62\xc1\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-overlong-1-3") << raw("\x63\xe0\x81\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-overlong-1-3") << raw("\x63\xe0\x81\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
QTest::newRow("invalid-utf8-overlong-1-4") << raw("\x64\xf0\x80\x81\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString; QTest::newRow("invalid-utf8-overlong-1-4") << raw("\x64\xf0\x80\x81\x81") << int(CborValidateStrictMode) << CborErrorInvalidUtf8TextString;
@ -1525,12 +1710,11 @@ void tst_Parser::strictValidation()
QFETCH(CborError, expectedError); QFETCH(CborError, expectedError);
QString decoded; QString decoded;
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
err = cbor_value_validate(&first, flags); err = cbor_value_validate(&w.first, flags);
QCOMPARE(err, expectedError); QCOMPARE(err, expectedError);
} }
@ -1549,12 +1733,11 @@ void tst_Parser::incompleteData()
QFETCH(QString, expected); QFETCH(QString, expected);
for (int len = 0; len < data.length() - 1; ++len) { for (int len = 0; len < data.length() - 1; ++len) {
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data.constData(), len);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), len, 0, &parser, &first);
if (!err) { if (!err) {
QString decoded; QString decoded;
err = parseOne(&first, &decoded); err = parseOne(&w.first, &decoded);
} }
if (err != CborErrorUnexpectedEOF) if (err != CborErrorUnexpectedEOF)
qDebug() << "Length is" << len; qDebug() << "Length is" << len;
@ -1583,14 +1766,13 @@ void tst_Parser::endPointer()
QFETCH(int, offset); QFETCH(int, offset);
QString decoded; QString decoded;
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
err = parseOne(&first, &decoded); err = parseOne(&w.first, &decoded);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
QCOMPARE(int(cbor_value_get_next_byte(&first) - reinterpret_cast<const quint8 *>(data.constBegin())), offset); QCOMPARE(int(cbor_value_get_next_byte(&w.first) - w.begin()), offset);
} }
void tst_Parser::recursionLimit_data() void tst_Parser::recursionLimit_data()
@ -1638,24 +1820,23 @@ void tst_Parser::recursionLimit()
{ {
QFETCH(QByteArray, data); QFETCH(QByteArray, data);
CborParser parser; ParserWrapper w;
CborValue first; CborError err = w.init(data);
CborError err = cbor_parser_init(reinterpret_cast<const quint8 *>(data.constData()), data.length(), 0, &parser, &first);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
// check that it is valid: // check that it is valid:
CborValue it = first; CborValue it = w.first;
{ {
QString dummy; QString dummy;
err = parseOne(&it, &dummy); err = parseOne(&it, &dummy);
QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\""); QVERIFY2(!err, QByteArray("Got error \"") + cbor_error_string(err) + "\"");
} }
it = first; it = w.first;
err = cbor_value_advance(&it); err = cbor_value_advance(&it);
QCOMPARE(err, CborErrorNestingTooDeep); QCOMPARE(err, CborErrorNestingTooDeep);
it = first; it = w.first;
if (cbor_value_is_map(&it)) { if (cbor_value_is_map(&it)) {
CborValue dummy; CborValue dummy;
err = cbor_value_map_find_value(&it, "foo", &dummy); err = cbor_value_map_find_value(&it, "foo", &dummy);

View File

@ -77,15 +77,6 @@ static inline qint64 ticksToNanoseconds(qint64 ticks)
return ticks * 1000000; return ticks * 1000000;
} }
static inline qint64 nanosecondsToTicks(qint64 nsec)
{
if (counterFrequency > 0) {
// QueryPerformanceCounter uses an arbitrary frequency
return double(nsec) * counterFrequency / 1000000000.;
}
// GetTickCount(64) uses milliseconds
return nsec / 1000000;
}
static quint64 getTickCount() static quint64 getTickCount()
{ {

View File

@ -235,6 +235,12 @@ QObject *QMetaObject::newInstance(QGenericArgument val0,
QGenericArgument val8, QGenericArgument val8,
QGenericArgument val9) const QGenericArgument val9) const
{ {
if (!inherits(&QObject::staticMetaObject))
{
qWarning("QMetaObject::newInstance: type %s does not inherit QObject", className());
return nullptr;
}
QByteArray constructorName = className(); QByteArray constructorName = className();
{ {
int idx = constructorName.lastIndexOf(':'); int idx = constructorName.lastIndexOf(':');

View File

@ -817,7 +817,7 @@ bool QTranslatorPrivate::do_load(const uchar *data, qsizetype len, const QString
data += MagicLength; data += MagicLength;
QStringList dependencies; QStringList dependencies;
while (data < end - 4) { while (data < end - 5) {
quint8 tag = read8(data++); quint8 tag = read8(data++);
quint32 blockLen = read32(data); quint32 blockLen = read32(data);
data += 4; data += 4;

View File

@ -101,6 +101,16 @@ static CborError _cbor_value_dup_string(const CborValue *, void **, size_t *, Cb
Q_UNREACHABLE(); Q_UNREACHABLE();
return CborErrorInternalError; return CborErrorInternalError;
} }
static CborError cbor_value_get_half_float_as_float(const CborValue *, float *)
{
Q_UNREACHABLE();
return CborErrorInternalError;
}
static CborError cbor_encode_float_as_half_float(CborEncoder *, float)
{
Q_UNREACHABLE();
return CborErrorInternalError;
}
QT_WARNING_POP QT_WARNING_POP
Q_DECLARE_TYPEINFO(CborEncoder, Q_PRIMITIVE_TYPE); Q_DECLARE_TYPEINFO(CborEncoder, Q_PRIMITIVE_TYPE);

View File

@ -260,21 +260,22 @@ namespace
QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node, const QShaderFormat &format) QByteArray replaceParameters(const QByteArray &original, const QShaderNode &node, const QShaderFormat &format)
{ {
auto result = original; QByteArray result = original;
for (const auto &parameterName : node.parameterNames()) { const QStringList parameterNames = node.parameterNames();
const auto placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8()); for (const QString &parameterName : parameterNames) {
const auto parameter = node.parameter(parameterName); const QByteArray placeholder = QByteArray(QByteArrayLiteral("$") + parameterName.toUtf8());
const QVariant parameter = node.parameter(parameterName);
if (parameter.userType() == qMetaTypeId<QShaderLanguage::StorageQualifier>()) { if (parameter.userType() == qMetaTypeId<QShaderLanguage::StorageQualifier>()) {
const auto qualifier = parameter.value<QShaderLanguage::StorageQualifier>(); const QShaderLanguage::StorageQualifier qualifier = parameter.value<QShaderLanguage::StorageQualifier>();
const auto value = toGlsl(qualifier, format); const QByteArray value = toGlsl(qualifier, format);
result.replace(placeholder, value); result.replace(placeholder, value);
} else if (parameter.userType() == qMetaTypeId<QShaderLanguage::VariableType>()) { } else if (parameter.userType() == qMetaTypeId<QShaderLanguage::VariableType>()) {
const auto type = parameter.value<QShaderLanguage::VariableType>(); const QShaderLanguage::VariableType type = parameter.value<QShaderLanguage::VariableType>();
const auto value = toGlsl(type); const QByteArray value = toGlsl(type);
result.replace(placeholder, value); result.replace(placeholder, value);
} else { } else {
const auto value = parameter.toString().toUtf8(); const QByteArray value = parameter.toString().toUtf8();
result.replace(placeholder, value); result.replace(placeholder, value);
} }
} }
@ -288,20 +289,20 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
auto code = QByteArrayList(); auto code = QByteArrayList();
if (format.isValid()) { if (format.isValid()) {
const auto isGLES = format.api() == QShaderFormat::OpenGLES; const bool isGLES = format.api() == QShaderFormat::OpenGLES;
const auto major = format.version().majorVersion(); const int major = format.version().majorVersion();
const auto minor = format.version().minorVersion(); const int minor = format.version().minorVersion();
const auto version = major == 2 && isGLES ? 100 const int version = major == 2 && isGLES ? 100
: major == 3 && isGLES ? 300 : major == 3 && isGLES ? 300
: major == 2 ? 100 + 10 * (minor + 1) : major == 2 ? 100 + 10 * (minor + 1)
: major == 3 && minor <= 2 ? 100 + 10 * (minor + 3) : major == 3 && minor <= 2 ? 100 + 10 * (minor + 3)
: major * 100 + minor * 10; : major * 100 + minor * 10;
const auto profile = isGLES && version > 100 ? QByteArrayLiteral(" es") const QByteArray profile = isGLES && version > 100 ? QByteArrayLiteral(" es")
: version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core") : version >= 150 && format.api() == QShaderFormat::OpenGLCoreProfile ? QByteArrayLiteral(" core")
: version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility") : version >= 150 && format.api() == QShaderFormat::OpenGLCompatibilityProfile ? QByteArrayLiteral(" compatibility")
: QByteArray(); : QByteArray();
code << (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile); code << (QByteArrayLiteral("#version ") + QByteArray::number(version) + profile);
code << QByteArray(); code << QByteArray();
@ -313,9 +314,11 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
[enabledLayers] (const QString &s) { return enabledLayers.contains(s); }); [enabledLayers] (const QString &s) { return enabledLayers.contains(s); });
}; };
for (const auto &node : graph.nodes()) { const QVector<QShaderNode> nodes = graph.nodes();
for (const QShaderNode &node : nodes) {
if (intersectsEnabledLayers(node.layers())) { if (intersectsEnabledLayers(node.layers())) {
for (const auto &snippet : node.rule(format).headerSnippets) { const QByteArrayList headerSnippets = node.rule(format).headerSnippets;
for (const QByteArray &snippet : headerSnippets) {
code << replaceParameters(snippet, node, format); code << replaceParameters(snippet, node, format);
} }
} }
@ -325,17 +328,18 @@ QByteArray QShaderGenerator::createShaderCode(const QStringList &enabledLayers)
code << QByteArrayLiteral("void main()"); code << QByteArrayLiteral("void main()");
code << QByteArrayLiteral("{"); code << QByteArrayLiteral("{");
for (const auto &statement : graph.createStatements(enabledLayers)) { for (const QShaderGraph::Statement &statement : graph.createStatements(enabledLayers)) {
const auto node = statement.node; const QShaderNode node = statement.node;
auto line = node.rule(format).substitution; QByteArray line = node.rule(format).substitution;
for (const auto &port : node.ports()) { const QVector<QShaderNodePort> ports = node.ports();
const auto portName = port.name; for (const QShaderNodePort &port : ports) {
const auto portDirection = port.direction; const QString portName = port.name;
const auto isInput = port.direction == QShaderNodePort::Input; const QShaderNodePort::Direction portDirection = port.direction;
const bool isInput = port.direction == QShaderNodePort::Input;
const auto portIndex = statement.portIndex(portDirection, portName); const int portIndex = statement.portIndex(portDirection, portName);
const auto variableIndex = isInput ? statement.inputs.at(portIndex) const int variableIndex = isInput ? statement.inputs.at(portIndex)
: statement.outputs.at(portIndex); : statement.outputs.at(portIndex);
if (variableIndex < 0) if (variableIndex < 0)
continue; continue;

View File

@ -82,8 +82,8 @@ namespace
auto statement = QShaderGraph::Statement(); auto statement = QShaderGraph::Statement();
statement.node = node; statement.node = node;
const auto ports = node.ports(); const QVector<QShaderNodePort> ports = node.ports();
for (const auto &port : ports) { for (const QShaderNodePort &port : ports) {
if (port.direction == QShaderNodePort::Input) { if (port.direction == QShaderNodePort::Input) {
statement.inputs.append(-1); statement.inputs.append(-1);
} else { } else {
@ -99,19 +99,19 @@ namespace
const QUuid &uuid) const QUuid &uuid)
{ {
auto targetStatement = idHash.value(uuid); auto targetStatement = idHash.value(uuid);
for (const auto &edge : edges) { for (const QShaderGraph::Edge &edge : edges) {
if (edge.targetNodeUuid != uuid) if (edge.targetNodeUuid != uuid)
continue; continue;
const auto sourceStatement = idHash.value(edge.sourceNodeUuid); const QShaderGraph::Statement sourceStatement = idHash.value(edge.sourceNodeUuid);
const auto sourcePortIndex = sourceStatement.portIndex(QShaderNodePort::Output, edge.sourcePortName); const int sourcePortIndex = sourceStatement.portIndex(QShaderNodePort::Output, edge.sourcePortName);
const auto targetPortIndex = targetStatement.portIndex(QShaderNodePort::Input, edge.targetPortName); const int targetPortIndex = targetStatement.portIndex(QShaderNodePort::Input, edge.targetPortName);
if (sourcePortIndex < 0 || targetPortIndex < 0) if (sourcePortIndex < 0 || targetPortIndex < 0)
continue; continue;
const auto &sourceOutputs = sourceStatement.outputs; const QVector<int> sourceOutputs = sourceStatement.outputs;
auto &targetInputs = targetStatement.inputs; QVector<int> &targetInputs = targetStatement.inputs;
targetInputs[targetPortIndex] = sourceOutputs[sourcePortIndex]; targetInputs[targetPortIndex] = sourceOutputs[sourcePortIndex];
} }
return targetStatement; return targetStatement;
@ -125,9 +125,9 @@ QUuid QShaderGraph::Statement::uuid() const noexcept
int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept int QShaderGraph::Statement::portIndex(QShaderNodePort::Direction direction, const QString &portName) const noexcept
{ {
const auto ports = node.ports(); const QVector<QShaderNodePort> ports = node.ports();
int index = 0; int index = 0;
for (const auto &port : ports) { for (const QShaderNodePort &port : ports) {
if (port.name == portName && port.direction == direction) if (port.name == portName && port.direction == direction)
return index; return index;
else if (port.direction == direction) else if (port.direction == direction)
@ -180,7 +180,7 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringLis
[enabledLayers] (const QString &s) { return enabledLayers.contains(s); }); [enabledLayers] (const QString &s) { return enabledLayers.contains(s); });
}; };
const auto enabledNodes = [this, intersectsEnabledLayers] { const QVector<QShaderNode> enabledNodes = [this, intersectsEnabledLayers] {
auto res = QVector<QShaderNode>(); auto res = QVector<QShaderNode>();
std::copy_if(m_nodes.cbegin(), m_nodes.cend(), std::copy_if(m_nodes.cbegin(), m_nodes.cend(),
std::back_inserter(res), std::back_inserter(res),
@ -190,7 +190,7 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringLis
return res; return res;
}(); }();
const auto enabledEdges = [this, intersectsEnabledLayers] { const QVector<Edge> enabledEdges = [this, intersectsEnabledLayers] {
auto res = QVector<Edge>(); auto res = QVector<Edge>();
std::copy_if(m_edges.cbegin(), m_edges.cend(), std::copy_if(m_edges.cbegin(), m_edges.cend(),
std::back_inserter(res), std::back_inserter(res),
@ -200,18 +200,18 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringLis
return res; return res;
}(); }();
const auto idHash = [enabledNodes] { const QHash<QUuid, Statement> idHash = [enabledNodes] {
auto nextVarId = 0; auto nextVarId = 0;
auto res = QHash<QUuid, Statement>(); auto res = QHash<QUuid, Statement>();
for (const auto &node : enabledNodes) for (const QShaderNode &node : enabledNodes)
res.insert(node.uuid(), nodeToStatement(node, nextVarId)); res.insert(node.uuid(), nodeToStatement(node, nextVarId));
return res; return res;
}(); }();
auto result = QVector<Statement>(); auto result = QVector<Statement>();
auto currentEdges = enabledEdges; QVector<Edge> currentEdges = enabledEdges;
auto currentUuids = [enabledNodes] { QVector<QUuid> currentUuids = [enabledNodes] {
const auto inputs = copyOutputNodes(enabledNodes); const QVector<QShaderNode> inputs = copyOutputNodes(enabledNodes);
auto res = QVector<QUuid>(); auto res = QVector<QUuid>();
std::transform(inputs.cbegin(), inputs.cend(), std::transform(inputs.cbegin(), inputs.cend(),
std::back_inserter(res), std::back_inserter(res),
@ -226,14 +226,14 @@ QVector<QShaderGraph::Statement> QShaderGraph::createStatements(const QStringLis
// because we want to track the dependencies from the output nodes and not the // because we want to track the dependencies from the output nodes and not the
// input nodes // input nodes
while (!currentUuids.isEmpty()) { while (!currentUuids.isEmpty()) {
const auto uuid = currentUuids.takeFirst(); const QUuid uuid = currentUuids.takeFirst();
result.append(completeStatement(idHash, enabledEdges, uuid)); result.append(completeStatement(idHash, enabledEdges, uuid));
const auto outgoing = outgoingEdges(currentEdges, uuid); const QVector<QShaderGraph::Edge> outgoing = outgoingEdges(currentEdges, uuid);
for (const auto &outgoingEdge : outgoing) { for (const QShaderGraph::Edge &outgoingEdge : outgoing) {
currentEdges.removeAll(outgoingEdge); currentEdges.removeAll(outgoingEdge);
const QUuid nextUuid = outgoingEdge.sourceNodeUuid; const QUuid nextUuid = outgoingEdge.sourceNodeUuid;
const auto incoming = incomingEdges(currentEdges, nextUuid); const QVector<QShaderGraph::Edge> incoming = incomingEdges(currentEdges, nextUuid);
if (incoming.isEmpty()) { if (incoming.isEmpty()) {
currentUuids.append(nextUuid); currentUuids.append(nextUuid);
} }

View File

@ -99,7 +99,7 @@ void QShaderGraphLoader::load()
return; return;
auto error = QJsonParseError(); auto error = QJsonParseError();
const auto document = QJsonDocument::fromJson(m_device->readAll(), &error); const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error);
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
qWarning() << "Invalid JSON document:" << error.errorString(); qWarning() << "Invalid JSON document:" << error.errorString();
@ -113,16 +113,16 @@ void QShaderGraphLoader::load()
return; return;
} }
const auto root = document.object(); const QJsonObject root = document.object();
const auto nodesValue = root.value(QStringLiteral("nodes")); const QJsonValue nodesValue = root.value(QStringLiteral("nodes"));
if (!nodesValue.isArray()) { if (!nodesValue.isArray()) {
qWarning() << "Invalid nodes property, should be an array"; qWarning() << "Invalid nodes property, should be an array";
m_status = Error; m_status = Error;
return; return;
} }
const auto edgesValue = root.value(QStringLiteral("edges")); const QJsonValue edgesValue = root.value(QStringLiteral("edges"));
if (!edgesValue.isArray()) { if (!edgesValue.isArray()) {
qWarning() << "Invalid edges property, should be an array"; qWarning() << "Invalid edges property, should be an array";
m_status = Error; m_status = Error;
@ -131,7 +131,7 @@ void QShaderGraphLoader::load()
bool hasError = false; bool hasError = false;
const auto prototypesValue = root.value(QStringLiteral("prototypes")); const QJsonValue prototypesValue = root.value(QStringLiteral("prototypes"));
if (!prototypesValue.isUndefined()) { if (!prototypesValue.isUndefined()) {
if (prototypesValue.isObject()) { if (prototypesValue.isObject()) {
QShaderNodesLoader loader; QShaderNodesLoader loader;
@ -144,60 +144,60 @@ void QShaderGraphLoader::load()
} }
} }
const auto nodes = nodesValue.toArray(); const QJsonArray nodes = nodesValue.toArray();
for (const auto &nodeValue : nodes) { for (const QJsonValue &nodeValue : nodes) {
if (!nodeValue.isObject()) { if (!nodeValue.isObject()) {
qWarning() << "Invalid node found"; qWarning() << "Invalid node found";
hasError = true; hasError = true;
continue; continue;
} }
const auto nodeObject = nodeValue.toObject(); const QJsonObject nodeObject = nodeValue.toObject();
const auto uuidString = nodeObject.value(QStringLiteral("uuid")).toString(); const QString uuidString = nodeObject.value(QStringLiteral("uuid")).toString();
const auto uuid = QUuid(uuidString); const QUuid uuid = QUuid(uuidString);
if (uuid.isNull()) { if (uuid.isNull()) {
qWarning() << "Invalid UUID found in node:" << uuidString; qWarning() << "Invalid UUID found in node:" << uuidString;
hasError = true; hasError = true;
continue; continue;
} }
const auto type = nodeObject.value(QStringLiteral("type")).toString(); const QString type = nodeObject.value(QStringLiteral("type")).toString();
if (!m_prototypes.contains(type)) { if (!m_prototypes.contains(type)) {
qWarning() << "Unsupported node type found:" << type; qWarning() << "Unsupported node type found:" << type;
hasError = true; hasError = true;
continue; continue;
} }
const auto layersArray = nodeObject.value(QStringLiteral("layers")).toArray(); const QJsonArray layersArray = nodeObject.value(QStringLiteral("layers")).toArray();
auto layers = QStringList(); auto layers = QStringList();
for (const auto &layerValue : layersArray) { for (const QJsonValue &layerValue : layersArray) {
layers.append(layerValue.toString()); layers.append(layerValue.toString());
} }
auto node = m_prototypes.value(type); QShaderNode node = m_prototypes.value(type);
node.setUuid(uuid); node.setUuid(uuid);
node.setLayers(layers); node.setLayers(layers);
const auto parametersValue = nodeObject.value(QStringLiteral("parameters")); const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
if (parametersValue.isObject()) { if (parametersValue.isObject()) {
const auto parametersObject = parametersValue.toObject(); const QJsonObject parametersObject = parametersValue.toObject();
for (const auto &parameterName : parametersObject.keys()) { for (const QString &parameterName : parametersObject.keys()) {
const auto parameterValue = parametersObject.value(parameterName); const QJsonValue parameterValue = parametersObject.value(parameterName);
if (parameterValue.isObject()) { if (parameterValue.isObject()) {
const auto parameterObject = parameterValue.toObject(); const QJsonObject parameterObject = parameterValue.toObject();
const auto type = parameterObject.value(QStringLiteral("type")).toString(); const QString type = parameterObject.value(QStringLiteral("type")).toString();
const auto typeId = QMetaType::type(type.toUtf8()); const int typeId = QMetaType::type(type.toUtf8());
const auto value = parameterObject.value(QStringLiteral("value")).toString(); const QString value = parameterObject.value(QStringLiteral("value")).toString();
auto variant = QVariant(value); auto variant = QVariant(value);
if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) { if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) {
const auto metaObject = QMetaType::metaObjectForType(typeId); const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId);
const auto className = metaObject->className(); const char *className = metaObject->className();
const auto enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8(); const QByteArray enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8();
const auto metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
const auto enumValue = metaEnum.keyToValue(value.toUtf8()); const int enumValue = metaEnum.keyToValue(value.toUtf8());
variant = QVariant(enumValue); variant = QVariant(enumValue);
variant.convert(typeId); variant.convert(typeId);
} else { } else {
@ -213,39 +213,39 @@ void QShaderGraphLoader::load()
m_graph.addNode(node); m_graph.addNode(node);
} }
const auto edges = edgesValue.toArray(); const QJsonArray edges = edgesValue.toArray();
for (const auto &edgeValue : edges) { for (const QJsonValue &edgeValue : edges) {
if (!edgeValue.isObject()) { if (!edgeValue.isObject()) {
qWarning() << "Invalid edge found"; qWarning() << "Invalid edge found";
hasError = true; hasError = true;
continue; continue;
} }
const auto edgeObject = edgeValue.toObject(); const QJsonObject edgeObject = edgeValue.toObject();
const auto sourceUuidString = edgeObject.value(QStringLiteral("sourceUuid")).toString(); const QString sourceUuidString = edgeObject.value(QStringLiteral("sourceUuid")).toString();
const auto sourceUuid = QUuid(sourceUuidString); const QUuid sourceUuid = QUuid(sourceUuidString);
if (sourceUuid.isNull()) { if (sourceUuid.isNull()) {
qWarning() << "Invalid source UUID found in edge:" << sourceUuidString; qWarning() << "Invalid source UUID found in edge:" << sourceUuidString;
hasError = true; hasError = true;
continue; continue;
} }
const auto sourcePort = edgeObject.value(QStringLiteral("sourcePort")).toString(); const QString sourcePort = edgeObject.value(QStringLiteral("sourcePort")).toString();
const auto targetUuidString = edgeObject.value(QStringLiteral("targetUuid")).toString(); const QString targetUuidString = edgeObject.value(QStringLiteral("targetUuid")).toString();
const auto targetUuid = QUuid(targetUuidString); const QUuid targetUuid = QUuid(targetUuidString);
if (targetUuid.isNull()) { if (targetUuid.isNull()) {
qWarning() << "Invalid target UUID found in edge:" << targetUuidString; qWarning() << "Invalid target UUID found in edge:" << targetUuidString;
hasError = true; hasError = true;
continue; continue;
} }
const auto targetPort = edgeObject.value(QStringLiteral("targetPort")).toString(); const QString targetPort = edgeObject.value(QStringLiteral("targetPort")).toString();
const auto layersArray = edgeObject.value(QStringLiteral("layers")).toArray(); const QJsonArray layersArray = edgeObject.value(QStringLiteral("layers")).toArray();
auto layers = QStringList(); auto layers = QStringList();
for (const auto &layerValue : layersArray) { for (const QJsonValue &layerValue : layersArray) {
layers.append(layerValue.toString()); layers.append(layerValue.toString());
} }

View File

@ -84,7 +84,7 @@ void QShaderNodesLoader::load()
return; return;
auto error = QJsonParseError(); auto error = QJsonParseError();
const auto document = QJsonDocument::fromJson(m_device->readAll(), &error); const QJsonDocument document = QJsonDocument::fromJson(m_device->readAll(), &error);
if (error.error != QJsonParseError::NoError) { if (error.error != QJsonParseError::NoError) {
qWarning() << "Invalid JSON document:" << error.errorString(); qWarning() << "Invalid JSON document:" << error.errorString();
@ -98,7 +98,7 @@ void QShaderNodesLoader::load()
return; return;
} }
const auto root = document.object(); const QJsonObject root = document.object();
load(root); load(root);
} }
@ -106,22 +106,22 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
{ {
bool hasError = false; bool hasError = false;
for (const auto &property : prototypesObject.keys()) { for (const QString &property : prototypesObject.keys()) {
const auto nodeValue = prototypesObject.value(property); const QJsonValue nodeValue = prototypesObject.value(property);
if (!nodeValue.isObject()) { if (!nodeValue.isObject()) {
qWarning() << "Invalid node found"; qWarning() << "Invalid node found";
hasError = true; hasError = true;
break; break;
} }
const auto nodeObject = nodeValue.toObject(); const QJsonObject nodeObject = nodeValue.toObject();
auto node = QShaderNode(); auto node = QShaderNode();
const auto inputsValue = nodeObject.value(QStringLiteral("inputs")); const QJsonValue inputsValue = nodeObject.value(QStringLiteral("inputs"));
if (inputsValue.isArray()) { if (inputsValue.isArray()) {
const auto inputsArray = inputsValue.toArray(); const QJsonArray inputsArray = inputsValue.toArray();
for (const auto &inputValue : inputsArray) { for (const QJsonValue &inputValue : inputsArray) {
if (!inputValue.isString()) { if (!inputValue.isString()) {
qWarning() << "Non-string value in inputs"; qWarning() << "Non-string value in inputs";
hasError = true; hasError = true;
@ -135,10 +135,10 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
} }
} }
const auto outputsValue = nodeObject.value(QStringLiteral("outputs")); const QJsonValue outputsValue = nodeObject.value(QStringLiteral("outputs"));
if (outputsValue.isArray()) { if (outputsValue.isArray()) {
const auto outputsArray = outputsValue.toArray(); const QJsonArray outputsArray = outputsValue.toArray();
for (const auto &outputValue : outputsArray) { for (const QJsonValue &outputValue : outputsArray) {
if (!outputValue.isString()) { if (!outputValue.isString()) {
qWarning() << "Non-string value in outputs"; qWarning() << "Non-string value in outputs";
hasError = true; hasError = true;
@ -152,25 +152,25 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
} }
} }
const auto parametersValue = nodeObject.value(QStringLiteral("parameters")); const QJsonValue parametersValue = nodeObject.value(QStringLiteral("parameters"));
if (parametersValue.isObject()) { if (parametersValue.isObject()) {
const auto parametersObject = parametersValue.toObject(); const QJsonObject parametersObject = parametersValue.toObject();
for (const auto &parameterName : parametersObject.keys()) { for (const QString &parameterName : parametersObject.keys()) {
const auto parameterValue = parametersObject.value(parameterName); const QJsonValue parameterValue = parametersObject.value(parameterName);
if (parameterValue.isObject()) { if (parameterValue.isObject()) {
const auto parameterObject = parameterValue.toObject(); const QJsonObject parameterObject = parameterValue.toObject();
const auto type = parameterObject.value(QStringLiteral("type")).toString(); const QString type = parameterObject.value(QStringLiteral("type")).toString();
const auto typeId = QMetaType::type(type.toUtf8()); const int typeId = QMetaType::type(type.toUtf8());
const auto value = parameterObject.value(QStringLiteral("value")).toString(); const QString value = parameterObject.value(QStringLiteral("value")).toString();
auto variant = QVariant(value); auto variant = QVariant(value);
if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) { if (QMetaType::typeFlags(typeId) & QMetaType::IsEnumeration) {
const auto metaObject = QMetaType::metaObjectForType(typeId); const QMetaObject *metaObject = QMetaType::metaObjectForType(typeId);
const auto className = metaObject->className(); const char *className = metaObject->className();
const auto enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8(); const QByteArray enumName = type.mid(static_cast<int>(qstrlen(className)) + 2).toUtf8();
const auto metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName)); const QMetaEnum metaEnum = metaObject->enumerator(metaObject->indexOfEnumerator(enumName));
const auto enumValue = metaEnum.keyToValue(value.toUtf8()); const int enumValue = metaEnum.keyToValue(value.toUtf8());
variant = QVariant(enumValue); variant = QVariant(enumValue);
variant.convert(typeId); variant.convert(typeId);
} else { } else {
@ -183,36 +183,36 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
} }
} }
const auto rulesValue = nodeObject.value(QStringLiteral("rules")); const QJsonValue rulesValue = nodeObject.value(QStringLiteral("rules"));
if (rulesValue.isArray()) { if (rulesValue.isArray()) {
const auto rulesArray = rulesValue.toArray(); const QJsonArray rulesArray = rulesValue.toArray();
for (const auto &ruleValue : rulesArray) { for (const QJsonValue &ruleValue : rulesArray) {
if (!ruleValue.isObject()) { if (!ruleValue.isObject()) {
qWarning() << "Rules should be objects"; qWarning() << "Rules should be objects";
hasError = true; hasError = true;
break; break;
} }
const auto ruleObject = ruleValue.toObject(); const QJsonObject ruleObject = ruleValue.toObject();
const auto formatValue = ruleObject.value(QStringLiteral("format")); const QJsonValue formatValue = ruleObject.value(QStringLiteral("format"));
if (!formatValue.isObject()) { if (!formatValue.isObject()) {
qWarning() << "Format is mandatory in rules and should be an object"; qWarning() << "Format is mandatory in rules and should be an object";
hasError = true; hasError = true;
break; break;
} }
const auto formatObject = formatValue.toObject(); const QJsonObject formatObject = formatValue.toObject();
auto format = QShaderFormat(); auto format = QShaderFormat();
const auto apiValue = formatObject.value(QStringLiteral("api")); const QJsonValue apiValue = formatObject.value(QStringLiteral("api"));
if (!apiValue.isString()) { if (!apiValue.isString()) {
qWarning() << "Format API must be a string"; qWarning() << "Format API must be a string";
hasError = true; hasError = true;
break; break;
} }
const auto api = apiValue.toString(); const QString api = apiValue.toString();
format.setApi(api == QStringLiteral("OpenGLES") ? QShaderFormat::OpenGLES format.setApi(api == QStringLiteral("OpenGLES") ? QShaderFormat::OpenGLES
: api == QStringLiteral("OpenGLNoProfile") ? QShaderFormat::OpenGLNoProfile : api == QStringLiteral("OpenGLNoProfile") ? QShaderFormat::OpenGLNoProfile
: api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile : api == QStringLiteral("OpenGLCoreProfile") ? QShaderFormat::OpenGLCoreProfile
@ -224,8 +224,8 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
break; break;
} }
const auto majorValue = formatObject.value(QStringLiteral("major")); const QJsonValue majorValue = formatObject.value(QStringLiteral("major"));
const auto minorValue = formatObject.value(QStringLiteral("minor")); const QJsonValue minorValue = formatObject.value(QStringLiteral("minor"));
if (!majorValue.isDouble() || !minorValue.isDouble()) { if (!majorValue.isDouble() || !minorValue.isDouble()) {
qWarning() << "Format major and minor version must be values"; qWarning() << "Format major and minor version must be values";
hasError = true; hasError = true;
@ -233,28 +233,28 @@ void QShaderNodesLoader::load(const QJsonObject &prototypesObject)
} }
format.setVersion(QVersionNumber(majorValue.toInt(), minorValue.toInt())); format.setVersion(QVersionNumber(majorValue.toInt(), minorValue.toInt()));
const auto extensionsValue = formatObject.value(QStringLiteral("extensions")); const QJsonValue extensionsValue = formatObject.value(QStringLiteral("extensions"));
const auto extensionsArray = extensionsValue.toArray(); const QJsonArray extensionsArray = extensionsValue.toArray();
auto extensions = QStringList(); auto extensions = QStringList();
std::transform(extensionsArray.constBegin(), extensionsArray.constEnd(), std::transform(extensionsArray.constBegin(), extensionsArray.constEnd(),
std::back_inserter(extensions), std::back_inserter(extensions),
[] (const QJsonValue &extensionValue) { return extensionValue.toString(); }); [] (const QJsonValue &extensionValue) { return extensionValue.toString(); });
format.setExtensions(extensions); format.setExtensions(extensions);
const auto vendor = formatObject.value(QStringLiteral("vendor")).toString(); const QString vendor = formatObject.value(QStringLiteral("vendor")).toString();
format.setVendor(vendor); format.setVendor(vendor);
const auto substitutionValue = ruleObject.value(QStringLiteral("substitution")); const QJsonValue substitutionValue = ruleObject.value(QStringLiteral("substitution"));
if (!substitutionValue.isString()) { if (!substitutionValue.isString()) {
qWarning() << "Substitution needs to be a string"; qWarning() << "Substitution needs to be a string";
hasError = true; hasError = true;
break; break;
} }
const auto substitution = substitutionValue.toString().toUtf8(); const QByteArray substitution = substitutionValue.toString().toUtf8();
const auto snippetsValue = ruleObject.value(QStringLiteral("headerSnippets")); const QJsonValue snippetsValue = ruleObject.value(QStringLiteral("headerSnippets"));
const auto snippetsArray = snippetsValue.toArray(); const QJsonArray snippetsArray = snippetsValue.toArray();
auto snippets = QByteArrayList(); auto snippets = QByteArrayList();
std::transform(snippetsArray.constBegin(), snippetsArray.constEnd(), std::transform(snippetsArray.constBegin(), snippetsArray.constEnd(),
std::back_inserter(snippets), std::back_inserter(snippets),

View File

@ -66,6 +66,11 @@ void QEglFSVivWaylandIntegration::platformInit()
mScreenSize.setWidth(width); mScreenSize.setWidth(width);
} }
void QEglFSVivWaylandIntegration::platformDestroy()
{
wl_display_destroy(mWaylandDisplay);
}
QSize QEglFSVivWaylandIntegration::screenSize() const QSize QEglFSVivWaylandIntegration::screenSize() const
{ {
return mScreenSize; return mScreenSize;

View File

@ -49,6 +49,7 @@ class QEglFSVivWaylandIntegration : public QEglFSDeviceIntegration
{ {
public: public:
void platformInit() override; void platformInit() override;
void platformDestroy() override;
QSize screenSize() const override; QSize screenSize() const override;
EGLNativeWindowType createNativeWindow(QPlatformWindow *window, const QSize &size, const QSurfaceFormat &format) override; EGLNativeWindowType createNativeWindow(QPlatformWindow *window, const QSize &size, const QSurfaceFormat &format) override;
void destroyNativeWindow(EGLNativeWindowType window) override; void destroyNativeWindow(EGLNativeWindowType window) override;

View File

@ -1453,26 +1453,41 @@ static QString createTemporaryItemCopy(QWindowsShellItem &qItem, QString *errorM
return result; return result;
} }
static QUrl itemToDialogUrl(QWindowsShellItem &qItem, QString *errorMessage)
{
QUrl url = qItem.url();
if (url.isLocalFile() || url.scheme().startsWith(QLatin1String("http")))
return url;
const QString path = qItem.path();
if (path.isEmpty() && !qItem.isDir() && qItem.canStream()) {
const QString temporaryCopy = createTemporaryItemCopy(qItem, errorMessage);
if (temporaryCopy.isEmpty()) {
QDebug(errorMessage).noquote() << "Unable to create a local copy of"
<< qItem << ": " << errorMessage;
return QUrl();
}
return QUrl::fromLocalFile(temporaryCopy);
}
if (!url.isValid())
QDebug(errorMessage).noquote() << "Invalid URL obtained from" << qItem;
return url;
}
QList<QUrl> QWindowsNativeOpenFileDialog::dialogResult() const QList<QUrl> QWindowsNativeOpenFileDialog::dialogResult() const
{ {
QList<QUrl> result; QList<QUrl> result;
IShellItemArray *items = nullptr; IShellItemArray *items = nullptr;
if (SUCCEEDED(openFileDialog()->GetResults(&items)) && items) { if (SUCCEEDED(openFileDialog()->GetResults(&items)) && items) {
QString errorMessage;
for (IShellItem *item : QWindowsShellItem::itemsFromItemArray(items)) { for (IShellItem *item : QWindowsShellItem::itemsFromItemArray(items)) {
QWindowsShellItem qItem(item); QWindowsShellItem qItem(item);
const QString path = qItem.path(); const QUrl url = itemToDialogUrl(qItem, &errorMessage);
if (path.isEmpty() && !qItem.isDir()) { if (!url.isValid()) {
QString errorMessage; qWarning("%s", qPrintable(errorMessage));
const QString temporaryCopy = createTemporaryItemCopy(qItem, &errorMessage); result.clear();
if (temporaryCopy.isEmpty()) { break;
qWarning().noquote() << "Unable to create a local copy of" << qItem
<< ": " << errorMessage;
} else {
result.append(QUrl::fromLocalFile(temporaryCopy));
}
} else {
result.append(qItem.url());
} }
result.append(url);
} }
} }
return result; return result;

View File

@ -1105,16 +1105,16 @@ void QWin32PrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &
if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice.supportedDuplexModes().contains(mode)) if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice.supportedDuplexModes().contains(mode))
break; break;
switch (mode) { switch (mode) {
case QPrinter::DuplexNone: case QPrint::DuplexNone:
d->devMode->dmDuplex = DMDUP_SIMPLEX; d->devMode->dmDuplex = DMDUP_SIMPLEX;
break; break;
case QPrinter::DuplexAuto: case QPrint::DuplexAuto:
d->devMode->dmDuplex = d->m_pageLayout.orientation() == QPageLayout::Landscape ? DMDUP_HORIZONTAL : DMDUP_VERTICAL; d->devMode->dmDuplex = d->m_pageLayout.orientation() == QPageLayout::Landscape ? DMDUP_HORIZONTAL : DMDUP_VERTICAL;
break; break;
case QPrinter::DuplexLongSide: case QPrint::DuplexLongSide:
d->devMode->dmDuplex = DMDUP_VERTICAL; d->devMode->dmDuplex = DMDUP_VERTICAL;
break; break;
case QPrinter::DuplexShortSide: case QPrint::DuplexShortSide:
d->devMode->dmDuplex = DMDUP_HORIZONTAL; d->devMode->dmDuplex = DMDUP_HORIZONTAL;
break; break;
default: default:

View File

@ -1581,29 +1581,6 @@ void QTableView::paintEvent(QPaintEvent *event)
colp += columnWidth(col) - gridSize; colp += columnWidth(col) - gridSize;
painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom()); painter.drawLine(colp, dirtyArea.top(), colp, dirtyArea.bottom());
} }
//draw the top & left grid lines if the headers are not visible.
//We do update this line when subsequent scroll happen (see scrollContentsBy)
if (horizontalHeader->isHidden() && top == 0) {
const int row = verticalHeader->logicalIndex(top);
if (!verticalHeader->isSectionHidden(row)) {
const int rowY = rowViewportPosition(row) + offset.y();
if (rowY == dirtyArea.top())
painter.drawLine(dirtyArea.left(), rowY, dirtyArea.right(), rowY);
}
}
if (verticalHeader->isHidden() && left == 0) {
const int col = horizontalHeader->logicalIndex(left);
if (!horizontalHeader->isSectionHidden(col)) {
int colX = columnViewportPosition(col) + offset.x();
if (!isLeftToRight())
colX += columnWidth(left) - 1;
if (isLeftToRight() && colX == dirtyArea.left())
painter.drawLine(colX, dirtyArea.top(), colX, dirtyArea.bottom());
if (!isLeftToRight() && colX == dirtyArea.right())
painter.drawLine(colX, dirtyArea.top(), colX, dirtyArea.bottom());
}
}
painter.setPen(old); painter.setPen(old);
} }
} }
@ -1878,7 +1855,7 @@ QModelIndex QTableView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifi
visualColumn = d->nextActiveVisualColumn(visualRow, right, -1, visualColumn = d->nextActiveVisualColumn(visualRow, right, -1,
QTableViewPrivate::SearchDirection::Decreasing); QTableViewPrivate::SearchDirection::Decreasing);
if (modifiers & Qt::ControlModifier) if (modifiers & Qt::ControlModifier)
visualRow = d->nextActiveVisualRow(bottom, current.column(), -1, visualRow = d->nextActiveVisualRow(bottom, visualColumn, -1,
QTableViewPrivate::SearchDirection::Decreasing); QTableViewPrivate::SearchDirection::Decreasing);
break; break;
case MovePageUp: { case MovePageUp: {

View File

@ -46,6 +46,7 @@
#include "qstyleoption.h" #include "qstyleoption.h"
#include "private/qstyle_p.h" #include "private/qstyle_p.h"
#include "private/qguiapplication_p.h" #include "private/qguiapplication_p.h"
#include <qpa/qplatformtheme.h>
#ifndef QT_NO_DEBUG #ifndef QT_NO_DEBUG
#include "qdebug.h" #include "qdebug.h"
#endif #endif
@ -2447,6 +2448,13 @@ void QStyle::setProxy(QStyle *style)
d->proxyStyle = style; d->proxyStyle = style;
} }
//Windows and KDE allow menus to cover the taskbar, while GNOME and macOS don't
bool QStylePrivate::useFullScreenForPopup()
{
auto theme = QGuiApplicationPrivate::platformTheme();
return theme && theme->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool();
}
QT_END_NAMESPACE QT_END_NAMESPACE
#include "moc_qstyle.cpp" #include "moc_qstyle.cpp"

View File

@ -67,6 +67,9 @@ class QStylePrivate: public QObjectPrivate
public: public:
inline QStylePrivate() inline QStylePrivate()
: layoutSpacingIndex(-1), proxyStyle(nullptr) {} : layoutSpacingIndex(-1), proxyStyle(nullptr) {}
static bool useFullScreenForPopup();
mutable int layoutSpacingIndex; mutable int layoutSpacingIndex;
QStyle *proxyStyle; QStyle *proxyStyle;
}; };

View File

@ -80,6 +80,7 @@
#if QT_CONFIG(effects) #if QT_CONFIG(effects)
# include <private/qeffects_p.h> # include <private/qeffects_p.h>
#endif #endif
#include <private/qstyle_p.h>
#ifndef QT_NO_ACCESSIBILITY #ifndef QT_NO_ACCESSIBILITY
#include "qaccessible.h" #include "qaccessible.h"
#endif #endif
@ -261,16 +262,11 @@ void QComboBoxPrivate::_q_modelDestroyed()
model = QAbstractItemModelPrivate::staticEmptyModel(); model = QAbstractItemModelPrivate::staticEmptyModel();
} }
//Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
QRect QComboBoxPrivate::popupGeometry(int screen) const QRect QComboBoxPrivate::popupGeometry(int screen) const
{ {
bool useFullScreenForPopupMenu = false; return QStylePrivate::useFullScreenForPopup()
if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) ? QDesktopWidgetPrivate::screenGeometry(screen)
useFullScreenForPopupMenu = theme->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool(); : QDesktopWidgetPrivate::availableGeometry(screen);
return useFullScreenForPopupMenu ?
QDesktopWidgetPrivate::screenGeometry(screen) :
QDesktopWidgetPrivate::availableGeometry(screen);
} }
bool QComboBoxPrivate::updateHoverControl(const QPoint &pos) bool QComboBoxPrivate::updateHoverControl(const QPoint &pos)

View File

@ -78,6 +78,7 @@
#include <private/qguiapplication_p.h> #include <private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h> #include <qpa/qplatformtheme.h>
#include <private/qdesktopwidget_p.h> #include <private/qdesktopwidget_p.h>
#include <private/qstyle_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
@ -307,29 +308,26 @@ int QMenuPrivate::scrollerHeight() const
return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q)); return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
} }
//Windows and KDE allow menus to cover the taskbar, while GNOME and Mac don't // Windows and KDE allow menus to cover the taskbar, while GNOME and macOS
// don't. Torn-off menus are again different
inline bool QMenuPrivate::useFullScreenForPopup() const
{
return !tornoff && QStylePrivate::useFullScreenForPopup();
}
QRect QMenuPrivate::popupGeometry() const QRect QMenuPrivate::popupGeometry() const
{ {
Q_Q(const QMenu); Q_Q(const QMenu);
if (!tornoff && // Torn-off menus are different return useFullScreenForPopup()
QGuiApplicationPrivate::platformTheme() && ? QDesktopWidgetPrivate::screenGeometry(q)
QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) { : QDesktopWidgetPrivate::availableGeometry(q);
return QDesktopWidgetPrivate::screenGeometry(q);
} else {
return QDesktopWidgetPrivate::availableGeometry(q);
}
} }
//Windows and KDE allow menus to cover the taskbar, while GNOME and Mac don't
QRect QMenuPrivate::popupGeometry(int screen) const QRect QMenuPrivate::popupGeometry(int screen) const
{ {
if (!tornoff && // Torn-off menus are different return useFullScreenForPopup()
QGuiApplicationPrivate::platformTheme() && ? QDesktopWidgetPrivate::screenGeometry(screen)
QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) { : QDesktopWidgetPrivate::availableGeometry(screen);
return QDesktopWidgetPrivate::screenGeometry(screen);
} else {
return QDesktopWidgetPrivate::availableGeometry(screen);
}
} }
QVector<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const QVector<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const

View File

@ -349,6 +349,7 @@ public:
void updateActionRects(const QRect &screen) const; void updateActionRects(const QRect &screen) const;
QRect popupGeometry() const; QRect popupGeometry() const;
QRect popupGeometry(int screen) const; QRect popupGeometry(int screen) const;
bool useFullScreenForPopup() const;
int getLastVisibleAction() const; int getLastVisibleAction() const;
//selection //selection

View File

@ -47,6 +47,7 @@ class tst_QUrl : public QObject
private slots: private slots:
void initTestCase(); void initTestCase();
void cleanupTestCase();
void effectiveTLDs_data(); void effectiveTLDs_data();
void effectiveTLDs(); void effectiveTLDs();
void getSetCheck(); void getSetCheck();
@ -188,6 +189,7 @@ private slots:
private: private:
void testThreadingHelper(); void testThreadingHelper();
const QString m_currentPath = QDir::currentPath();
QTemporaryDir m_tempDir; QTemporaryDir m_tempDir;
}; };
@ -196,6 +198,12 @@ void tst_QUrl::initTestCase()
QVERIFY2(m_tempDir.isValid(), qPrintable(m_tempDir.errorString())); QVERIFY2(m_tempDir.isValid(), qPrintable(m_tempDir.errorString()));
} }
void tst_QUrl::cleanupTestCase()
{
// Restore working directory changed in fromUserInputWithCwd()
QDir::setCurrent(m_currentPath);
}
// Testing get/set functions // Testing get/set functions
void tst_QUrl::getSetCheck() void tst_QUrl::getSetCheck()
{ {

View File

@ -40,6 +40,13 @@ struct MyStruct
int i; int i;
}; };
class MyGadget
{
Q_GADGET
public:
Q_INVOKABLE MyGadget() {}
};
namespace MyNamespace { namespace MyNamespace {
// Used in tst_QMetaObject::checkScope // Used in tst_QMetaObject::checkScope
class MyClass : public QObject class MyClass : public QObject
@ -1208,6 +1215,12 @@ void tst_QMetaObject::invokeMetaConstructor()
QCOMPARE(obj2->parent(), (QObject*)&obj); QCOMPARE(obj2->parent(), (QObject*)&obj);
QVERIFY(qobject_cast<NamespaceWithConstructibleClass::ConstructibleClass*>(obj2) != 0); QVERIFY(qobject_cast<NamespaceWithConstructibleClass::ConstructibleClass*>(obj2) != 0);
} }
// gadget shouldn't return a valid pointer
{
QCOMPARE(MyGadget::staticMetaObject.constructorCount(), 1);
QTest::ignoreMessage(QtWarningMsg, "QMetaObject::newInstance: type MyGadget does not inherit QObject");
QVERIFY(!MyGadget::staticMetaObject.newInstance());
}
} }
void tst_QMetaObject::invokeTypedefTypes() void tst_QMetaObject::invokeTypedefTypes()

View File

@ -76,11 +76,15 @@ void fill_push_header(const HttpHeader &originalRequest, HttpHeader &promisedReq
} }
Http2Server::Http2Server(bool h2c, const Http2::RawSettings &ss, const Http2::RawSettings &cs) Http2Server::Http2Server(H2Type type, const Http2::RawSettings &ss, const Http2::RawSettings &cs)
: serverSettings(ss), : connectionType(type),
expectedClientSettings(cs), serverSettings(ss),
clearTextHTTP2(h2c) expectedClientSettings(cs)
{ {
#if !QT_CONFIG(ssl)
Q_ASSERT(type != H2Type::h2Alpn && type != H2Type::h2Direct);
#endif
responseBody = "<html>\n" responseBody = "<html>\n"
"<head>\n" "<head>\n"
"<title>Sample \"Hello, World\" Application</title>\n" "<title>Sample \"Hello, World\" Application</title>\n"
@ -129,15 +133,15 @@ void Http2Server::redirectOpenStream(quint16 port)
targetPort = port; targetPort = port;
} }
bool Http2Server::isClearText() const
{
return connectionType == H2Type::h2c || connectionType == H2Type::h2cDirect;
}
void Http2Server::startServer() void Http2Server::startServer()
{ {
#ifdef QT_NO_SSL
// Let the test fail with timeout.
if (!clearTextHTTP2)
return;
#endif
if (listen()) { if (listen()) {
if (clearTextHTTP2) if (isClearText())
authority = QStringLiteral("127.0.0.1:%1").arg(serverPort()).toLatin1(); authority = QStringLiteral("127.0.0.1:%1").arg(serverPort()).toLatin1();
emit serverStarted(serverPort()); emit serverStarted(serverPort());
} }
@ -146,7 +150,7 @@ void Http2Server::startServer()
bool Http2Server::sendProtocolSwitchReply() bool Http2Server::sendProtocolSwitchReply()
{ {
Q_ASSERT(socket); Q_ASSERT(socket);
Q_ASSERT(clearTextHTTP2 && upgradeProtocol); Q_ASSERT(connectionType == H2Type::h2c);
// The first and the last HTTP/1.1 response we send: // The first and the last HTTP/1.1 response we send:
const char response[] = "HTTP/1.1 101 Switching Protocols\r\n" const char response[] = "HTTP/1.1 101 Switching Protocols\r\n"
"Connection: Upgrade\r\n" "Connection: Upgrade\r\n"
@ -262,25 +266,28 @@ void Http2Server::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
void Http2Server::incomingConnection(qintptr socketDescriptor) void Http2Server::incomingConnection(qintptr socketDescriptor)
{ {
if (clearTextHTTP2) { if (isClearText()) {
socket.reset(new QTcpSocket); socket.reset(new QTcpSocket);
const bool set = socket->setSocketDescriptor(socketDescriptor); const bool set = socket->setSocketDescriptor(socketDescriptor);
Q_ASSERT(set); Q_ASSERT(set);
// Stop listening: // Stop listening:
close(); close();
upgradeProtocol = true; upgradeProtocol = connectionType == H2Type::h2c;
QMetaObject::invokeMethod(this, "connectionEstablished", QMetaObject::invokeMethod(this, "connectionEstablished",
Qt::QueuedConnection); Qt::QueuedConnection);
} else { } else {
#ifndef QT_NO_SSL #if QT_CONFIG(ssl)
socket.reset(new QSslSocket); socket.reset(new QSslSocket);
QSslSocket *sslSocket = static_cast<QSslSocket *>(socket.data()); QSslSocket *sslSocket = static_cast<QSslSocket *>(socket.data());
// Add HTTP2 as supported protocol:
auto conf = QSslConfiguration::defaultConfiguration(); if (connectionType == H2Type::h2Alpn) {
auto protos = conf.allowedNextProtocols(); // Add HTTP2 as supported protocol:
protos.prepend(QSslConfiguration::ALPNProtocolHTTP2); auto conf = QSslConfiguration::defaultConfiguration();
conf.setAllowedNextProtocols(protos); auto protos = conf.allowedNextProtocols();
sslSocket->setSslConfiguration(conf); protos.prepend(QSslConfiguration::ALPNProtocolHTTP2);
conf.setAllowedNextProtocols(protos);
sslSocket->setSslConfiguration(conf);
}
// SSL-related setup ... // SSL-related setup ...
sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone); sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
sslSocket->setProtocol(QSsl::TlsV1_2OrLater); sslSocket->setProtocol(QSsl::TlsV1_2OrLater);
@ -299,7 +306,7 @@ void Http2Server::incomingConnection(qintptr socketDescriptor)
connect(sslSocket, SIGNAL(encrypted()), this, SLOT(connectionEstablished())); connect(sslSocket, SIGNAL(encrypted()), this, SLOT(connectionEstablished()));
sslSocket->startServerEncryption(); sslSocket->startServerEncryption();
#else #else
Q_UNREACHABLE(); Q_ASSERT(0);
#endif #endif
} }
} }
@ -377,7 +384,7 @@ void Http2Server::connectionEstablished()
{ {
using namespace Http2; using namespace Http2;
if (testingGOAWAY && !clearTextHTTP2) if (testingGOAWAY && !isClearText())
return triggerGOAWAYEmulation(); return triggerGOAWAYEmulation();
// For clearTextHTTP2 we first have to respond with 'protocol switch' // For clearTextHTTP2 we first have to respond with 'protocol switch'
@ -392,7 +399,7 @@ void Http2Server::connectionEstablished()
waitingClientSettings = false; waitingClientSettings = false;
settingsSent = false; settingsSent = false;
if (clearTextHTTP2) { if (connectionType == H2Type::h2c) {
requestLine.clear(); requestLine.clear();
// Now we have to handle HTTP/1.1 request. We use Get/Post in our test, // Now we have to handle HTTP/1.1 request. We use Get/Post in our test,
// so set requestType to something unsupported: // so set requestType to something unsupported:
@ -818,7 +825,7 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
Q_ASSERT(targetPort); Q_ASSERT(targetPort);
header.push_back({":status", "308"}); header.push_back({":status", "308"});
const QString url("%1://localhost:%2/"); const QString url("%1://localhost:%2/");
header.push_back({"location", url.arg(clearTextHTTP2 ? QStringLiteral("http") : QStringLiteral("https"), header.push_back({"location", url.arg(isClearText() ? QStringLiteral("http") : QStringLiteral("https"),
QString::number(targetPort)).toLatin1()}); QString::number(targetPort)).toLatin1()});
} else { } else {

View File

@ -62,21 +62,32 @@ public:
Q_DECLARE_PRIVATE(Http11Reply) Q_DECLARE_PRIVATE(Http11Reply)
}; };
enum class H2Type {
h2Alpn, // Secure connection, ALPN to negotiate h2.
h2c, // Clear text with protocol upgrade.
h2Direct, // Secure connection, ALPN not supported.
h2cDirect, // Clear text direct
};
class Http2Server : public QTcpServer class Http2Server : public QTcpServer
{ {
Q_OBJECT Q_OBJECT
public: public:
Http2Server(bool clearText, const Http2::RawSettings &serverSettings,
Http2Server(H2Type type, const Http2::RawSettings &serverSettings,
const Http2::RawSettings &clientSettings); const Http2::RawSettings &clientSettings);
~Http2Server(); ~Http2Server();
// To be called before server started: // To be called before server started:
void enablePushPromise(bool enabled, const QByteArray &path = QByteArray()); void enablePushPromise(bool enabled, const QByteArray &path = QByteArray());
void setResponseBody(const QByteArray &body); void setResponseBody(const QByteArray &body);
void emulateGOAWAY(int timeout); void emulateGOAWAY(int timeout);
void redirectOpenStream(quint16 targetPort); void redirectOpenStream(quint16 targetPort);
bool isClearText() const;
// Invokables, since we can call them from the main thread, // Invokables, since we can call them from the main thread,
// but server (can) work on its own thread. // but server (can) work on its own thread.
Q_INVOKABLE void startServer(); Q_INVOKABLE void startServer();
@ -129,6 +140,7 @@ private:
QScopedPointer<QAbstractSocket> socket; QScopedPointer<QAbstractSocket> socket;
H2Type connectionType = H2Type::h2Alpn;
// Connection preface: // Connection preface:
bool waitingClientPreface = false; bool waitingClientPreface = false;
bool waitingClientSettings = false; bool waitingClientSettings = false;
@ -170,7 +182,6 @@ private:
quint32 streamRecvWindowSize = Http2::defaultSessionWindowSize; quint32 streamRecvWindowSize = Http2::defaultSessionWindowSize;
QByteArray responseBody; QByteArray responseBody;
bool clearTextHTTP2 = false;
bool pushPromiseEnabled = false; bool pushPromiseEnabled = false;
quint32 lastPromisedStream = 0; quint32 lastPromisedStream = 0;
QByteArray pushPath; QByteArray pushPath;

View File

@ -60,6 +60,9 @@ const bool clearTextHTTP2 = false;
const bool clearTextHTTP2 = true; const bool clearTextHTTP2 = true;
#endif #endif
Q_DECLARE_METATYPE(H2Type)
Q_DECLARE_METATYPE(QNetworkRequest::Attribute)
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
class tst_Http2 : public QObject class tst_Http2 : public QObject
@ -70,6 +73,7 @@ public:
~tst_Http2(); ~tst_Http2();
private slots: private slots:
// Tests: // Tests:
void singleRequest_data();
void singleRequest(); void singleRequest();
void multipleRequests(); void multipleRequests();
void flowControlClientSide(); void flowControlClientSide();
@ -101,13 +105,13 @@ private:
// small payload. // small payload.
void runEventLoop(int ms = 5000); void runEventLoop(int ms = 5000);
void stopEventLoop(); void stopEventLoop();
Http2Server *newServer(const Http2::RawSettings &serverSettings, Http2Server *newServer(const Http2::RawSettings &serverSettings, H2Type connectionType,
const Http2::ProtocolParameters &clientSettings = {}); const Http2::ProtocolParameters &clientSettings = {});
// Send a get or post request, depending on a payload (empty or not). // Send a get or post request, depending on a payload (empty or not).
void sendRequest(int streamNumber, void sendRequest(int streamNumber,
QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority, QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority,
const QByteArray &payload = QByteArray()); const QByteArray &payload = QByteArray());
QUrl requestUrl() const; QUrl requestUrl(H2Type connnectionType) const;
quint16 serverPort = 0; quint16 serverPort = 0;
QThread *workerThread = nullptr; QThread *workerThread = nullptr;
@ -144,6 +148,11 @@ struct ServerDeleter
using ServerPtr = QScopedPointer<Http2Server, ServerDeleter>; using ServerPtr = QScopedPointer<Http2Server, ServerDeleter>;
H2Type defaultConnectionType()
{
return clearTextHTTP2 ? H2Type::h2c : H2Type::h2Alpn;
}
} // unnamed namespace } // unnamed namespace
tst_Http2::tst_Http2() tst_Http2::tst_Http2()
@ -165,25 +174,59 @@ tst_Http2::~tst_Http2()
} }
} }
void tst_Http2::singleRequest_data()
{
QTest::addColumn<QNetworkRequest::Attribute>("h2Attribute");
QTest::addColumn<H2Type>("connectionType");
// 'Clear text' that should always work, either via the protocol upgrade
// or as direct.
QTest::addRow("h2c-upgrade") << QNetworkRequest::HTTP2AllowedAttribute << H2Type::h2c;
QTest::addRow("h2c-direct") << QNetworkRequest::Http2DirectAttribute << H2Type::h2cDirect;
if (!clearTextHTTP2) {
// Qt with TLS where TLS-backend supports ALPN.
QTest::addRow("h2-ALPN") << QNetworkRequest::HTTP2AllowedAttribute << H2Type::h2Alpn;
}
#if QT_CONFIG(ssl)
QTest::addRow("h2-direct") << QNetworkRequest::Http2DirectAttribute << H2Type::h2Direct;
#endif
}
void tst_Http2::singleRequest() void tst_Http2::singleRequest()
{ {
clearHTTP2State(); clearHTTP2State();
#if QT_CONFIG(securetransport)
// Normally on macOS we use plain text only for SecureTransport
// does not support ALPN on the server side. With 'direct encrytped'
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
auto envRollback = qScopeGuard([](){
qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN");
});
#endif
serverPort = 0; serverPort = 0;
nRequests = 1; nRequests = 1;
ServerPtr srv(newServer(defaultServerSettings)); QFETCH(const H2Type, connectionType);
ServerPtr srv(newServer(defaultServerSettings, connectionType));
QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection);
runEventLoop(); runEventLoop();
QVERIFY(serverPort != 0); QVERIFY(serverPort != 0);
auto url = requestUrl(); auto url = requestUrl(connectionType);
url.setPath("/index.html"); url.setPath("/index.html");
QNetworkRequest request(url); QNetworkRequest request(url);
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, QVariant(true)); 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); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
@ -208,7 +251,7 @@ void tst_Http2::multipleRequests()
serverPort = 0; serverPort = 0;
nRequests = 10; nRequests = 10;
ServerPtr srv(newServer(defaultServerSettings)); ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType()));
QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection);
@ -259,7 +302,7 @@ void tst_Http2::flowControlClientSide()
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, params)); ServerPtr srv(newServer(serverSettings, defaultConnectionType(), params));
const QByteArray respond(int(Http2::defaultSessionWindowSize * 10), 'x'); const QByteArray respond(int(Http2::defaultSessionWindowSize * 10), 'x');
@ -301,7 +344,7 @@ void tst_Http2::flowControlServerSide()
const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 7}}; const Http2::RawSettings serverSettings = {{Settings::MAX_CONCURRENT_STREAMS_ID, 7}};
ServerPtr srv(newServer(serverSettings)); ServerPtr srv(newServer(serverSettings, defaultConnectionType()));
const QByteArray payload(int(Http2::defaultSessionWindowSize * 500), 'x'); const QByteArray payload(int(Http2::defaultSessionWindowSize * 500), 'x');
@ -336,7 +379,7 @@ void tst_Http2::pushPromise()
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, params)); ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType(), params));
srv->enablePushPromise(true, QByteArray("/script.js")); srv->enablePushPromise(true, QByteArray("/script.js"));
QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection);
@ -344,7 +387,7 @@ void tst_Http2::pushPromise()
QVERIFY(serverPort != 0); QVERIFY(serverPort != 0);
auto url = requestUrl(); auto url = requestUrl(defaultConnectionType());
url.setPath("/index.html"); url.setPath("/index.html");
QNetworkRequest request(url); QNetworkRequest request(url);
@ -410,14 +453,14 @@ void tst_Http2::goaway()
serverPort = 0; serverPort = 0;
nRequests = 3; nRequests = 3;
ServerPtr srv(newServer(defaultServerSettings)); ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType()));
srv->emulateGOAWAY(responseTimeoutMS); srv->emulateGOAWAY(responseTimeoutMS);
QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection); QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection);
runEventLoop(); runEventLoop();
QVERIFY(serverPort != 0); QVERIFY(serverPort != 0);
auto url = requestUrl(); auto url = requestUrl(defaultConnectionType());
// We have to store these replies, so that we can check errors later. // We have to store these replies, so that we can check errors later.
std::vector<QNetworkReply *> replies(nRequests); std::vector<QNetworkReply *> replies(nRequests);
for (int i = 0; i < nRequests; ++i) { for (int i = 0; i < nRequests; ++i) {
@ -457,7 +500,7 @@ void tst_Http2::earlyResponse()
serverPort = 0; serverPort = 0;
nRequests = 1; nRequests = 1;
ServerPtr targetServer(newServer(defaultServerSettings)); ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType()));
QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection); QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
runEventLoop(); runEventLoop();
@ -467,7 +510,7 @@ void tst_Http2::earlyResponse()
const quint16 targetPort = serverPort; const quint16 targetPort = serverPort;
serverPort = 0; serverPort = 0;
ServerPtr redirector(newServer(defaultServerSettings)); ServerPtr redirector(newServer(defaultServerSettings, defaultConnectionType()));
redirector->redirectOpenStream(targetPort); redirector->redirectOpenStream(targetPort);
QMetaObject::invokeMethod(redirector.data(), "startServer", Qt::QueuedConnection); QMetaObject::invokeMethod(redirector.data(), "startServer", Qt::QueuedConnection);
@ -507,11 +550,11 @@ void tst_Http2::stopEventLoop()
eventLoop.exitLoop(); eventLoop.exitLoop();
} }
Http2Server *tst_Http2::newServer(const Http2::RawSettings &serverSettings, Http2Server *tst_Http2::newServer(const Http2::RawSettings &serverSettings, H2Type connectionType,
const Http2::ProtocolParameters &clientSettings) const Http2::ProtocolParameters &clientSettings)
{ {
using namespace Http2; using namespace Http2;
auto srv = new Http2Server(clearTextHTTP2, serverSettings, auto srv = new Http2Server(connectionType, serverSettings,
clientSettings.settingsFrameData); clientSettings.settingsFrameData);
using Srv = Http2Server; using Srv = Http2Server;
@ -536,7 +579,7 @@ void tst_Http2::sendRequest(int streamNumber,
QNetworkRequest::Priority priority, QNetworkRequest::Priority priority,
const QByteArray &payload) const QByteArray &payload)
{ {
auto url = requestUrl(); auto url = requestUrl(defaultConnectionType());
url.setPath(QString("/stream%1.html").arg(streamNumber)); url.setPath(QString("/stream%1.html").arg(streamNumber));
QNetworkRequest request(url); QNetworkRequest request(url);
@ -555,10 +598,24 @@ void tst_Http2::sendRequest(int streamNumber,
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished); connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
} }
QUrl tst_Http2::requestUrl() const QUrl tst_Http2::requestUrl(H2Type connectionType) const
{ {
#if !QT_CONFIG(ssl)
Q_ASSERT(connectionType != H2Type::h2Alpn && connectionType != H2Type::h2Direct);
#endif
static auto url = QUrl(QLatin1String(clearTextHTTP2 ? "http://127.0.0.1" : "https://127.0.0.1")); static auto url = QUrl(QLatin1String(clearTextHTTP2 ? "http://127.0.0.1" : "https://127.0.0.1"));
url.setPort(serverPort); url.setPort(serverPort);
// Clear text may mean no-TLS-at-all or crappy-TLS-without-ALPN.
switch (connectionType) {
case H2Type::h2Alpn:
case H2Type::h2Direct:
url.setScheme(QStringLiteral("https"));
break;
case H2Type::h2c:
case H2Type::h2cDirect:
url.setScheme(QStringLiteral("http"));
break;
}
return url; return url;
} }